From 028e5a4700fda385f2d2253e09507bae9dfdd149 Mon Sep 17 00:00:00 2001
From: Thomasr
Date: Tue, 11 Feb 2025 01:44:06 -0500
Subject: [PATCH 001/124] Update oracle jdbc dependency
---
server/api-service/lowcoder-plugins/oraclePlugin/pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml b/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml
index 03ae32762..a109e8a7a 100644
--- a/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml
+++ b/server/api-service/lowcoder-plugins/oraclePlugin/pom.xml
@@ -31,8 +31,8 @@
com.oracle.database.jdbc
- ojdbc6
- 11.2.0.4
+ ojdbc11
+ 23.7.0.25.01
org.testcontainers
From 917880e45f26ddb7e7b622b574d20d2a190d7b1a Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Tue, 11 Feb 2025 01:49:32 -0500
Subject: [PATCH 002/124] Check for uniqueness of variable name within new
cloned query
---
client/packages/lowcoder/src/comps/queries/queryComp.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
index 066b351fe..31e101b85 100644
--- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
@@ -777,13 +777,15 @@ class QueryListComp extends QueryListTmpComp implements BottomResListComp {
const jsonData = originQuery.toJsonValue();
//Regenerate variable header
+ const newKeys:string[] = [];
jsonData.variables?.variables?.forEach(kv => {
const [prefix, _] = (kv.key as string).split(/(?=\d+$)/);
let i=1, newName = "";
do {
newName = prefix + (i++);
- } while(editorState.checkRename("", newName));
+ } while(editorState.checkRename("", newName) || newKeys.includes(newName));
kv.key = newName;
+ newKeys.push(newName);
})
const newQueryName = this.genNewName(editorState);
From 62be602bc3f7d4722a5f2ca7b43584e0dba8f28c Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 6 Feb 2025 10:54:33 -0500
Subject: [PATCH 003/124] waterfall, simplebar chart done
---
.../src/comps/barChartComp/barChartComp.tsx | 320 ++++++++++++++++
.../comps/barChartComp/barChartConstants.tsx | 322 +++++++++++++++++
.../barChartComp/barChartPropertyView.tsx | 147 ++++++++
.../src/comps/barChartComp/barChartUtils.ts | 342 ++++++++++++++++++
.../src/comps/barChartComp/seriesComp.tsx | 119 ++++++
.../chartConfigs/barChartConfig.tsx | 46 ++-
.../chartComp/chartConfigs/barChartConfig.tsx | 2 +-
.../src/i18n/comps/locales/en.ts | 80 ++++
.../src/i18n/comps/locales/enObj.tsx | 17 +
.../src/i18n/comps/locales/types.tsx | 1 +
client/packages/lowcoder-comps/src/index.ts | 2 +
11 files changed, 1392 insertions(+), 6 deletions(-)
create mode 100644 client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
create mode 100644 client/packages/lowcoder-comps/src/comps/barChartComp/seriesComp.tsx
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx
new file mode 100644
index 000000000..e13818586
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx
@@ -0,0 +1,320 @@
+import {
+ changeChildAction,
+ changeValueAction,
+ CompAction,
+ CompActionTypes,
+ wrapChildAction,
+} from "lowcoder-core";
+import { AxisFormatterComp, EchartsAxisType } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { barChartChildrenMap, ChartSize, getDataKeys } from "./barChartConstants";
+import { barChartPropertyView } from "./barChartPropertyView";
+import _ from "lodash";
+import { useContext, useEffect, useMemo, useRef, useState } from "react";
+import ReactResizeDetector from "react-resize-detector";
+import ReactECharts from "../basicChartComp/reactEcharts";
+import {
+ childrenToProps,
+ depsConfig,
+ genRandomKey,
+ NameConfig,
+ UICompBuilder,
+ withDefault,
+ withExposingConfigs,
+ withViewFn,
+ ThemeContext,
+ chartColorPalette,
+ getPromiseAfterDispatch,
+ dropdownControl,
+ JSONObject,
+} from "lowcoder-sdk";
+import { getEchartsLocale, trans } from "i18n/comps";
+import { ItemColorComp } from "comps/basicChartComp/chartConfigs/lineChartConfig";
+import {
+ echartsConfigOmitChildren,
+ getEchartsConfig,
+ getSelectedPoints,
+} from "./barChartUtils";
+import 'echarts-extension-gmap';
+import log from "loglevel";
+
+let clickEventCallback = () => {};
+
+const chartModeOptions = [
+ {
+ label: "ECharts JSON",
+ value: "json",
+ }
+] as const;
+
+let BarChartTmpComp = (function () {
+ return new UICompBuilder({mode:dropdownControl(chartModeOptions,'ui'),...barChartChildrenMap}, () => null)
+ .setPropertyViewFn(barChartPropertyView)
+ .build();
+})();
+
+BarChartTmpComp = withViewFn(BarChartTmpComp, (comp) => {
+ const mode = comp.children.mode.getView();
+ const onUIEvent = comp.children.onUIEvent.getView();
+ const onEvent = comp.children.onEvent.getView();
+ const echartsCompRef = useRef();
+ const [chartSize, setChartSize] = useState();
+ const firstResize = useRef(true);
+ const theme = useContext(ThemeContext);
+ const defaultChartTheme = {
+ color: chartColorPalette,
+ backgroundColor: "#fff",
+ };
+
+ let themeConfig = defaultChartTheme;
+ try {
+ themeConfig = theme?.theme.chart ? JSON.parse(theme?.theme.chart) : defaultChartTheme;
+ } catch (error) {
+ log.error('theme chart error: ', error);
+ }
+
+ const triggerClickEvent = async (dispatch: any, action: CompAction) => {
+ await getPromiseAfterDispatch(
+ dispatch,
+ action,
+ { autoHandleAfterReduce: true }
+ );
+ onEvent('click');
+ }
+
+ useEffect(() => {
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("click", (param: any) => {
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: 'click',
+ data: param.data,
+ }
+ }));
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", param.data, false)
+ );
+ });
+ return () => {
+ echartsCompInstance?.off("click");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, []);
+
+ useEffect(() => {
+ // bind events
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("selectchanged", (param: any) => {
+ const option: any = echartsCompInstance?.getOption();
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: param.fromAction,
+ data: getSelectedPoints(param, option)
+ }
+ }));
+
+ if (param.fromAction === "select") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("select");
+ } else if (param.fromAction === "unselect") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("unselect");
+ }
+
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", getSelectedPoints(param, option), false)
+ );
+ });
+ // unbind
+ return () => {
+ echartsCompInstance?.off("selectchanged");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, [onUIEvent]);
+
+ const echartsConfigChildren = _.omit(comp.children, echartsConfigOmitChildren);
+ const childrenProps = childrenToProps(echartsConfigChildren);
+ const option = useMemo(() => {
+ return getEchartsConfig(
+ childrenProps as ToViewReturn,
+ chartSize,
+ themeConfig
+ );
+ }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]);
+
+ useEffect(() => {
+ comp.children.mapInstance.dispatch(changeValueAction(null, false))
+ if(comp.children.mapInstance.value) return;
+ }, [option])
+
+ return (
+ {
+ if (w && h) {
+ setChartSize({ w: w, h: h });
+ }
+ if (!firstResize.current) {
+ // ignore the first resize, which will impact the loading animation
+ echartsCompRef.current?.getEchartsInstance().resize();
+ } else {
+ firstResize.current = false;
+ }
+ }}
+ >
+ (echartsCompRef.current = e)}
+ style={{ height: "100%" }}
+ notMerge
+ lazyUpdate
+ opts={{ locale: getEchartsLocale() }}
+ option={option}
+ mode={mode}
+ />
+
+ );
+});
+
+function getYAxisFormatContextValue(
+ data: Array,
+ yAxisType: EchartsAxisType,
+ yAxisName?: string
+) {
+ const dataSample = yAxisName && data.length > 0 && data[0][yAxisName];
+ let contextValue = dataSample;
+ if (yAxisType === "time") {
+ // to timestamp
+ const time =
+ typeof dataSample === "number" || typeof dataSample === "string"
+ ? new Date(dataSample).getTime()
+ : null;
+ if (time) contextValue = time;
+ }
+ return contextValue;
+}
+
+BarChartTmpComp = class extends BarChartTmpComp {
+ private lastYAxisFormatContextVal?: JSONValue;
+ private lastColorContext?: JSONObject;
+
+ updateContext(comp: this) {
+ // the context value of axis format
+ let resultComp = comp;
+ const data = comp.children.data.getView();
+ const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
+ const yAxisContextValue = getYAxisFormatContextValue(
+ data,
+ comp.children.yConfig.children.yAxisType.getView(),
+ sampleSeries?.children.columnName.getView()
+ );
+ if (yAxisContextValue !== comp.lastYAxisFormatContextVal) {
+ comp.lastYAxisFormatContextVal = yAxisContextValue;
+ resultComp = comp.setChild(
+ "yConfig",
+ comp.children.yConfig.reduce(
+ wrapChildAction(
+ "formatter",
+ AxisFormatterComp.changeContextDataAction({ value: yAxisContextValue })
+ )
+ )
+ );
+ }
+ // item color context
+ const colorContextVal = {
+ seriesName: sampleSeries?.children.seriesName.getView(),
+ value: yAxisContextValue,
+ };
+ if (
+ comp.children.chartConfig.children.comp.children.hasOwnProperty("itemColor") &&
+ !_.isEqual(colorContextVal, comp.lastColorContext)
+ ) {
+ comp.lastColorContext = colorContextVal;
+ resultComp = resultComp.setChild(
+ "chartConfig",
+ comp.children.chartConfig.reduce(
+ wrapChildAction(
+ "comp",
+ wrapChildAction("itemColor", ItemColorComp.changeContextDataAction(colorContextVal))
+ )
+ )
+ );
+ }
+ return resultComp;
+ }
+
+ override reduce(action: CompAction): this {
+ const comp = super.reduce(action);
+ if (action.type === CompActionTypes.UPDATE_NODES_V2) {
+ const newData = comp.children.data.getView();
+ // data changes
+ if (comp.children.data !== this.children.data) {
+ setTimeout(() => {
+ // update x-axis value
+ const keys = getDataKeys(newData);
+ if (keys.length > 0 && !keys.includes(comp.children.xAxisKey.getView())) {
+ comp.children.xAxisKey.dispatch(changeValueAction(keys[0] || ""));
+ }
+ // pass to child series comp
+ comp.children.series.dispatchDataChanged(newData);
+ }, 0);
+ }
+ return this.updateContext(comp);
+ }
+ return comp;
+ }
+
+ override autoHeight(): boolean {
+ return false;
+ }
+};
+
+let BarChartComp = withExposingConfigs(BarChartTmpComp, [
+ depsConfig({
+ name: "selectedPoints",
+ desc: trans("chart.selectedPointsDesc"),
+ depKeys: ["selectedPoints"],
+ func: (input) => {
+ return input.selectedPoints;
+ },
+ }),
+ depsConfig({
+ name: "lastInteractionData",
+ desc: trans("chart.lastInteractionDataDesc"),
+ depKeys: ["lastInteractionData"],
+ func: (input) => {
+ return input.lastInteractionData;
+ },
+ }),
+ depsConfig({
+ name: "data",
+ desc: trans("chart.dataDesc"),
+ depKeys: ["data", "mode"],
+ func: (input) =>[] ,
+ }),
+ new NameConfig("title", trans("chart.titleDesc")),
+]);
+
+
+export const BarChartCompWithDefault = withDefault(BarChartComp, {
+ xAxisKey: "date",
+ series: [
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.spending"),
+ columnName: "spending",
+ },
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.budget"),
+ columnName: "budget",
+ },
+ ],
+});
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
new file mode 100644
index 000000000..f8fb71203
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
@@ -0,0 +1,322 @@
+import {
+ jsonControl,
+ JSONObject,
+ stateComp,
+ toJSONObjectArray,
+ toObject,
+ BoolControl,
+ withDefault,
+ StringControl,
+ NumberControl,
+ FunctionControl,
+ dropdownControl,
+ eventHandlerControl,
+ valueComp,
+ withType,
+ uiChildren,
+ clickEvent,
+ styleControl,
+ EchartDefaultTextStyle,
+ EchartDefaultChartStyle,
+ toArray
+} from "lowcoder-sdk";
+import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core";
+import { BarChartConfig } from "../basicChartComp/chartConfigs/barChartConfig";
+import { XAxisConfig, YAxisConfig } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { LegendConfig } from "../basicChartComp/chartConfigs/legendConfig";
+import { EchartsLegendConfig } from "../basicChartComp/chartConfigs/echartsLegendConfig";
+import { EchartsLabelConfig } from "../basicChartComp/chartConfigs/echartsLabelConfig";
+import { LineChartConfig } from "../basicChartComp/chartConfigs/lineChartConfig";
+import { PieChartConfig } from "../basicChartComp/chartConfigs/pieChartConfig";
+import { ScatterChartConfig } from "../basicChartComp/chartConfigs/scatterChartConfig";
+import { SeriesListComp } from "./seriesComp";
+import { EChartsOption } from "echarts";
+import { i18nObjs, trans } from "i18n/comps";
+import { GaugeChartConfig } from "../basicChartComp/chartConfigs/gaugeChartConfig";
+import { FunnelChartConfig } from "../basicChartComp/chartConfigs/funnelChartConfig";
+import {EchartsTitleVerticalConfig} from "../chartComp/chartConfigs/echartsTitleVerticalConfig";
+import {EchartsTitleConfig} from "../chartComp/chartConfigs/echartsTitleConfig";
+
+export const ChartTypeOptions = [
+ {
+ label: trans("chart.bar"),
+ value: "bar",
+ },
+ {
+ label: trans("chart.line"),
+ value: "line",
+ },
+ {
+ label: trans("chart.scatter"),
+ value: "scatter",
+ },
+ {
+ label: trans("chart.pie"),
+ value: "pie",
+ },
+] as const;
+
+export const UIEventOptions = [
+ {
+ label: trans("chart.select"),
+ value: "select",
+ description: trans("chart.selectDesc"),
+ },
+ {
+ label: trans("chart.unSelect"),
+ value: "unselect",
+ description: trans("chart.unselectDesc"),
+ },
+] as const;
+
+export const MapEventOptions = [
+ {
+ label: trans("chart.mapReady"),
+ value: "mapReady",
+ description: trans("chart.mapReadyDesc"),
+ },
+ {
+ label: trans("chart.zoomLevelChange"),
+ value: "zoomLevelChange",
+ description: trans("chart.zoomLevelChangeDesc"),
+ },
+ {
+ label: trans("chart.centerPositionChange"),
+ value: "centerPositionChange",
+ description: trans("chart.centerPositionChangeDesc"),
+ },
+] as const;
+
+export const XAxisDirectionOptions = [
+ {
+ label: trans("chart.horizontal"),
+ value: "horizontal",
+ },
+ {
+ label: trans("chart.vertical"),
+ value: "vertical",
+ },
+] as const;
+
+export type XAxisDirectionType = ValueFromOption;
+
+export const noDataAxisConfig = {
+ animation: false,
+ xAxis: {
+ type: "category",
+ name: trans("chart.noData"),
+ nameLocation: "middle",
+ data: [],
+ axisLine: {
+ lineStyle: {
+ color: "#8B8FA3",
+ },
+ },
+ },
+ yAxis: {
+ type: "value",
+ axisLabel: {
+ color: "#8B8FA3",
+ },
+ splitLine: {
+ lineStyle: {
+ color: "#F0F0F0",
+ },
+ },
+ },
+ tooltip: {
+ show: false,
+ },
+ series: [
+ {
+ data: [700],
+ type: "line",
+ itemStyle: {
+ opacity: 0,
+ },
+ },
+ ],
+} as EChartsOption;
+
+export const noDataPieChartConfig = {
+ animation: false,
+ tooltip: {
+ show: false,
+ },
+ legend: {
+ formatter: trans("chart.unknown"),
+ top: "bottom",
+ selectedMode: false,
+ },
+ color: ["#B8BBCC", "#CED0D9", "#DCDEE6", "#E6E6EB"],
+ series: [
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["25%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["75%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ ],
+} as EChartsOption;
+
+export type ChartSize = { w: number; h: number };
+
+export const getDataKeys = (data: Array) => {
+ if (!data) {
+ return [];
+ }
+ const dataKeys: Array = [];
+ data.slice(0, 50).forEach((d) => {
+ Object.keys(d).forEach((key) => {
+ if (!dataKeys.includes(key)) {
+ dataKeys.push(key);
+ }
+ });
+ });
+ return dataKeys;
+};
+
+const ChartOptionMap = {
+ bar: BarChartConfig,
+ line: LineChartConfig,
+ pie: PieChartConfig,
+ scatter: ScatterChartConfig,
+};
+
+const EchartsOptionMap = {
+ funnel: FunnelChartConfig,
+ gauge: GaugeChartConfig,
+};
+
+const ChartOptionComp = withType(ChartOptionMap, "bar");
+const EchartsOptionComp = withType(EchartsOptionMap, "funnel");
+export type CharOptionCompType = keyof typeof ChartOptionMap;
+
+export const chartUiModeChildren = {
+ title: withDefault(StringControl, trans("echarts.defaultTitle")),
+ data: jsonControl(toJSONObjectArray, i18nObjs.defaultDataSource),
+ xAxisKey: valueComp(""), // x-axis, key from data
+ xAxisDirection: dropdownControl(XAxisDirectionOptions, "horizontal"),
+ series: SeriesListComp,
+ xConfig: XAxisConfig,
+ yConfig: YAxisConfig,
+ legendConfig: LegendConfig,
+ chartConfig: ChartOptionComp,
+ onUIEvent: eventHandlerControl(UIEventOptions),
+};
+
+let chartJsonModeChildren: any = {
+ echartsOption: jsonControl(toObject, i18nObjs.defaultEchartsJsonOption),
+ echartsTitle: withDefault(StringControl, trans("echarts.defaultTitle")),
+ echartsLegendConfig: EchartsLegendConfig,
+ echartsLabelConfig: EchartsLabelConfig,
+ echartsConfig: EchartsOptionComp,
+ echartsTitleVerticalConfig: EchartsTitleVerticalConfig,
+ echartsTitleConfig:EchartsTitleConfig,
+
+ left:withDefault(NumberControl,trans('chart.defaultLeft')),
+ right:withDefault(NumberControl,trans('chart.defaultRight')),
+ top:withDefault(NumberControl,trans('chart.defaultTop')),
+ bottom:withDefault(NumberControl,trans('chart.defaultBottom')),
+
+ tooltip: withDefault(BoolControl, true),
+ legendVisibility: withDefault(BoolControl, true),
+}
+if (EchartDefaultChartStyle && EchartDefaultTextStyle) {
+ chartJsonModeChildren = {
+ ...chartJsonModeChildren,
+ chartStyle: styleControl(EchartDefaultChartStyle, 'chartStyle'),
+ titleStyle: styleControl(EchartDefaultTextStyle, 'titleStyle'),
+ xAxisStyle: styleControl(EchartDefaultTextStyle, 'xAxis'),
+ yAxisStyle: styleControl(EchartDefaultTextStyle, 'yAxisStyle'),
+ legendStyle: styleControl(EchartDefaultTextStyle, 'legendStyle'),
+ }
+}
+
+const chartMapModeChildren = {
+ mapInstance: stateComp(),
+ getMapInstance: FunctionControl,
+ mapApiKey: withDefault(StringControl, ''),
+ mapZoomLevel: withDefault(NumberControl, 3),
+ mapCenterLng: withDefault(NumberControl, 15.932644),
+ mapCenterLat: withDefault(NumberControl, 50.942063),
+ mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption),
+ onMapEvent: eventHandlerControl(MapEventOptions),
+ showCharts: withDefault(BoolControl, true),
+}
+
+export type UIChartDataType = {
+ seriesName: string;
+ // coordinate chart
+ x?: any;
+ y?: any;
+ // pie or funnel
+ itemName?: any;
+ value?: any;
+};
+
+export type NonUIChartDataType = {
+ name: string;
+ value: any;
+}
+
+export const barChartChildrenMap = {
+ selectedPoints: stateComp>([]),
+ lastInteractionData: stateComp | NonUIChartDataType>({}),
+ onEvent: eventHandlerControl([clickEvent] as const),
+ ...chartUiModeChildren,
+ ...chartJsonModeChildren,
+ ...chartMapModeChildren,
+};
+
+const chartUiChildrenMap = uiChildren(barChartChildrenMap);
+export type ChartCompPropsType = RecordConstructorToView;
+export type ChartCompChildrenType = RecordConstructorToComp;
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
new file mode 100644
index 000000000..8a85ff36b
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
@@ -0,0 +1,147 @@
+import { changeChildAction, CompAction } from "lowcoder-core";
+import { ChartCompChildrenType, ChartTypeOptions,getDataKeys } from "./barChartConstants";
+import { newSeries } from "./seriesComp";
+import {
+ CustomModal,
+ Dropdown,
+ hiddenPropertyView,
+ Option,
+ RedButton,
+ Section,
+ sectionNames,
+ controlItem,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+export function barChartPropertyView(
+ children: ChartCompChildrenType,
+ dispatch: (action: CompAction) => void
+) {
+ const series = children.series.getView();
+ const columnOptions = getDataKeys(children.data.getView()).map((key) => ({
+ label: key,
+ value: key,
+ }));
+
+ const uiModePropertyView = (
+ <>
+
+ {children.chartConfig.getPropertyView()}
+ {
+ dispatch(changeChildAction("xAxisKey", value));
+ }}
+ />
+ s.getView().seriesName}
+ popoverTitle={(s) => s.getView().columnName}
+ content={(s, index) => (
+ <>
+ {s.getPropertyViewWithData(columnOptions)}
+ {
+ {
+ CustomModal.confirm({
+ title: trans("chart.delete"),
+ content: trans("chart.confirmDelete") + `${s.getView().seriesName}?`,
+ onConfirm: () =>
+ children.series.dispatch(children.series.deleteAction(index)),
+ confirmBtnType: "delete",
+ okText: trans("chart.delete"),
+ });
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ if (columnOptions.length <= 0) {
+ return;
+ }
+ children.series.dispatch(
+ children.series.pushAction(
+ newSeries(trans("chart.customSeries"), columnOptions[0].value)
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = children.series.arrayMoveAction(fromIndex, toIndex);
+ children.series.dispatch(action);
+ }}
+ hide={(s) => s.getView().hide}
+ onHide={(s, hide) => s.children.hide.dispatchChangeValueAction(hide)}
+ dataIndex={(s) => s.getView().dataIndex}
+ />
+
+
+
+ {children.onUIEvent.propertyView({title: trans("chart.chartEventHandlers")})}
+
+
+ {children.onEvent.propertyView()}
+
+
+
+ {children.echartsTitleConfig.getPropertyView()}
+ {children.echartsTitleVerticalConfig.getPropertyView()}
+ {children.legendConfig.getPropertyView()}
+ {children.title.propertyView({ label: trans("chart.title") })}
+ {children.left.propertyView({ label: trans("chart.left"), tooltip: trans("echarts.leftTooltip") })}
+ {children.right.propertyView({ label: trans("chart.right"), tooltip: trans("echarts.rightTooltip") })}
+ {children.top.propertyView({ label: trans("chart.top"), tooltip: trans("echarts.topTooltip") })}
+ {children.bottom.propertyView({ label: trans("chart.bottom"), tooltip: trans("echarts.bottomTooltip") })}
+ {children.chartConfig.children.compType.getView() !== "pie" && (
+ <>
+ {children.xAxisDirection.propertyView({
+ label: trans("chart.xAxisDirection"),
+ radioButton: true,
+ })}
+ {children.xConfig.getPropertyView()}
+ {children.yConfig.getPropertyView()}
+ >
+ )}
+ {hiddenPropertyView(children)}
+ {children.tooltip.propertyView({label: trans("echarts.tooltip"), tooltip: trans("echarts.tooltipTooltip")})}
+
+
+ {children.chartStyle?.getPropertyView()}
+
+
+ {children.titleStyle?.getPropertyView()}
+
+
+ {children.xAxisStyle?.getPropertyView()}
+
+
+ {children.yAxisStyle?.getPropertyView()}
+
+
+ {children.legendStyle?.getPropertyView()}
+
+
+ {children.data.propertyView({
+ label: trans("chart.data"),
+ })}
+
+ >
+ );
+
+ const getChatConfigByMode = (mode: string) => {
+ switch(mode) {
+ case "ui":
+ return uiModePropertyView;
+ }
+ }
+ return (
+ <>
+ {getChatConfigByMode(children.mode.getView())}
+ >
+ );
+}
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
new file mode 100644
index 000000000..7987ad51b
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -0,0 +1,342 @@
+import {
+ CharOptionCompType,
+ ChartCompPropsType,
+ ChartSize,
+ noDataAxisConfig,
+ noDataPieChartConfig,
+} from "comps/basicChartComp/chartConstants";
+import { getPieRadiusAndCenter } from "comps/basicChartComp/chartConfigs/pieChartConfig";
+import { EChartsOptionWithMap } from "../basicChartComp/reactEcharts/types";
+import _ from "lodash";
+import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk";
+import { calcXYConfig } from "comps/basicChartComp/chartConfigs/cartesianAxisConfig";
+import Big from "big.js";
+import { googleMapsApiUrl } from "../basicChartComp/chartConfigs/chartUrls";
+import opacityToHex from "../../util/opacityToHex";
+import parseBackground from "../../util/gradientBackgroundColor";
+import {ba} from "@fullcalendar/core/internal-common";
+import {chartStyleWrapper, styleWrapper} from "../../util/styleWrapper";
+
+export function transformData(
+ originData: JSONObject[],
+ xAxis: string,
+ seriesColumnNames: string[]
+) {
+ // aggregate data by x-axis
+ const transformedData: JSONObject[] = [];
+ originData.reduce((prev, cur) => {
+ if (cur === null || cur === undefined) {
+ return prev;
+ }
+ const groupValue = cur[xAxis] as string;
+ if (!prev[groupValue]) {
+ // init as 0
+ const initValue: any = {};
+ seriesColumnNames.forEach((name) => {
+ initValue[name] = 0;
+ });
+ prev[groupValue] = initValue;
+ transformedData.push(prev[groupValue]);
+ }
+ // remain the x-axis data
+ prev[groupValue][xAxis] = groupValue;
+ seriesColumnNames.forEach((key) => {
+ if (key === xAxis) {
+ return;
+ } else if (isNumeric(cur[key])) {
+ const bigNum = Big(cur[key]);
+ prev[groupValue][key] = bigNum.add(prev[groupValue][key]).toNumber();
+ } else {
+ prev[groupValue][key] += 1;
+ }
+ });
+ return prev;
+ }, {} as any);
+ return transformedData;
+}
+
+const notAxisChartSet: Set = new Set(["pie"] as const);
+export const echartsConfigOmitChildren = [
+ "hidden",
+ "selectedPoints",
+ "onUIEvent",
+ "mapInstance"
+] as const;
+type EchartsConfigProps = Omit;
+
+
+export function isAxisChart(type: CharOptionCompType) {
+ return !notAxisChartSet.has(type);
+}
+
+export function getSeriesConfig(props: EchartsConfigProps) {
+ let visibleSeries = props.series.filter((s) => !s.getView().hide);
+ if(props.chartConfig.subtype === "waterfall") {
+ const seriesOn = visibleSeries[0];
+ const seriesPlaceholder = visibleSeries[0];
+ visibleSeries = [seriesPlaceholder, seriesOn];
+ }
+ const seriesLength = visibleSeries.length;
+ return visibleSeries.map((s, index) => {
+ if (isAxisChart(props.chartConfig.type)) {
+ let encodeX: string, encodeY: string;
+ const horizontalX = props.xAxisDirection === "horizontal";
+ let itemStyle = props.chartConfig.itemStyle;
+ // FIXME: need refactor... chartConfig returns a function with paramters
+ if (props.chartConfig.type === "bar") {
+ // barChart's border radius, depend on x-axis direction and stack state
+ const borderRadius = horizontalX ? [2, 2, 0, 0] : [0, 2, 2, 0];
+ if (props.chartConfig.stack && index === visibleSeries.length - 1) {
+ itemStyle = { ...itemStyle, borderRadius: borderRadius };
+ } else if (!props.chartConfig.stack) {
+ itemStyle = { ...itemStyle, borderRadius: borderRadius };
+ }
+
+ if(props.chartConfig.subtype === "waterfall" && index === 0) {
+ itemStyle = {
+ borderColor: 'transparent',
+ color: 'transparent'
+ }
+ }
+ }
+ if (horizontalX) {
+ encodeX = props.xAxisKey;
+ encodeY = s.getView().columnName;
+ } else {
+ encodeX = s.getView().columnName;
+ encodeY = props.xAxisKey;
+ }
+ return {
+ name: props.chartConfig.subtype === "waterfall" && index === 0?s.getView().seriesName + "_placeholder":s.getView().seriesName,
+ selectedMode: "single",
+ select: {
+ itemStyle: {
+ borderColor: "#000",
+ },
+ },
+ encode: {
+ x: encodeX,
+ y: encodeY,
+ },
+ // each type of chart's config
+ ...props.chartConfig,
+ itemStyle: itemStyle,
+ label: {
+ ...props.chartConfig.label,
+ ...(!horizontalX && { position: "outside" }),
+ },
+ };
+ } else {
+ // pie
+ const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
+ return {
+ ...props.chartConfig,
+ radius: radiusAndCenter.radius,
+ center: radiusAndCenter.center,
+ name: s.getView().seriesName,
+ selectedMode: "single",
+ encode: {
+ itemName: props.xAxisKey,
+ value: s.getView().columnName,
+ },
+ };
+ }
+ });
+}
+
+// https://echarts.apache.org/en/option.html
+export function getEchartsConfig(
+ props: EchartsConfigProps,
+ chartSize?: ChartSize,
+ theme?: any,
+): EChartsOptionWithMap {
+ // axisChart
+ const axisChart = isAxisChart(props.chartConfig.type);
+ const gridPos = {
+ left: `${props?.left}%`,
+ right: `${props?.right}%`,
+ bottom: `${props?.bottom}%`,
+ top: `${props?.top}%`,
+ };
+ let config: any = {
+ title: {
+ text: props.title,
+ top: props.echartsTitleVerticalConfig.top,
+ left:props.echartsTitleConfig.top,
+ textStyle: {
+ ...styleWrapper(props?.titleStyle, theme?.titleStyle)
+ }
+ },
+ backgroundColor: parseBackground( props?.chartStyle?.background || theme?.chartStyle?.backgroundColor || "#FFFFFF"),
+ legend: {
+ ...props.legendConfig,
+ textStyle: {
+ ...styleWrapper(props?.legendStyle, theme?.legendStyle, 15)
+ }
+ },
+ tooltip: props.tooltip && {
+ trigger: "axis",
+ axisPointer: {
+ type: "line",
+ lineStyle: {
+ color: "rgba(0,0,0,0.2)",
+ width: 2,
+ type: "solid"
+ }
+ }
+ },
+ grid: {
+ ...gridPos,
+ containLabel: true,
+ },
+ };
+ if (props.data.length <= 0) {
+ // no data
+ return {
+ ...config,
+ ...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
+ };
+ }
+ const yAxisConfig = props.yConfig();
+ const seriesColumnNames = props.series
+ .filter((s) => !s.getView().hide)
+ .map((s) => s.getView().columnName);
+ // y-axis is category and time, data doesn't need to aggregate
+ let transformedData =
+ yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.echartsOption.length && props.echartsOption || props.data : transformData(props.echartsOption.length && props.echartsOption || props.data, props.xAxisKey, seriesColumnNames);
+
+ if(props.chartConfig.subtype === "waterfall") {
+ let sum = transformedData.reduce((acc, item) => {
+ if(typeof item[seriesColumnNames[0]] === 'number') return acc + item[seriesColumnNames[0]];
+ else return acc;
+ }, 0)
+ transformedData.map(d => {
+ d[`${seriesColumnNames[0]}_placeholder`] = sum - d[seriesColumnNames[0]];
+ sum = d[`${seriesColumnNames[0]}_placeholder`];
+ })
+ }
+
+ config = {
+ ...config,
+ dataset: [
+ {
+ source: transformedData,
+ sourceHeader: false,
+ },
+ ],
+ series: getSeriesConfig(props).map(series => ({
+ ...series,
+ encode: {
+ ...series.encode,
+ y: series.name,
+ },
+ itemStyle: {
+ ...series.itemStyle,
+ ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ },
+ lineStyle: {
+ ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ }
+ })),
+ };
+ console.log("Series", config.series);
+ if (axisChart) {
+ // pure chart's size except the margin around
+ let chartRealSize;
+ if (chartSize) {
+ const rightSize =
+ typeof gridPos.right === "number"
+ ? gridPos.right
+ : (chartSize.w * parseFloat(gridPos.right)) / 100.0;
+ chartRealSize = {
+ // actually it's self-adaptive with the x-axis label on the left, not that accurate but work
+ w: chartSize.w - gridPos.left - rightSize,
+ // also self-adaptive on the bottom
+ h: chartSize.h - gridPos.top - gridPos.bottom,
+ right: rightSize,
+ };
+ }
+ const finalXyConfig = calcXYConfig(
+ props.xConfig,
+ yAxisConfig,
+ props.xAxisDirection,
+ transformedData.map((d) => d[props.xAxisKey]),
+ chartRealSize
+ );
+ config = {
+ ...config,
+ // @ts-ignore
+ xAxis: {
+ ...finalXyConfig.xConfig,
+ axisLabel: {
+ ...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
+ }
+ },
+ // @ts-ignore
+ yAxis: {
+ ...finalXyConfig.yConfig,
+ axisLabel: {
+ ...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
+ }
+ },
+ };
+ }
+ // log.log("Echarts transformedData and config", transformedData, config);
+ return config;
+}
+
+export function getSelectedPoints(param: any, option: any) {
+ const series = option.series;
+ const dataSource = _.isArray(option.dataset) && option.dataset[0]?.source;
+ if (series && dataSource) {
+ return param.selected.flatMap((selectInfo: any) => {
+ const seriesInfo = series[selectInfo.seriesIndex];
+ if (!seriesInfo || !seriesInfo.encode) {
+ return [];
+ }
+ return selectInfo.dataIndex.map((index: any) => {
+ const commonResult = {
+ seriesName: seriesInfo.name,
+ };
+ if (seriesInfo.encode.itemName && seriesInfo.encode.value) {
+ return {
+ ...commonResult,
+ itemName: dataSource[index][seriesInfo.encode.itemName],
+ value: dataSource[index][seriesInfo.encode.value],
+ };
+ } else {
+ return {
+ ...commonResult,
+ x: dataSource[index][seriesInfo.encode.x],
+ y: dataSource[index][seriesInfo.encode.y],
+ };
+ }
+ });
+ });
+ }
+ return [];
+}
+
+export function loadGoogleMapsScript(apiKey: string) {
+ const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`;
+ const scripts = document.getElementsByTagName('script');
+ // is script already loaded
+ let scriptIndex = _.findIndex(scripts, (script) => script.src.endsWith(mapsUrl));
+ if(scriptIndex > -1) {
+ return scripts[scriptIndex];
+ }
+ // is script loaded with diff api_key, remove the script and load again
+ scriptIndex = _.findIndex(scripts, (script) => script.src.startsWith(googleMapsApiUrl));
+ if(scriptIndex > -1) {
+ scripts[scriptIndex].remove();
+ }
+
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = mapsUrl;
+ script.async = true;
+ script.defer = true;
+ window.document.body.appendChild(script);
+
+ return script;
+}
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/seriesComp.tsx
new file mode 100644
index 000000000..9ded885b5
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/seriesComp.tsx
@@ -0,0 +1,119 @@
+import {
+ BoolControl,
+ StringControl,
+ list,
+ JSONObject,
+ isNumeric,
+ genRandomKey,
+ Dropdown,
+ OptionsType,
+ MultiCompBuilder,
+ valueComp,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+import { ConstructorToComp, ConstructorToDataType, ConstructorToView } from "lowcoder-core";
+import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcoder-core";
+
+export type SeriesCompType = ConstructorToComp;
+export type RawSeriesCompType = ConstructorToView;
+type SeriesDataType = ConstructorToDataType;
+
+type ActionDataType = {
+ type: "chartDataChanged";
+ chartData: Array;
+};
+
+export function newSeries(name: string, columnName: string): SeriesDataType {
+ return {
+ seriesName: name,
+ columnName: columnName,
+ dataIndex: genRandomKey(),
+ };
+}
+
+const seriesChildrenMap = {
+ columnName: StringControl,
+ seriesName: StringControl,
+ hide: BoolControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+
+const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn(() => {
+ return <>>;
+ })
+ .build();
+
+class SeriesComp extends SeriesTmpComp {
+ getPropertyViewWithData(columnOptions: OptionsType): React.ReactNode {
+ return (
+ <>
+ {this.children.seriesName.propertyView({
+ label: trans("chart.seriesName"),
+ })}
+ {
+ this.children.columnName.dispatchChangeValueAction(value);
+ }}
+ />
+ >
+ );
+ }
+}
+
+const SeriesListTmpComp = list(SeriesComp);
+
+export class SeriesListComp extends SeriesListTmpComp {
+ override reduce(action: CompAction): this {
+ if (isMyCustomAction(action, "chartDataChanged")) {
+ // auto generate series
+ const actions = this.genExampleSeriesActions(action.value.chartData);
+ return this.reduce(this.multiAction(actions));
+ }
+ return super.reduce(action);
+ }
+
+ private genExampleSeriesActions(chartData: Array) {
+ const actions: CustomAction[] = [];
+ if (!chartData || chartData.length <= 0 || !chartData[0]) {
+ return actions;
+ }
+ let delCnt = 0;
+ const existColumns = this.getView().map((s) => s.getView().columnName);
+ // delete series not in data
+ existColumns.forEach((columnName) => {
+ if (chartData[0]?.[columnName] === undefined) {
+ actions.push(this.deleteAction(0));
+ delCnt++;
+ }
+ });
+ if (existColumns.length > delCnt) {
+ // don't generate example if exists
+ return actions;
+ }
+ // generate example series
+ const exampleKeys = Object.keys(chartData[0])
+ .filter((key) => {
+ return !existColumns.includes(key) && isNumeric(chartData[0][key]);
+ })
+ .slice(0, 3);
+ exampleKeys.forEach((key) => actions.push(this.pushAction(newSeries(key, key))));
+ return actions;
+ }
+
+ dispatchDataChanged(chartData: Array): void {
+ this.dispatch(
+ customAction({
+ type: "chartDataChanged",
+ chartData: chartData,
+ })
+ );
+ }
+}
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
index 6c91fe252..f4d177e42 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
@@ -1,11 +1,16 @@
import {
BoolControl,
+ NumberControl,
+ withDefault,
dropdownControl,
MultiCompBuilder,
showLabelPropertyView,
+ ColorControl,
+ Dropdown,
} from "lowcoder-sdk";
+import { changeChildAction, CompAction } from "lowcoder-core";
import { BarSeriesOption } from "echarts";
-import { trans } from "i18n/comps";
+import { i18nObjs, trans } from "i18n/comps";
const BarTypeOptions = [
{
@@ -16,6 +21,10 @@ const BarTypeOptions = [
label: trans("chart.stackedBar"),
value: "stackedBar",
},
+ {
+ label: trans("chart.waterfallBar"),
+ value: "waterfall",
+ },
] as const;
export const BarChartConfig = (function () {
@@ -23,27 +32,54 @@ export const BarChartConfig = (function () {
{
showLabel: BoolControl,
type: dropdownControl(BarTypeOptions, "basicBar"),
+ barWidth: withDefault(NumberControl, i18nObjs.defaultBarChartOption.barWidth),
+ showBackground: BoolControl,
+ backgroundColor: withDefault(ColorControl, i18nObjs.defaultBarChartOption.barBg),
},
(props): BarSeriesOption => {
const config: BarSeriesOption = {
type: "bar",
+ subtype: props.type,
label: {
show: props.showLabel,
position: "top",
},
+ barWidth: `${props.barWidth}%`,
+ showBackground: props.showBackground,
+ backgroundStyle: {
+ color: props.backgroundColor,
+ }
+
};
if (props.type === "stackedBar") {
config.stack = "stackValue";
}
+ if (props.type === "waterfall") {
+ config.label = undefined;
+ config.stack = "stackValue";
+ }
return config;
}
)
- .setPropertyViewFn((children) => (
+ .setPropertyViewFn((children, dispatch: (action: CompAction) => void) => (
<>
+ {
+ dispatch(changeChildAction("type", value));
+ }}
+ />
{showLabelPropertyView(children)}
- {children.type.propertyView({
- label: trans("chart.barType"),
- radioButton: true,
+ {children.barWidth.propertyView({
+ label: trans("barChart.barWidth"),
+ })}
+ {children.showBackground.propertyView({
+ label: trans("barChart.showBg"),
+ })}
+ {children.showBackground.getView() && children.backgroundColor.propertyView({
+ label: trans("barChart.bgColor"),
})}
>
))
diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/barChartConfig.tsx
index 6c91fe252..707b16170 100644
--- a/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/barChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartConfigs/barChartConfig.tsx
@@ -5,7 +5,7 @@ import {
showLabelPropertyView,
} from "lowcoder-sdk";
import { BarSeriesOption } from "echarts";
-import { trans } from "i18n/comps";
+import { i18nObjs, trans } from "i18n/comps";
const BarTypeOptions = [
{
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 c2861b8ca..fc45ca4f6 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -311,6 +311,85 @@ export const en = {
defaultBarometerPointerLength: "125",
defaultBarometerPointer_Y: "-10"
+ },
+ barChart: {
+ title: 'Title',
+ barWidth: 'Bar Width(%)',
+ showBg: 'Show Bar Background',
+ bgColor: 'Background Color',
+ defaultTitle: 'Bar Chart',
+ barType: 'Bar Chart Type',
+ tooltip: 'Tooltip',
+ left: 'Left',
+ defaultLeft:'35',
+ top: 'Top',
+ defaultTop:'60',
+ bottom: 'Bottom',
+ defaultBottom:'60',
+ width: 'Width',
+ defaultWidth:'80',
+ min: 'Min',
+ defaultMin:'0',
+ max: 'Max',
+ defaultMax:'100',
+ gap: 'Gap',
+ defaultGap: '2',
+ defaultStartAngle: '210',
+ startAngle: 'Start Angle',
+ defaultEndAngle: '-30',
+ endAngle: 'End Angle',
+ defaultSplitNumber: '10',
+ splitNumber: 'Split Number',
+ radius: 'Radius',
+ defaultRadius: '80',
+ defaultTemperatureRadius: '60',
+ defaultPointerLength: '50',
+ pointerLength: 'Pointer Length',
+ pointerWidth: 'Pointer Width',
+ defaultPointerWidth: '5',
+ label:'Label',
+ position_x: 'Position-X',
+ defaultPosition_X: '50',
+ position_y: 'Position-Y',
+ defaultPosition_Y: '60',
+ progressBarWidth: 'Progress Bar Width',
+ defaultProgressBarWidth: '10',
+ defaultStageProgressBarWidth: '15',
+ defaultTemperatureProgressBarWidth: '35',
+ defaultRingProgressBarWidth: '20',
+ progressBar: 'Progress Bar',
+ roundCap: "Round Cap",
+ chartType: "Chart Type",
+ chartTypeTooltip: "Select the types of Charts.",
+ defaultPointer_Y: "0",
+ gradeDefaultPointerIcon: "path://M12.8,0.7l12,40.1H0.7L12.8,0.7z",
+ clockDefaultPointerIcon: "path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z",
+ defaultBarometerPointerIcon: 'path://M2090.36389,615.30999 L2090.36389,615.30999 C2091.48372,615.30999 2092.40383,616.194028 2092.44859,617.312956 L2096.90698,728.755929 C2097.05155,732.369577 2094.2393,735.416212 2090.62566,735.56078 C2090.53845,735.564269 2090.45117,735.566014 2090.36389,735.566014 L2090.36389,735.566014 C2086.74736,735.566014 2083.81557,732.63423 2083.81557,729.017692 C2083.81557,728.930412 2083.81732,728.84314 2083.82081,728.755929 L2088.2792,617.312956 C2088.32396,616.194028 2089.24407,615.30999 2090.36389,615.30999 Z',
+ defaultMultiTitlePointerIcon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
+ pointer_Y: "Pointer-Y",
+ pointer_Y_Tooltip: "Select the Y-value of the pointer.",
+ pointerIcon: "Pointer Icon",
+ pointerIconTooltip: "Select the Pointer Icon",
+ defaultGradeBarPointerLength: "25",
+ defaultGradeBarPointerWidth: "10",
+ defaultGradeBarPointer_Y: "45",
+ defaultAxisLabelDistance: "10",
+ defaultTemperatureAxisLabelDistance: "30",
+ axisLabelDistance: "Axis Label Distance",
+ progressBarColor: "Progress Bar Color",
+ gradeProgressBarString: "Progress Bar String",
+ defaultAxisTickLength: "7",
+ defaultAxisTickWidth: "2",
+ defaultAxisTickColor: "#444444",
+ defaultStageAxisTickColor: "#ffffff",
+ axisTickLength: "axisTick Length",
+ axisTickWidth: "axisTick Width",
+ axisTickWidthStage: "axisTick Width",
+ axisTickColor: "AxisTick Color",
+ defaultBarometerPointerWidth: "3",
+ defaultBarometerPointerLength: "125",
+ defaultBarometerPointer_Y: "-10"
+
},
echarts: {
legendVisibility: 'Legend',
@@ -399,6 +478,7 @@ export const en = {
"Indicates the value of each coordinate. Example: '{{value * 100 + \"%\"}}'",
basicBar: "Basic Bar",
stackedBar: "Stacked Bar",
+ waterfallBar: "Waterfall Chart",
barType: "Bar Chart Type",
categoryAxis: "Category Axis",
valueAxis: "Value Axis",
diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx
index 408e50bf5..e50794ac5 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/enObj.tsx
@@ -152,6 +152,23 @@ export const enObj: I18nObjects = {
budget: 8000,
},
],
+ defaultBarChartOption: {
+ xAxis: {
+ type: 'category',
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+ },
+ yAxis: {
+ type: 'value'
+ },
+ series: [
+ {
+ data: [120, 200, 150, 80, 70, 110, 130],
+ type: 'bar'
+ }
+ ],
+ barWidth: 30,
+ barBg: "#aaaaaa"
+ },
defaultEchartsJsonOption: {
data: [
diff --git a/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx b/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx
index 674e7fc8a..ea9063e0a 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/types.tsx
@@ -2,6 +2,7 @@ import { JSONObject } from "lowcoder-sdk";
import { XAXisComponentOption } from "echarts";
export type I18nObjects = {
+ defaultBarChartOption: Record;
defaultDataSource: JSONObject[];
defaultEchartsJsonOption: Record;
defaultGaugeChartOption: Record;
diff --git a/client/packages/lowcoder-comps/src/index.ts b/client/packages/lowcoder-comps/src/index.ts
index acdc6c784..95afd0693 100644
--- a/client/packages/lowcoder-comps/src/index.ts
+++ b/client/packages/lowcoder-comps/src/index.ts
@@ -19,10 +19,12 @@ import { MeetingControllerComp } from "./comps/agoraMeetingComp/meetingControlle
import { VideoMeetingStreamComp } from "./comps/agoraMeetingComp/videoMeetingStreamComp";
import { VideoSharingStreamComp } from "./comps/agoraMeetingComp/videoSharingStreamComp";
import { BasicChartCompWithDefault } from "comps/basicChartComp/chartComp";
+import { BarChartCompWithDefault } from "comps/barChartComp/barChartComp";
export default {
chart: ChartCompWithDefault,
basicChart: BasicChartCompWithDefault,
+ barChart: BarChartCompWithDefault,
chartsGeoMap: ChartsGeoMapComp,
funnelChart: FunnelChartCompWithDefault,
gaugeChart: GaugeChartCompWithDefault,
From 52bfbbb833bed2b09d8b70f60ac2d14e6ddc2c7f Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 6 Feb 2025 11:34:32 -0500
Subject: [PATCH 004/124] waterfall done
---
.../src/comps/barChartComp/barChartConstants.tsx | 1 +
.../src/comps/barChartComp/barChartPropertyView.tsx | 3 +++
.../src/comps/barChartComp/barChartUtils.ts | 13 ++++++++++---
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
index f8fb71203..2b2cf05f4 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
@@ -245,6 +245,7 @@ export const chartUiModeChildren = {
data: jsonControl(toJSONObjectArray, i18nObjs.defaultDataSource),
xAxisKey: valueComp(""), // x-axis, key from data
xAxisDirection: dropdownControl(XAxisDirectionOptions, "horizontal"),
+ xAxisData: jsonControl(toArray, []),
series: SeriesListComp,
xConfig: XAxisConfig,
yConfig: YAxisConfig,
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
index 8a85ff36b..5f3d41879 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartPropertyView.tsx
@@ -35,6 +35,9 @@ export function barChartPropertyView(
dispatch(changeChildAction("xAxisKey", value));
}}
/>
+ {children.chartConfig.getView().subtype === "waterfall" && children.xAxisData.propertyView({
+ label: "X-Label-Data"
+ })}
{
d[`${seriesColumnNames[0]}_placeholder`] = sum - d[seriesColumnNames[0]];
sum = d[`${seriesColumnNames[0]}_placeholder`];
})
+ transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
}
+ console.log("TransformedData", transformedData);
+
config = {
...config,
dataset: [
@@ -236,10 +240,10 @@ export function getEchartsConfig(
},
lineStyle: {
...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
- }
+ },
+ data: transformedData.map((i: any) => i[series.name])
})),
};
- console.log("Series", config.series);
if (axisChart) {
// pure chart's size except the margin around
let chartRealSize;
@@ -270,7 +274,8 @@ export function getEchartsConfig(
...finalXyConfig.xConfig,
axisLabel: {
...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
- }
+ },
+ data: finalXyConfig.xConfig.type === "category"?props?.xAxisData:undefined,
},
// @ts-ignore
yAxis: {
@@ -280,6 +285,8 @@ export function getEchartsConfig(
}
},
};
+ console.log("Config", config);
+ console.log("Props", props);
}
// log.log("Echarts transformedData and config", transformedData, config);
return config;
From 82e3b476576653402b382e6fd811df46af255aaf Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 6 Feb 2025 12:06:53 -0500
Subject: [PATCH 005/124] polar chart
---
.../src/comps/barChartComp/barChartUtils.ts | 17 ++++++++++
.../chartConfigs/barChartConfig.tsx | 31 +++++++++++++++++++
.../src/i18n/comps/locales/en.ts | 5 +++
3 files changed, 53 insertions(+)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 0a5cf29da..4bd3f3f9e 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -218,6 +218,23 @@ export function getEchartsConfig(
transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
}
+ if(props.chartConfig.subtype === "polar") {
+ config = {
+ ...config,
+ polar: {
+ radius: [props.chartConfig.polarData.polarRadiusDeg, `${props.chartConfig.polarData.polarRadiusSize}%`],
+ },
+ radiusAxis: {
+ max: props.chartConfig.polarData.radiusAxisMax,
+ },
+ angleAxis: {
+ type: 'category',
+ data: props.chartConfig.polarData.labelData,
+ startAngle: 75
+ },
+ }
+ }
+
console.log("TransformedData", transformedData);
config = {
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
index f4d177e42..a9953401c 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
@@ -7,6 +7,8 @@ import {
showLabelPropertyView,
ColorControl,
Dropdown,
+ toArray,
+ jsonControl,
} from "lowcoder-sdk";
import { changeChildAction, CompAction } from "lowcoder-core";
import { BarSeriesOption } from "echarts";
@@ -25,6 +27,10 @@ const BarTypeOptions = [
label: trans("chart.waterfallBar"),
value: "waterfall",
},
+ {
+ label: trans("chart.polar"),
+ value: "polar",
+ },
] as const;
export const BarChartConfig = (function () {
@@ -35,6 +41,10 @@ export const BarChartConfig = (function () {
barWidth: withDefault(NumberControl, i18nObjs.defaultBarChartOption.barWidth),
showBackground: BoolControl,
backgroundColor: withDefault(ColorControl, i18nObjs.defaultBarChartOption.barBg),
+ radiusAxisMax: withDefault(NumberControl, 4),
+ polarRadiusDeg: withDefault(NumberControl, 30),
+ polarRadiusSize: withDefault(NumberControl, 80),
+ labelData: jsonControl(toArray, []),
},
(props): BarSeriesOption => {
const config: BarSeriesOption = {
@@ -48,6 +58,12 @@ export const BarChartConfig = (function () {
showBackground: props.showBackground,
backgroundStyle: {
color: props.backgroundColor,
+ },
+ polarData: {
+ radiusAxisMax: props.radiusAxisMax,
+ polarRadiusDeg: props.polarRadiusDeg,
+ polarRadiusSize: props.polarRadiusSize,
+ labelData: props.labelData,
}
};
@@ -58,6 +74,9 @@ export const BarChartConfig = (function () {
config.label = undefined;
config.stack = "stackValue";
}
+ if (props.type === "polar") {
+ config.coordinateSystem = 'polar';
+ }
return config;
}
)
@@ -81,6 +100,18 @@ export const BarChartConfig = (function () {
{children.showBackground.getView() && children.backgroundColor.propertyView({
label: trans("barChart.bgColor"),
})}
+ {children.type.getView() === "polar" && children.radiusAxisMax.propertyView({
+ label: trans("barChart.radiusAxisMax"),
+ })}
+ {children.type.getView() === "polar" && children.polarRadiusDeg.propertyView({
+ label: trans("barChart.polarRadiusDeg"),
+ })}
+ {children.type.getView() === "polar" && children.polarRadiusSize.propertyView({
+ label: trans("barChart.polarRadiusSize"),
+ })}
+ {children.type.getView() === "polar" && children.labelData.propertyView({
+ label: trans("barChart.polarLabelData"),
+ })}
>
))
.build();
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 fc45ca4f6..9bbc6aa98 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -317,6 +317,10 @@ export const en = {
barWidth: 'Bar Width(%)',
showBg: 'Show Bar Background',
bgColor: 'Background Color',
+ radiusAxisMax: 'Max',
+ polarRadiusDeg: 'Degree',
+ polarRadiusSize: 'Size (%)',
+ polarLabelData: 'Label Data',
defaultTitle: 'Bar Chart',
barType: 'Bar Chart Type',
tooltip: 'Tooltip',
@@ -479,6 +483,7 @@ export const en = {
basicBar: "Basic Bar",
stackedBar: "Stacked Bar",
waterfallBar: "Waterfall Chart",
+ polar: "Polar",
barType: "Bar Chart Type",
categoryAxis: "Category Axis",
valueAxis: "Value Axis",
From ed7d742f5a0ecf996ee4be6d77ad2dfa5b135da7 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Fri, 7 Feb 2025 05:51:51 -0500
Subject: [PATCH 006/124] add barchart comp package info
---
client/packages/lowcoder-comps/package.json | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json
index 7cf6fc1af..c7f06754f 100644
--- a/client/packages/lowcoder-comps/package.json
+++ b/client/packages/lowcoder-comps/package.json
@@ -58,6 +58,14 @@
"h": 40
}
},
+ "barChart": {
+ "name": "Bar Chart",
+ "icon": "./icons/icon-chart.svg",
+ "layoutInfo": {
+ "w": 12,
+ "h": 40
+ }
+ },
"imageEditor": {
"name": "Image Editor",
"icon": "./icons/icon-chart.svg",
From ec054bb526c22f87a12d54965e1d006a891e62e9 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 10 Feb 2025 04:17:14 -0500
Subject: [PATCH 007/124] tangent chart
---
.../src/comps/barChartComp/barChartUtils.ts | 33 +++++++++++++++----
.../chartConfigs/barChartConfig.tsx | 7 +++-
.../src/i18n/comps/locales/en.ts | 1 +
3 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 4bd3f3f9e..c337109ff 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -107,7 +107,7 @@ export function getSeriesConfig(props: EchartsConfigProps) {
encodeY = props.xAxisKey;
}
return {
- name: props.chartConfig.subtype === "waterfall" && index === 0?s.getView().seriesName + "_placeholder":s.getView().seriesName,
+ name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
selectedMode: "single",
select: {
itemStyle: {
@@ -212,8 +212,8 @@ export function getEchartsConfig(
}, 0)
const total = sum;
transformedData.map(d => {
- d[`${seriesColumnNames[0]}_placeholder`] = sum - d[seriesColumnNames[0]];
- sum = d[`${seriesColumnNames[0]}_placeholder`];
+ d[` `] = sum - d[seriesColumnNames[0]];
+ sum = d[` `];
})
transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
}
@@ -225,11 +225,14 @@ export function getEchartsConfig(
radius: [props.chartConfig.polarData.polarRadiusDeg, `${props.chartConfig.polarData.polarRadiusSize}%`],
},
radiusAxis: {
- max: props.chartConfig.polarData.radiusAxisMax,
+ type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
+ data: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.labelData:undefined,
+ max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
},
angleAxis: {
- type: 'category',
- data: props.chartConfig.polarData.labelData,
+ type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
+ data: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.labelData,
+ max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
startAngle: 75
},
}
@@ -302,6 +305,24 @@ export function getEchartsConfig(
}
},
};
+
+ //Waterfall x-label initialization
+ if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
+ //default labels
+ config.xAxis.data = ["Total"];
+ for(let i=1; i {
@@ -64,6 +65,7 @@ export const BarChartConfig = (function () {
polarRadiusDeg: props.polarRadiusDeg,
polarRadiusSize: props.polarRadiusSize,
labelData: props.labelData,
+ polarIsTangent: props.polarIsTangent,
}
};
@@ -100,6 +102,9 @@ export const BarChartConfig = (function () {
{children.showBackground.getView() && children.backgroundColor.propertyView({
label: trans("barChart.bgColor"),
})}
+ {children.type.getView() === "polar" && children.polarIsTangent.propertyView({
+ label: trans("barChart.polarIsTangent"),
+ })}
{children.type.getView() === "polar" && children.radiusAxisMax.propertyView({
label: trans("barChart.radiusAxisMax"),
})}
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 9bbc6aa98..5d0e7bc7d 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -317,6 +317,7 @@ export const en = {
barWidth: 'Bar Width(%)',
showBg: 'Show Bar Background',
bgColor: 'Background Color',
+ polarIsTangent: 'Tangential Chart',
radiusAxisMax: 'Max',
polarRadiusDeg: 'Degree',
polarRadiusSize: 'Size (%)',
From 5da87e0488e1eaeab50226d020f79b72563109af Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 10 Feb 2025 10:26:26 -0500
Subject: [PATCH 008/124] Polar end angle
---
.../comps/barChartComp/barChartConstants.tsx | 2 +-
.../src/comps/barChartComp/barChartUtils.ts | 51 ++++++++++---------
.../chartConfigs/barChartConfig.tsx | 27 +++++++---
.../src/i18n/comps/locales/en.ts | 6 ++-
4 files changed, 50 insertions(+), 36 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
index 2b2cf05f4..668b569be 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartConstants.tsx
@@ -35,7 +35,7 @@ import { i18nObjs, trans } from "i18n/comps";
import { GaugeChartConfig } from "../basicChartComp/chartConfigs/gaugeChartConfig";
import { FunnelChartConfig } from "../basicChartComp/chartConfigs/funnelChartConfig";
import {EchartsTitleVerticalConfig} from "../chartComp/chartConfigs/echartsTitleVerticalConfig";
-import {EchartsTitleConfig} from "../chartComp/chartConfigs/echartsTitleConfig";
+import {EchartsTitleConfig} from "../basicChartComp/chartConfigs/echartsTitleConfig";
export const ChartTypeOptions = [
{
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index c337109ff..689477fc7 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -4,7 +4,7 @@ import {
ChartSize,
noDataAxisConfig,
noDataPieChartConfig,
-} from "comps/basicChartComp/chartConstants";
+} from "comps/barChartComp/barChartConstants";
import { getPieRadiusAndCenter } from "comps/basicChartComp/chartConfigs/pieChartConfig";
import { EChartsOptionWithMap } from "../basicChartComp/reactEcharts/types";
import _ from "lodash";
@@ -56,6 +56,7 @@ export function transformData(
}
const notAxisChartSet: Set = new Set(["pie"] as const);
+const notAxisChartSubtypeSet: Set = new Set(["polar"] as const);
export const echartsConfigOmitChildren = [
"hidden",
"selectedPoints",
@@ -65,8 +66,8 @@ export const echartsConfigOmitChildren = [
type EchartsConfigProps = Omit;
-export function isAxisChart(type: CharOptionCompType) {
- return !notAxisChartSet.has(type);
+export function isAxisChart(type: CharOptionCompType, subtype: string) {
+ return !notAxisChartSet.has(type) && !notAxisChartSubtypeSet.has(subtype);
}
export function getSeriesConfig(props: EchartsConfigProps) {
@@ -78,7 +79,7 @@ export function getSeriesConfig(props: EchartsConfigProps) {
}
const seriesLength = visibleSeries.length;
return visibleSeries.map((s, index) => {
- if (isAxisChart(props.chartConfig.type)) {
+ if (isAxisChart(props.chartConfig.type, props.chartConfig.subtype)) {
let encodeX: string, encodeY: string;
const horizontalX = props.xAxisDirection === "horizontal";
let itemStyle = props.chartConfig.itemStyle;
@@ -151,7 +152,7 @@ export function getEchartsConfig(
theme?: any,
): EChartsOptionWithMap {
// axisChart
- const axisChart = isAxisChart(props.chartConfig.type);
+ const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.subtype);
const gridPos = {
left: `${props?.left}%`,
right: `${props?.right}%`,
@@ -222,7 +223,7 @@ export function getEchartsConfig(
config = {
...config,
polar: {
- radius: [props.chartConfig.polarData.polarRadiusDeg, `${props.chartConfig.polarData.polarRadiusSize}%`],
+ radius: [props.chartConfig.polarData.polarRadiusStart, props.chartConfig.polarData.polarRadiusEnd],
},
radiusAxis: {
type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
@@ -233,7 +234,8 @@ export function getEchartsConfig(
type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
data: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.labelData,
max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
- startAngle: 75
+ startAngle: props.chartConfig.polarData.polarStartAngle,
+ endAngle: props.chartConfig.polarData.polarEndAngle,
},
}
}
@@ -305,27 +307,26 @@ export function getEchartsConfig(
}
},
};
+ }
- //Waterfall x-label initialization
- if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
- //default labels
- config.xAxis.data = ["Total"];
- for(let i=1; i
Date: Mon, 10 Feb 2025 11:11:25 -0500
Subject: [PATCH 009/124] race chart
---
.../src/comps/barChartComp/barChartUtils.ts | 26 +++++++++++++++++++
.../chartConfigs/barChartConfig.tsx | 11 ++++++--
.../src/i18n/comps/locales/en.ts | 1 +
3 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 689477fc7..e9d76bfa1 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -191,6 +191,16 @@ export function getEchartsConfig(
containLabel: true,
},
};
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ // Disable init animation.
+ animationDuration: 0,
+ animationDurationUpdate: 2000,
+ animationEasing: 'linear',
+ animationEasingUpdate: 'linear',
+ }
+ }
if (props.data.length <= 0) {
// no data
return {
@@ -307,6 +317,22 @@ export function getEchartsConfig(
}
},
};
+
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ xAxis: {
+ ...config.xAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ yAxis: {
+ ...config.yAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ }
+ }
}
//Waterfall x-label initialization
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
index e73a3c3bd..d46f13e86 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
@@ -48,15 +48,19 @@ export const BarChartConfig = (function () {
polarStartAngle: withDefault(NumberControl, 90),
polarEndAngle: withDefault(NumberControl, -180),
polarIsTangent: withDefault(BoolControl, false),
+ race: withDefault(BoolControl, false),
labelData: jsonControl(toArray, []),
},
(props): BarSeriesOption => {
const config: BarSeriesOption = {
type: "bar",
subtype: props.type,
+ realtimeSort: props.race,
+ seriesLayoutBy: props.race?'column':undefined,
label: {
show: props.showLabel,
position: "top",
+ valueAnimation: props.race,
},
barWidth: `${props.barWidth}%`,
showBackground: props.showBackground,
@@ -71,8 +75,8 @@ export const BarChartConfig = (function () {
polarEndAngle: props.polarEndAngle,
labelData: props.labelData,
polarIsTangent: props.polarIsTangent,
- }
-
+ },
+ race: props.race,
};
if (props.type === "stackedBar") {
config.stack = "stackValue";
@@ -101,6 +105,9 @@ export const BarChartConfig = (function () {
{children.barWidth.propertyView({
label: trans("barChart.barWidth"),
})}
+ {children.race.propertyView({
+ label: trans("barChart.race"),
+ })}
{children.showBackground.propertyView({
label: trans("barChart.showBg"),
})}
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 aa78edfac..addf18014 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -315,6 +315,7 @@ export const en = {
barChart: {
title: 'Title',
barWidth: 'Bar Width(%)',
+ race: 'Race',
showBg: 'Show Bar Background',
bgColor: 'Background Color',
polarIsTangent: 'Tangential Chart',
From d92c816b39f18eaa59c2a1e7f639e6792a5a7b01 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 10 Feb 2025 11:43:06 -0500
Subject: [PATCH 010/124] Fixed stack bar error
---
.../src/comps/barChartComp/barChartUtils.ts | 3 ++-
.../basicChartComp/chartConfigs/barChartConfig.tsx | 10 +++++-----
.../lowcoder-comps/src/i18n/comps/locales/en.ts | 1 +
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index e9d76bfa1..af1352db2 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -71,6 +71,7 @@ export function isAxisChart(type: CharOptionCompType, subtype: string) {
}
export function getSeriesConfig(props: EchartsConfigProps) {
+ console.log("SeriesProps:", props);
let visibleSeries = props.series.filter((s) => !s.getView().hide);
if(props.chartConfig.subtype === "waterfall") {
const seriesOn = visibleSeries[0];
@@ -307,7 +308,7 @@ export function getEchartsConfig(
axisLabel: {
...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
},
- data: finalXyConfig.xConfig.type === "category"?props?.xAxisData:undefined,
+ data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:undefined,
},
// @ts-ignore
yAxis: {
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
index d46f13e86..2f482c50b 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/barChartConfig.tsx
@@ -20,10 +20,6 @@ const BarTypeOptions = [
label: trans("chart.basicBar"),
value: "basicBar",
},
- {
- label: trans("chart.stackedBar"),
- value: "stackedBar",
- },
{
label: trans("chart.waterfallBar"),
value: "waterfall",
@@ -48,6 +44,7 @@ export const BarChartConfig = (function () {
polarStartAngle: withDefault(NumberControl, 90),
polarEndAngle: withDefault(NumberControl, -180),
polarIsTangent: withDefault(BoolControl, false),
+ stack: withDefault(BoolControl, false),
race: withDefault(BoolControl, false),
labelData: jsonControl(toArray, []),
},
@@ -78,7 +75,7 @@ export const BarChartConfig = (function () {
},
race: props.race,
};
- if (props.type === "stackedBar") {
+ if (props.stack) {
config.stack = "stackValue";
}
if (props.type === "waterfall") {
@@ -108,6 +105,9 @@ export const BarChartConfig = (function () {
{children.race.propertyView({
label: trans("barChart.race"),
})}
+ {children.stack.propertyView({
+ label: trans("barChart.stack"),
+ })}
{children.showBackground.propertyView({
label: trans("barChart.showBg"),
})}
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 addf18014..9b80cdf23 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -316,6 +316,7 @@ export const en = {
title: 'Title',
barWidth: 'Bar Width(%)',
race: 'Race',
+ stack: 'Stacked',
showBg: 'Show Bar Background',
bgColor: 'Background Color',
polarIsTangent: 'Tangential Chart',
From 7f6ba4bb204b3e699b899d93da70cc80c4457c7f Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 10 Feb 2025 11:59:57 -0500
Subject: [PATCH 011/124] default axis label
---
.../src/comps/barChartComp/barChartUtils.ts | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index af1352db2..6b740e11f 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -339,19 +339,22 @@ export function getEchartsConfig(
//Waterfall x-label initialization
if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
//default labels
- config.xAxis.data = ["Total"];
- for(let i=1; i
Date: Mon, 10 Feb 2025 12:12:38 -0500
Subject: [PATCH 012/124] hide legend in waterfall chart
---
.../src/comps/barChartComp/barChartUtils.ts | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 6b740e11f..183fcc1e3 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -218,6 +218,7 @@ export function getEchartsConfig(
yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.echartsOption.length && props.echartsOption || props.data : transformData(props.echartsOption.length && props.echartsOption || props.data, props.xAxisKey, seriesColumnNames);
if(props.chartConfig.subtype === "waterfall") {
+ config.legend = undefined;
let sum = transformedData.reduce((acc, item) => {
if(typeof item[seriesColumnNames[0]] === 'number') return acc + item[seriesColumnNames[0]];
else return acc;
@@ -348,13 +349,13 @@ export function getEchartsConfig(
//Polar x-label initialization
if(props.chartConfig?.subtype === "polar" && props.chartConfig.polarData.labelData.length === 0) {
//default labels
- // let labelData = [];
- // for(let i=0; i
Date: Mon, 10 Feb 2025 14:08:06 -0500
Subject: [PATCH 013/124] linechart
---
client/packages/lowcoder-comps/package.json | 8 +
.../src/comps/lineChartComp/lineChartComp.tsx | 320 +++++++++++++
.../lineChartComp/lineChartConstants.tsx | 323 ++++++++++++++
.../lineChartComp/lineChartPropertyView.tsx | 150 +++++++
.../src/comps/lineChartComp/lineChartUtils.ts | 419 ++++++++++++++++++
.../src/comps/lineChartComp/seriesComp.tsx | 119 +++++
client/packages/lowcoder-comps/src/index.ts | 2 +
7 files changed, 1341 insertions(+)
create mode 100644 client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
create mode 100644 client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json
index c7f06754f..d96a31913 100644
--- a/client/packages/lowcoder-comps/package.json
+++ b/client/packages/lowcoder-comps/package.json
@@ -66,6 +66,14 @@
"h": 40
}
},
+ "lineChart": {
+ "name": "Line Chart",
+ "icon": "./icons/icon-chart.svg",
+ "layoutInfo": {
+ "w": 12,
+ "h": 40
+ }
+ },
"imageEditor": {
"name": "Image Editor",
"icon": "./icons/icon-chart.svg",
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
new file mode 100644
index 000000000..0c3ec2b5c
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
@@ -0,0 +1,320 @@
+import {
+ changeChildAction,
+ changeValueAction,
+ CompAction,
+ CompActionTypes,
+ wrapChildAction,
+} from "lowcoder-core";
+import { AxisFormatterComp, EchartsAxisType } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { lineChartChildrenMap, ChartSize, getDataKeys } from "./lineChartConstants";
+import { lineChartPropertyView } from "./lineChartPropertyView";
+import _ from "lodash";
+import { useContext, useEffect, useMemo, useRef, useState } from "react";
+import ReactResizeDetector from "react-resize-detector";
+import ReactECharts from "../basicChartComp/reactEcharts";
+import {
+ childrenToProps,
+ depsConfig,
+ genRandomKey,
+ NameConfig,
+ UICompBuilder,
+ withDefault,
+ withExposingConfigs,
+ withViewFn,
+ ThemeContext,
+ chartColorPalette,
+ getPromiseAfterDispatch,
+ dropdownControl,
+ JSONObject,
+} from "lowcoder-sdk";
+import { getEchartsLocale, trans } from "i18n/comps";
+import { ItemColorComp } from "comps/basicChartComp/chartConfigs/lineChartConfig";
+import {
+ echartsConfigOmitChildren,
+ getEchartsConfig,
+ getSelectedPoints,
+} from "./lineChartUtils";
+import 'echarts-extension-gmap';
+import log from "loglevel";
+
+let clickEventCallback = () => {};
+
+const chartModeOptions = [
+ {
+ label: "ECharts JSON",
+ value: "json",
+ }
+] as const;
+
+let LineChartTmpComp = (function () {
+ return new UICompBuilder({mode:dropdownControl(chartModeOptions,'ui'),...lineChartChildrenMap}, () => null)
+ .setPropertyViewFn(lineChartPropertyView)
+ .build();
+})();
+
+LineChartTmpComp = withViewFn(LineChartTmpComp, (comp) => {
+ const mode = comp.children.mode.getView();
+ const onUIEvent = comp.children.onUIEvent.getView();
+ const onEvent = comp.children.onEvent.getView();
+ const echartsCompRef = useRef();
+ const [chartSize, setChartSize] = useState();
+ const firstResize = useRef(true);
+ const theme = useContext(ThemeContext);
+ const defaultChartTheme = {
+ color: chartColorPalette,
+ backgroundColor: "#fff",
+ };
+
+ let themeConfig = defaultChartTheme;
+ try {
+ themeConfig = theme?.theme.chart ? JSON.parse(theme?.theme.chart) : defaultChartTheme;
+ } catch (error) {
+ log.error('theme chart error: ', error);
+ }
+
+ const triggerClickEvent = async (dispatch: any, action: CompAction) => {
+ await getPromiseAfterDispatch(
+ dispatch,
+ action,
+ { autoHandleAfterReduce: true }
+ );
+ onEvent('click');
+ }
+
+ useEffect(() => {
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("click", (param: any) => {
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: 'click',
+ data: param.data,
+ }
+ }));
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", param.data, false)
+ );
+ });
+ return () => {
+ echartsCompInstance?.off("click");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, []);
+
+ useEffect(() => {
+ // bind events
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("selectchanged", (param: any) => {
+ const option: any = echartsCompInstance?.getOption();
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: param.fromAction,
+ data: getSelectedPoints(param, option)
+ }
+ }));
+
+ if (param.fromAction === "select") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("select");
+ } else if (param.fromAction === "unselect") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("unselect");
+ }
+
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", getSelectedPoints(param, option), false)
+ );
+ });
+ // unbind
+ return () => {
+ echartsCompInstance?.off("selectchanged");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, [onUIEvent]);
+
+ const echartsConfigChildren = _.omit(comp.children, echartsConfigOmitChildren);
+ const childrenProps = childrenToProps(echartsConfigChildren);
+ const option = useMemo(() => {
+ return getEchartsConfig(
+ childrenProps as ToViewReturn,
+ chartSize,
+ themeConfig
+ );
+ }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]);
+
+ useEffect(() => {
+ comp.children.mapInstance.dispatch(changeValueAction(null, false))
+ if(comp.children.mapInstance.value) return;
+ }, [option])
+
+ return (
+ {
+ if (w && h) {
+ setChartSize({ w: w, h: h });
+ }
+ if (!firstResize.current) {
+ // ignore the first resize, which will impact the loading animation
+ echartsCompRef.current?.getEchartsInstance().resize();
+ } else {
+ firstResize.current = false;
+ }
+ }}
+ >
+ (echartsCompRef.current = e)}
+ style={{ height: "100%" }}
+ notMerge
+ lazyUpdate
+ opts={{ locale: getEchartsLocale() }}
+ option={option}
+ mode={mode}
+ />
+
+ );
+});
+
+function getYAxisFormatContextValue(
+ data: Array,
+ yAxisType: EchartsAxisType,
+ yAxisName?: string
+) {
+ const dataSample = yAxisName && data.length > 0 && data[0][yAxisName];
+ let contextValue = dataSample;
+ if (yAxisType === "time") {
+ // to timestamp
+ const time =
+ typeof dataSample === "number" || typeof dataSample === "string"
+ ? new Date(dataSample).getTime()
+ : null;
+ if (time) contextValue = time;
+ }
+ return contextValue;
+}
+
+LineChartTmpComp = class extends LineChartTmpComp {
+ private lastYAxisFormatContextVal?: JSONValue;
+ private lastColorContext?: JSONObject;
+
+ updateContext(comp: this) {
+ // the context value of axis format
+ let resultComp = comp;
+ const data = comp.children.data.getView();
+ const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
+ const yAxisContextValue = getYAxisFormatContextValue(
+ data,
+ comp.children.yConfig.children.yAxisType.getView(),
+ sampleSeries?.children.columnName.getView()
+ );
+ if (yAxisContextValue !== comp.lastYAxisFormatContextVal) {
+ comp.lastYAxisFormatContextVal = yAxisContextValue;
+ resultComp = comp.setChild(
+ "yConfig",
+ comp.children.yConfig.reduce(
+ wrapChildAction(
+ "formatter",
+ AxisFormatterComp.changeContextDataAction({ value: yAxisContextValue })
+ )
+ )
+ );
+ }
+ // item color context
+ const colorContextVal = {
+ seriesName: sampleSeries?.children.seriesName.getView(),
+ value: yAxisContextValue,
+ };
+ if (
+ comp.children.chartConfig.children.comp.children.hasOwnProperty("itemColor") &&
+ !_.isEqual(colorContextVal, comp.lastColorContext)
+ ) {
+ comp.lastColorContext = colorContextVal;
+ resultComp = resultComp.setChild(
+ "chartConfig",
+ comp.children.chartConfig.reduce(
+ wrapChildAction(
+ "comp",
+ wrapChildAction("itemColor", ItemColorComp.changeContextDataAction(colorContextVal))
+ )
+ )
+ );
+ }
+ return resultComp;
+ }
+
+ override reduce(action: CompAction): this {
+ const comp = super.reduce(action);
+ if (action.type === CompActionTypes.UPDATE_NODES_V2) {
+ const newData = comp.children.data.getView();
+ // data changes
+ if (comp.children.data !== this.children.data) {
+ setTimeout(() => {
+ // update x-axis value
+ const keys = getDataKeys(newData);
+ if (keys.length > 0 && !keys.includes(comp.children.xAxisKey.getView())) {
+ comp.children.xAxisKey.dispatch(changeValueAction(keys[0] || ""));
+ }
+ // pass to child series comp
+ comp.children.series.dispatchDataChanged(newData);
+ }, 0);
+ }
+ return this.updateContext(comp);
+ }
+ return comp;
+ }
+
+ override autoHeight(): boolean {
+ return false;
+ }
+};
+
+let LineChartComp = withExposingConfigs(LineChartTmpComp, [
+ depsConfig({
+ name: "selectedPoints",
+ desc: trans("chart.selectedPointsDesc"),
+ depKeys: ["selectedPoints"],
+ func: (input) => {
+ return input.selectedPoints;
+ },
+ }),
+ depsConfig({
+ name: "lastInteractionData",
+ desc: trans("chart.lastInteractionDataDesc"),
+ depKeys: ["lastInteractionData"],
+ func: (input) => {
+ return input.lastInteractionData;
+ },
+ }),
+ depsConfig({
+ name: "data",
+ desc: trans("chart.dataDesc"),
+ depKeys: ["data", "mode"],
+ func: (input) =>[] ,
+ }),
+ new NameConfig("title", trans("chart.titleDesc")),
+]);
+
+
+export const LineChartCompWithDefault = withDefault(LineChartComp, {
+ xAxisKey: "date",
+ series: [
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.spending"),
+ columnName: "spending",
+ },
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.budget"),
+ columnName: "budget",
+ },
+ ],
+});
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
new file mode 100644
index 000000000..2ab7bca75
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
@@ -0,0 +1,323 @@
+import {
+ jsonControl,
+ JSONObject,
+ stateComp,
+ toJSONObjectArray,
+ toObject,
+ BoolControl,
+ withDefault,
+ StringControl,
+ NumberControl,
+ FunctionControl,
+ dropdownControl,
+ eventHandlerControl,
+ valueComp,
+ withType,
+ uiChildren,
+ clickEvent,
+ styleControl,
+ EchartDefaultTextStyle,
+ EchartDefaultChartStyle,
+ toArray
+} from "lowcoder-sdk";
+import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core";
+import { BarChartConfig } from "../basicChartComp/chartConfigs/barChartConfig";
+import { XAxisConfig, YAxisConfig } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { LegendConfig } from "../basicChartComp/chartConfigs/legendConfig";
+import { EchartsLegendConfig } from "../basicChartComp/chartConfigs/echartsLegendConfig";
+import { EchartsLabelConfig } from "../basicChartComp/chartConfigs/echartsLabelConfig";
+import { LineChartConfig } from "../basicChartComp/chartConfigs/lineChartConfig";
+import { PieChartConfig } from "../basicChartComp/chartConfigs/pieChartConfig";
+import { ScatterChartConfig } from "../basicChartComp/chartConfigs/scatterChartConfig";
+import { SeriesListComp } from "./seriesComp";
+import { EChartsOption } from "echarts";
+import { i18nObjs, trans } from "i18n/comps";
+import { GaugeChartConfig } from "../basicChartComp/chartConfigs/gaugeChartConfig";
+import { FunnelChartConfig } from "../basicChartComp/chartConfigs/funnelChartConfig";
+import {EchartsTitleVerticalConfig} from "../chartComp/chartConfigs/echartsTitleVerticalConfig";
+import {EchartsTitleConfig} from "../basicChartComp/chartConfigs/echartsTitleConfig";
+
+export const ChartTypeOptions = [
+ {
+ label: trans("chart.bar"),
+ value: "bar",
+ },
+ {
+ label: trans("chart.line"),
+ value: "line",
+ },
+ {
+ label: trans("chart.scatter"),
+ value: "scatter",
+ },
+ {
+ label: trans("chart.pie"),
+ value: "pie",
+ },
+] as const;
+
+export const UIEventOptions = [
+ {
+ label: trans("chart.select"),
+ value: "select",
+ description: trans("chart.selectDesc"),
+ },
+ {
+ label: trans("chart.unSelect"),
+ value: "unselect",
+ description: trans("chart.unselectDesc"),
+ },
+] as const;
+
+export const MapEventOptions = [
+ {
+ label: trans("chart.mapReady"),
+ value: "mapReady",
+ description: trans("chart.mapReadyDesc"),
+ },
+ {
+ label: trans("chart.zoomLevelChange"),
+ value: "zoomLevelChange",
+ description: trans("chart.zoomLevelChangeDesc"),
+ },
+ {
+ label: trans("chart.centerPositionChange"),
+ value: "centerPositionChange",
+ description: trans("chart.centerPositionChangeDesc"),
+ },
+] as const;
+
+export const XAxisDirectionOptions = [
+ {
+ label: trans("chart.horizontal"),
+ value: "horizontal",
+ },
+ {
+ label: trans("chart.vertical"),
+ value: "vertical",
+ },
+] as const;
+
+export type XAxisDirectionType = ValueFromOption;
+
+export const noDataAxisConfig = {
+ animation: false,
+ xAxis: {
+ type: "category",
+ name: trans("chart.noData"),
+ nameLocation: "middle",
+ data: [],
+ axisLine: {
+ lineStyle: {
+ color: "#8B8FA3",
+ },
+ },
+ },
+ yAxis: {
+ type: "value",
+ axisLabel: {
+ color: "#8B8FA3",
+ },
+ splitLine: {
+ lineStyle: {
+ color: "#F0F0F0",
+ },
+ },
+ },
+ tooltip: {
+ show: false,
+ },
+ series: [
+ {
+ data: [700],
+ type: "line",
+ itemStyle: {
+ opacity: 0,
+ },
+ },
+ ],
+} as EChartsOption;
+
+export const noDataPieChartConfig = {
+ animation: false,
+ tooltip: {
+ show: false,
+ },
+ legend: {
+ formatter: trans("chart.unknown"),
+ top: "bottom",
+ selectedMode: false,
+ },
+ color: ["#B8BBCC", "#CED0D9", "#DCDEE6", "#E6E6EB"],
+ series: [
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["25%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["75%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ ],
+} as EChartsOption;
+
+export type ChartSize = { w: number; h: number };
+
+export const getDataKeys = (data: Array) => {
+ if (!data) {
+ return [];
+ }
+ const dataKeys: Array = [];
+ data.slice(0, 50).forEach((d) => {
+ Object.keys(d).forEach((key) => {
+ if (!dataKeys.includes(key)) {
+ dataKeys.push(key);
+ }
+ });
+ });
+ return dataKeys;
+};
+
+const ChartOptionMap = {
+ bar: BarChartConfig,
+ line: LineChartConfig,
+ pie: PieChartConfig,
+ scatter: ScatterChartConfig,
+};
+
+const EchartsOptionMap = {
+ funnel: FunnelChartConfig,
+ gauge: GaugeChartConfig,
+};
+
+const ChartOptionComp = withType(ChartOptionMap, "line");
+const EchartsOptionComp = withType(EchartsOptionMap, "funnel");
+export type CharOptionCompType = keyof typeof ChartOptionMap;
+
+export const chartUiModeChildren = {
+ title: withDefault(StringControl, trans("echarts.defaultTitle")),
+ data: jsonControl(toJSONObjectArray, i18nObjs.defaultDataSource),
+ xAxisKey: valueComp(""), // x-axis, key from data
+ xAxisDirection: dropdownControl(XAxisDirectionOptions, "horizontal"),
+ xAxisData: jsonControl(toArray, []),
+ series: SeriesListComp,
+ xConfig: XAxisConfig,
+ yConfig: YAxisConfig,
+ legendConfig: LegendConfig,
+ chartConfig: ChartOptionComp,
+ onUIEvent: eventHandlerControl(UIEventOptions),
+};
+
+let chartJsonModeChildren: any = {
+ echartsOption: jsonControl(toObject, i18nObjs.defaultEchartsJsonOption),
+ echartsTitle: withDefault(StringControl, trans("echarts.defaultTitle")),
+ echartsLegendConfig: EchartsLegendConfig,
+ echartsLabelConfig: EchartsLabelConfig,
+ echartsConfig: EchartsOptionComp,
+ echartsTitleVerticalConfig: EchartsTitleVerticalConfig,
+ echartsTitleConfig:EchartsTitleConfig,
+
+ left:withDefault(NumberControl,trans('chart.defaultLeft')),
+ right:withDefault(NumberControl,trans('chart.defaultRight')),
+ top:withDefault(NumberControl,trans('chart.defaultTop')),
+ bottom:withDefault(NumberControl,trans('chart.defaultBottom')),
+
+ tooltip: withDefault(BoolControl, true),
+ legendVisibility: withDefault(BoolControl, true),
+}
+if (EchartDefaultChartStyle && EchartDefaultTextStyle) {
+ chartJsonModeChildren = {
+ ...chartJsonModeChildren,
+ chartStyle: styleControl(EchartDefaultChartStyle, 'chartStyle'),
+ titleStyle: styleControl(EchartDefaultTextStyle, 'titleStyle'),
+ xAxisStyle: styleControl(EchartDefaultTextStyle, 'xAxis'),
+ yAxisStyle: styleControl(EchartDefaultTextStyle, 'yAxisStyle'),
+ legendStyle: styleControl(EchartDefaultTextStyle, 'legendStyle'),
+ }
+}
+
+const chartMapModeChildren = {
+ mapInstance: stateComp(),
+ getMapInstance: FunctionControl,
+ mapApiKey: withDefault(StringControl, ''),
+ mapZoomLevel: withDefault(NumberControl, 3),
+ mapCenterLng: withDefault(NumberControl, 15.932644),
+ mapCenterLat: withDefault(NumberControl, 50.942063),
+ mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption),
+ onMapEvent: eventHandlerControl(MapEventOptions),
+ showCharts: withDefault(BoolControl, true),
+}
+
+export type UIChartDataType = {
+ seriesName: string;
+ // coordinate chart
+ x?: any;
+ y?: any;
+ // pie or funnel
+ itemName?: any;
+ value?: any;
+};
+
+export type NonUIChartDataType = {
+ name: string;
+ value: any;
+}
+
+export const lineChartChildrenMap = {
+ selectedPoints: stateComp>([]),
+ lastInteractionData: stateComp | NonUIChartDataType>({}),
+ onEvent: eventHandlerControl([clickEvent] as const),
+ ...chartUiModeChildren,
+ ...chartJsonModeChildren,
+ ...chartMapModeChildren,
+};
+
+const chartUiChildrenMap = uiChildren(lineChartChildrenMap);
+export type ChartCompPropsType = RecordConstructorToView;
+export type ChartCompChildrenType = RecordConstructorToComp;
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
new file mode 100644
index 000000000..e002142a2
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
@@ -0,0 +1,150 @@
+import { changeChildAction, CompAction } from "lowcoder-core";
+import { ChartCompChildrenType, ChartTypeOptions,getDataKeys } from "./lineChartConstants";
+import { newSeries } from "./seriesComp";
+import {
+ CustomModal,
+ Dropdown,
+ hiddenPropertyView,
+ Option,
+ RedButton,
+ Section,
+ sectionNames,
+ controlItem,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+export function lineChartPropertyView(
+ children: ChartCompChildrenType,
+ dispatch: (action: CompAction) => void
+) {
+ const series = children.series.getView();
+ const columnOptions = getDataKeys(children.data.getView()).map((key) => ({
+ label: key,
+ value: key,
+ }));
+
+ const uiModePropertyView = (
+ <>
+
+ {children.chartConfig.getPropertyView()}
+ {
+ dispatch(changeChildAction("xAxisKey", value));
+ }}
+ />
+ {children.chartConfig.getView().subtype === "waterfall" && children.xAxisData.propertyView({
+ label: "X-Label-Data"
+ })}
+ s.getView().seriesName}
+ popoverTitle={(s) => s.getView().columnName}
+ content={(s, index) => (
+ <>
+ {s.getPropertyViewWithData(columnOptions)}
+ {
+ {
+ CustomModal.confirm({
+ title: trans("chart.delete"),
+ content: trans("chart.confirmDelete") + `${s.getView().seriesName}?`,
+ onConfirm: () =>
+ children.series.dispatch(children.series.deleteAction(index)),
+ confirmBtnType: "delete",
+ okText: trans("chart.delete"),
+ });
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ if (columnOptions.length <= 0) {
+ return;
+ }
+ children.series.dispatch(
+ children.series.pushAction(
+ newSeries(trans("chart.customSeries"), columnOptions[0].value)
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = children.series.arrayMoveAction(fromIndex, toIndex);
+ children.series.dispatch(action);
+ }}
+ hide={(s) => s.getView().hide}
+ onHide={(s, hide) => s.children.hide.dispatchChangeValueAction(hide)}
+ dataIndex={(s) => s.getView().dataIndex}
+ />
+
+
+
+ {children.onUIEvent.propertyView({title: trans("chart.chartEventHandlers")})}
+
+
+ {children.onEvent.propertyView()}
+
+
+
+ {children.echartsTitleConfig.getPropertyView()}
+ {children.echartsTitleVerticalConfig.getPropertyView()}
+ {children.legendConfig.getPropertyView()}
+ {children.title.propertyView({ label: trans("chart.title") })}
+ {children.left.propertyView({ label: trans("chart.left"), tooltip: trans("echarts.leftTooltip") })}
+ {children.right.propertyView({ label: trans("chart.right"), tooltip: trans("echarts.rightTooltip") })}
+ {children.top.propertyView({ label: trans("chart.top"), tooltip: trans("echarts.topTooltip") })}
+ {children.bottom.propertyView({ label: trans("chart.bottom"), tooltip: trans("echarts.bottomTooltip") })}
+ {children.chartConfig.children.compType.getView() !== "pie" && (
+ <>
+ {children.xAxisDirection.propertyView({
+ label: trans("chart.xAxisDirection"),
+ radioButton: true,
+ })}
+ {children.xConfig.getPropertyView()}
+ {children.yConfig.getPropertyView()}
+ >
+ )}
+ {hiddenPropertyView(children)}
+ {children.tooltip.propertyView({label: trans("echarts.tooltip"), tooltip: trans("echarts.tooltipTooltip")})}
+
+
+ {children.chartStyle?.getPropertyView()}
+
+
+ {children.titleStyle?.getPropertyView()}
+
+
+ {children.xAxisStyle?.getPropertyView()}
+
+
+ {children.yAxisStyle?.getPropertyView()}
+
+
+ {children.legendStyle?.getPropertyView()}
+
+
+ {children.data.propertyView({
+ label: trans("chart.data"),
+ })}
+
+ >
+ );
+
+ const getChatConfigByMode = (mode: string) => {
+ switch(mode) {
+ case "ui":
+ return uiModePropertyView;
+ }
+ }
+ return (
+ <>
+ {getChatConfigByMode(children.mode.getView())}
+ >
+ );
+}
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
new file mode 100644
index 000000000..4a6cb5cd1
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -0,0 +1,419 @@
+import {
+ CharOptionCompType,
+ ChartCompPropsType,
+ ChartSize,
+ noDataAxisConfig,
+ noDataPieChartConfig,
+} from "comps/lineChartComp/lineChartConstants";
+import { getPieRadiusAndCenter } from "comps/basicChartComp/chartConfigs/pieChartConfig";
+import { EChartsOptionWithMap } from "../basicChartComp/reactEcharts/types";
+import _ from "lodash";
+import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk";
+import { calcXYConfig } from "comps/basicChartComp/chartConfigs/cartesianAxisConfig";
+import Big from "big.js";
+import { googleMapsApiUrl } from "../basicChartComp/chartConfigs/chartUrls";
+import opacityToHex from "../../util/opacityToHex";
+import parseBackground from "../../util/gradientBackgroundColor";
+import {ba} from "@fullcalendar/core/internal-common";
+import {chartStyleWrapper, styleWrapper} from "../../util/styleWrapper";
+
+export function transformData(
+ originData: JSONObject[],
+ xAxis: string,
+ seriesColumnNames: string[]
+) {
+ // aggregate data by x-axis
+ const transformedData: JSONObject[] = [];
+ originData.reduce((prev, cur) => {
+ if (cur === null || cur === undefined) {
+ return prev;
+ }
+ const groupValue = cur[xAxis] as string;
+ if (!prev[groupValue]) {
+ // init as 0
+ const initValue: any = {};
+ seriesColumnNames.forEach((name) => {
+ initValue[name] = 0;
+ });
+ prev[groupValue] = initValue;
+ transformedData.push(prev[groupValue]);
+ }
+ // remain the x-axis data
+ prev[groupValue][xAxis] = groupValue;
+ seriesColumnNames.forEach((key) => {
+ if (key === xAxis) {
+ return;
+ } else if (isNumeric(cur[key])) {
+ const bigNum = Big(cur[key]);
+ prev[groupValue][key] = bigNum.add(prev[groupValue][key]).toNumber();
+ } else {
+ prev[groupValue][key] += 1;
+ }
+ });
+ return prev;
+ }, {} as any);
+ return transformedData;
+}
+
+const notAxisChartSet: Set = new Set(["pie"] as const);
+const notAxisChartSubtypeSet: Set = new Set(["polar"] as const);
+export const echartsConfigOmitChildren = [
+ "hidden",
+ "selectedPoints",
+ "onUIEvent",
+ "mapInstance"
+] as const;
+type EchartsConfigProps = Omit;
+
+
+export function isAxisChart(type: CharOptionCompType, subtype: string) {
+ return !notAxisChartSet.has(type) && !notAxisChartSubtypeSet.has(subtype);
+}
+
+export function getSeriesConfig(props: EchartsConfigProps) {
+ console.log("SeriesProps:", props);
+ let visibleSeries = props.series.filter((s) => !s.getView().hide);
+ if(props.chartConfig.subtype === "waterfall") {
+ const seriesOn = visibleSeries[0];
+ const seriesPlaceholder = visibleSeries[0];
+ visibleSeries = [seriesPlaceholder, seriesOn];
+ }
+ const seriesLength = visibleSeries.length;
+ return visibleSeries.map((s, index) => {
+ if (isAxisChart(props.chartConfig.type, props.chartConfig.subtype)) {
+ let encodeX: string, encodeY: string;
+ const horizontalX = props.xAxisDirection === "horizontal";
+ let itemStyle = props.chartConfig.itemStyle;
+ // FIXME: need refactor... chartConfig returns a function with paramters
+ if (props.chartConfig.type === "bar") {
+ // barChart's border radius, depend on x-axis direction and stack state
+ const borderRadius = horizontalX ? [2, 2, 0, 0] : [0, 2, 2, 0];
+ if (props.chartConfig.stack && index === visibleSeries.length - 1) {
+ itemStyle = { ...itemStyle, borderRadius: borderRadius };
+ } else if (!props.chartConfig.stack) {
+ itemStyle = { ...itemStyle, borderRadius: borderRadius };
+ }
+
+ if(props.chartConfig.subtype === "waterfall" && index === 0) {
+ itemStyle = {
+ borderColor: 'transparent',
+ color: 'transparent'
+ }
+ }
+ }
+ if (horizontalX) {
+ encodeX = props.xAxisKey;
+ encodeY = s.getView().columnName;
+ } else {
+ encodeX = s.getView().columnName;
+ encodeY = props.xAxisKey;
+ }
+ return {
+ name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
+ selectedMode: "single",
+ select: {
+ itemStyle: {
+ borderColor: "#000",
+ },
+ },
+ encode: {
+ x: encodeX,
+ y: encodeY,
+ },
+ // each type of chart's config
+ ...props.chartConfig,
+ itemStyle: itemStyle,
+ label: {
+ ...props.chartConfig.label,
+ ...(!horizontalX && { position: "outside" }),
+ },
+ };
+ } else {
+ // pie
+ const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
+ return {
+ ...props.chartConfig,
+ radius: radiusAndCenter.radius,
+ center: radiusAndCenter.center,
+ name: s.getView().seriesName,
+ selectedMode: "single",
+ encode: {
+ itemName: props.xAxisKey,
+ value: s.getView().columnName,
+ },
+ };
+ }
+ });
+}
+
+// https://echarts.apache.org/en/option.html
+export function getEchartsConfig(
+ props: EchartsConfigProps,
+ chartSize?: ChartSize,
+ theme?: any,
+): EChartsOptionWithMap {
+ // axisChart
+ const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.subtype);
+ const gridPos = {
+ left: `${props?.left}%`,
+ right: `${props?.right}%`,
+ bottom: `${props?.bottom}%`,
+ top: `${props?.top}%`,
+ };
+ let config: any = {
+ title: {
+ text: props.title,
+ top: props.echartsTitleVerticalConfig.top,
+ left:props.echartsTitleConfig.top,
+ textStyle: {
+ ...styleWrapper(props?.titleStyle, theme?.titleStyle)
+ }
+ },
+ backgroundColor: parseBackground( props?.chartStyle?.background || theme?.chartStyle?.backgroundColor || "#FFFFFF"),
+ legend: {
+ ...props.legendConfig,
+ textStyle: {
+ ...styleWrapper(props?.legendStyle, theme?.legendStyle, 15)
+ }
+ },
+ tooltip: props.tooltip && {
+ trigger: "axis",
+ axisPointer: {
+ type: "line",
+ lineStyle: {
+ color: "rgba(0,0,0,0.2)",
+ width: 2,
+ type: "solid"
+ }
+ }
+ },
+ grid: {
+ ...gridPos,
+ containLabel: true,
+ },
+ };
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ // Disable init animation.
+ animationDuration: 0,
+ animationDurationUpdate: 2000,
+ animationEasing: 'linear',
+ animationEasingUpdate: 'linear',
+ }
+ }
+ if (props.data.length <= 0) {
+ // no data
+ return {
+ ...config,
+ ...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
+ };
+ }
+ const yAxisConfig = props.yConfig();
+ const seriesColumnNames = props.series
+ .filter((s) => !s.getView().hide)
+ .map((s) => s.getView().columnName);
+ // y-axis is category and time, data doesn't need to aggregate
+ let transformedData =
+ yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.echartsOption.length && props.echartsOption || props.data : transformData(props.echartsOption.length && props.echartsOption || props.data, props.xAxisKey, seriesColumnNames);
+
+ if(props.chartConfig.subtype === "waterfall") {
+ config.legend = undefined;
+ let sum = transformedData.reduce((acc, item) => {
+ if(typeof item[seriesColumnNames[0]] === 'number') return acc + item[seriesColumnNames[0]];
+ else return acc;
+ }, 0)
+ const total = sum;
+ transformedData.map(d => {
+ d[` `] = sum - d[seriesColumnNames[0]];
+ sum = d[` `];
+ })
+ transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
+ }
+
+ if(props.chartConfig.subtype === "polar") {
+ config = {
+ ...config,
+ polar: {
+ radius: [props.chartConfig.polarData.polarRadiusStart, props.chartConfig.polarData.polarRadiusEnd],
+ },
+ radiusAxis: {
+ type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
+ data: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.labelData:undefined,
+ max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
+ },
+ angleAxis: {
+ type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
+ data: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.labelData,
+ max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
+ startAngle: props.chartConfig.polarData.polarStartAngle,
+ endAngle: props.chartConfig.polarData.polarEndAngle,
+ },
+ }
+ }
+
+ console.log("TransformedData", transformedData);
+
+ config = {
+ ...config,
+ dataset: [
+ {
+ source: transformedData,
+ sourceHeader: false,
+ },
+ ],
+ series: getSeriesConfig(props).map(series => ({
+ ...series,
+ encode: {
+ ...series.encode,
+ y: series.name,
+ },
+ itemStyle: {
+ ...series.itemStyle,
+ ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ },
+ lineStyle: {
+ ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ },
+ data: transformedData.map((i: any) => i[series.name])
+ })),
+ };
+ if (axisChart) {
+ // pure chart's size except the margin around
+ let chartRealSize;
+ if (chartSize) {
+ const rightSize =
+ typeof gridPos.right === "number"
+ ? gridPos.right
+ : (chartSize.w * parseFloat(gridPos.right)) / 100.0;
+ chartRealSize = {
+ // actually it's self-adaptive with the x-axis label on the left, not that accurate but work
+ w: chartSize.w - gridPos.left - rightSize,
+ // also self-adaptive on the bottom
+ h: chartSize.h - gridPos.top - gridPos.bottom,
+ right: rightSize,
+ };
+ }
+ const finalXyConfig = calcXYConfig(
+ props.xConfig,
+ yAxisConfig,
+ props.xAxisDirection,
+ transformedData.map((d) => d[props.xAxisKey]),
+ chartRealSize
+ );
+ config = {
+ ...config,
+ // @ts-ignore
+ xAxis: {
+ ...finalXyConfig.xConfig,
+ axisLabel: {
+ ...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
+ },
+ data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:undefined,
+ },
+ // @ts-ignore
+ yAxis: {
+ ...finalXyConfig.yConfig,
+ axisLabel: {
+ ...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
+ }
+ },
+ };
+
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ xAxis: {
+ ...config.xAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ yAxis: {
+ ...config.yAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ }
+ }
+ }
+
+ //Waterfall x-label initialization
+ if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
+ //default labels
+ config.xAxis.data = undefined;
+ // config.xAxis.data = ["Total"];
+ // for(let i=1; i {
+ const seriesInfo = series[selectInfo.seriesIndex];
+ if (!seriesInfo || !seriesInfo.encode) {
+ return [];
+ }
+ return selectInfo.dataIndex.map((index: any) => {
+ const commonResult = {
+ seriesName: seriesInfo.name,
+ };
+ if (seriesInfo.encode.itemName && seriesInfo.encode.value) {
+ return {
+ ...commonResult,
+ itemName: dataSource[index][seriesInfo.encode.itemName],
+ value: dataSource[index][seriesInfo.encode.value],
+ };
+ } else {
+ return {
+ ...commonResult,
+ x: dataSource[index][seriesInfo.encode.x],
+ y: dataSource[index][seriesInfo.encode.y],
+ };
+ }
+ });
+ });
+ }
+ return [];
+}
+
+export function loadGoogleMapsScript(apiKey: string) {
+ const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`;
+ const scripts = document.getElementsByTagName('script');
+ // is script already loaded
+ let scriptIndex = _.findIndex(scripts, (script) => script.src.endsWith(mapsUrl));
+ if(scriptIndex > -1) {
+ return scripts[scriptIndex];
+ }
+ // is script loaded with diff api_key, remove the script and load again
+ scriptIndex = _.findIndex(scripts, (script) => script.src.startsWith(googleMapsApiUrl));
+ if(scriptIndex > -1) {
+ scripts[scriptIndex].remove();
+ }
+
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = mapsUrl;
+ script.async = true;
+ script.defer = true;
+ window.document.body.appendChild(script);
+
+ return script;
+}
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
new file mode 100644
index 000000000..9ded885b5
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
@@ -0,0 +1,119 @@
+import {
+ BoolControl,
+ StringControl,
+ list,
+ JSONObject,
+ isNumeric,
+ genRandomKey,
+ Dropdown,
+ OptionsType,
+ MultiCompBuilder,
+ valueComp,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+import { ConstructorToComp, ConstructorToDataType, ConstructorToView } from "lowcoder-core";
+import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcoder-core";
+
+export type SeriesCompType = ConstructorToComp;
+export type RawSeriesCompType = ConstructorToView;
+type SeriesDataType = ConstructorToDataType;
+
+type ActionDataType = {
+ type: "chartDataChanged";
+ chartData: Array;
+};
+
+export function newSeries(name: string, columnName: string): SeriesDataType {
+ return {
+ seriesName: name,
+ columnName: columnName,
+ dataIndex: genRandomKey(),
+ };
+}
+
+const seriesChildrenMap = {
+ columnName: StringControl,
+ seriesName: StringControl,
+ hide: BoolControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+
+const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn(() => {
+ return <>>;
+ })
+ .build();
+
+class SeriesComp extends SeriesTmpComp {
+ getPropertyViewWithData(columnOptions: OptionsType): React.ReactNode {
+ return (
+ <>
+ {this.children.seriesName.propertyView({
+ label: trans("chart.seriesName"),
+ })}
+ {
+ this.children.columnName.dispatchChangeValueAction(value);
+ }}
+ />
+ >
+ );
+ }
+}
+
+const SeriesListTmpComp = list(SeriesComp);
+
+export class SeriesListComp extends SeriesListTmpComp {
+ override reduce(action: CompAction): this {
+ if (isMyCustomAction(action, "chartDataChanged")) {
+ // auto generate series
+ const actions = this.genExampleSeriesActions(action.value.chartData);
+ return this.reduce(this.multiAction(actions));
+ }
+ return super.reduce(action);
+ }
+
+ private genExampleSeriesActions(chartData: Array) {
+ const actions: CustomAction[] = [];
+ if (!chartData || chartData.length <= 0 || !chartData[0]) {
+ return actions;
+ }
+ let delCnt = 0;
+ const existColumns = this.getView().map((s) => s.getView().columnName);
+ // delete series not in data
+ existColumns.forEach((columnName) => {
+ if (chartData[0]?.[columnName] === undefined) {
+ actions.push(this.deleteAction(0));
+ delCnt++;
+ }
+ });
+ if (existColumns.length > delCnt) {
+ // don't generate example if exists
+ return actions;
+ }
+ // generate example series
+ const exampleKeys = Object.keys(chartData[0])
+ .filter((key) => {
+ return !existColumns.includes(key) && isNumeric(chartData[0][key]);
+ })
+ .slice(0, 3);
+ exampleKeys.forEach((key) => actions.push(this.pushAction(newSeries(key, key))));
+ return actions;
+ }
+
+ dispatchDataChanged(chartData: Array): void {
+ this.dispatch(
+ customAction({
+ type: "chartDataChanged",
+ chartData: chartData,
+ })
+ );
+ }
+}
diff --git a/client/packages/lowcoder-comps/src/index.ts b/client/packages/lowcoder-comps/src/index.ts
index 95afd0693..2b8dc8615 100644
--- a/client/packages/lowcoder-comps/src/index.ts
+++ b/client/packages/lowcoder-comps/src/index.ts
@@ -20,11 +20,13 @@ import { VideoMeetingStreamComp } from "./comps/agoraMeetingComp/videoMeetingStr
import { VideoSharingStreamComp } from "./comps/agoraMeetingComp/videoSharingStreamComp";
import { BasicChartCompWithDefault } from "comps/basicChartComp/chartComp";
import { BarChartCompWithDefault } from "comps/barChartComp/barChartComp";
+import { LineChartCompWithDefault } from "comps/lineChartComp/lineChartComp";
export default {
chart: ChartCompWithDefault,
basicChart: BasicChartCompWithDefault,
barChart: BarChartCompWithDefault,
+ lineChart: LineChartCompWithDefault,
chartsGeoMap: ChartsGeoMapComp,
funnelChart: FunnelChartCompWithDefault,
gaugeChart: GaugeChartCompWithDefault,
From fad530647d03d71e9c2785259722f7e6da7c6256 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Wed, 12 Feb 2025 03:50:06 -0500
Subject: [PATCH 014/124] Mark Line
---
.../src/comps/barChartComp/barChartUtils.ts | 4 -
.../chartConfigs/lineChartConfig.tsx | 30 +++----
.../src/comps/lineChartComp/lineChartComp.tsx | 6 --
.../lineChartComp/lineChartConstants.tsx | 36 +--------
.../src/comps/lineChartComp/lineChartUtils.ts | 8 +-
.../src/comps/lineChartComp/seriesComp.tsx | 80 ++++++++++++++++++-
.../src/i18n/comps/locales/en.ts | 11 +++
7 files changed, 107 insertions(+), 68 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 183fcc1e3..22de1df37 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -71,7 +71,6 @@ export function isAxisChart(type: CharOptionCompType, subtype: string) {
}
export function getSeriesConfig(props: EchartsConfigProps) {
- console.log("SeriesProps:", props);
let visibleSeries = props.series.filter((s) => !s.getView().hide);
if(props.chartConfig.subtype === "waterfall") {
const seriesOn = visibleSeries[0];
@@ -252,8 +251,6 @@ export function getEchartsConfig(
}
}
- console.log("TransformedData", transformedData);
-
config = {
...config,
dataset: [
@@ -357,7 +354,6 @@ export function getEchartsConfig(
if(props.chartConfig.polarData.polarIsTangent && config.radiusAxis.data.length === 0) config.radiusAxis.data = labelData;
if(!props.chartConfig.polarData.polarIsTangent && config.angleAxis.data.length === 0) config.angleAxis.data = labelData;
}
- console.log("Config", config);
// log.log("Echarts transformedData and config", transformedData, config);
return config;
}
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
index 266e5fbf7..9cb95be4b 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
@@ -10,21 +10,6 @@ import {
} from "lowcoder-sdk";
import { trans } from "i18n/comps";
-const BarTypeOptions = [
- {
- label: trans("chart.basicLine"),
- value: "basicLine",
- },
- {
- label: trans("chart.stackedLine"),
- value: "stackedLine",
- },
- {
- label: trans("chart.areaLine"),
- value: "areaLine",
- },
-] as const;
-
export const ItemColorComp = withContext(
new MultiCompBuilder({ value: ColorOrBoolCodeControl }, (props) => props.value)
.setPropertyViewFn((children) =>
@@ -42,7 +27,8 @@ export const LineChartConfig = (function () {
return new MultiCompBuilder(
{
showLabel: BoolControl,
- type: dropdownControl(BarTypeOptions, "basicLine"),
+ stacked: BoolControl,
+ area: BoolControl,
smooth: BoolControl,
itemColor: ItemColorComp,
},
@@ -71,9 +57,10 @@ export const LineChartConfig = (function () {
},
},
};
- if (props.type === "stackedLine") {
+ if (props.stacked) {
config.stack = "stackValue";
- } else if (props.type === "areaLine") {
+ }
+ if (props.area) {
config.areaStyle = {};
}
if (props.smooth) {
@@ -84,8 +71,11 @@ export const LineChartConfig = (function () {
)
.setPropertyViewFn((children) => (
<>
- {children.type.propertyView({
- label: trans("chart.lineType"),
+ {children.stacked.propertyView({
+ label: trans("lineChart.stacked"),
+ })}
+ {children.area.propertyView({
+ label: trans("lineChart.area"),
})}
{showLabelPropertyView(children)}
{children.smooth.propertyView({ label: trans("chart.smooth") })}
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
index 0c3ec2b5c..be3e5bf65 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx
@@ -25,7 +25,6 @@ import {
chartColorPalette,
getPromiseAfterDispatch,
dropdownControl,
- JSONObject,
} from "lowcoder-sdk";
import { getEchartsLocale, trans } from "i18n/comps";
import { ItemColorComp } from "comps/basicChartComp/chartConfigs/lineChartConfig";
@@ -151,11 +150,6 @@ LineChartTmpComp = withViewFn(LineChartTmpComp, (comp) => {
);
}, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]);
- useEffect(() => {
- comp.children.mapInstance.dispatch(changeValueAction(null, false))
- if(comp.children.mapInstance.value) return;
- }, [option])
-
return (
{
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
index 2ab7bca75..67ab564c3 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
@@ -1,6 +1,5 @@
import {
jsonControl,
- JSONObject,
stateComp,
toJSONObjectArray,
toObject,
@@ -8,17 +7,16 @@ import {
withDefault,
StringControl,
NumberControl,
- FunctionControl,
dropdownControl,
eventHandlerControl,
valueComp,
withType,
uiChildren,
clickEvent,
+ toArray,
styleControl,
EchartDefaultTextStyle,
EchartDefaultChartStyle,
- toArray
} from "lowcoder-sdk";
import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core";
import { BarChartConfig } from "../basicChartComp/chartConfigs/barChartConfig";
@@ -69,24 +67,6 @@ export const UIEventOptions = [
},
] as const;
-export const MapEventOptions = [
- {
- label: trans("chart.mapReady"),
- value: "mapReady",
- description: trans("chart.mapReadyDesc"),
- },
- {
- label: trans("chart.zoomLevelChange"),
- value: "zoomLevelChange",
- description: trans("chart.zoomLevelChangeDesc"),
- },
- {
- label: trans("chart.centerPositionChange"),
- value: "centerPositionChange",
- description: trans("chart.centerPositionChangeDesc"),
- },
-] as const;
-
export const XAxisDirectionOptions = [
{
label: trans("chart.horizontal"),
@@ -271,6 +251,7 @@ let chartJsonModeChildren: any = {
tooltip: withDefault(BoolControl, true),
legendVisibility: withDefault(BoolControl, true),
}
+
if (EchartDefaultChartStyle && EchartDefaultTextStyle) {
chartJsonModeChildren = {
...chartJsonModeChildren,
@@ -282,18 +263,6 @@ if (EchartDefaultChartStyle && EchartDefaultTextStyle) {
}
}
-const chartMapModeChildren = {
- mapInstance: stateComp(),
- getMapInstance: FunctionControl,
- mapApiKey: withDefault(StringControl, ''),
- mapZoomLevel: withDefault(NumberControl, 3),
- mapCenterLng: withDefault(NumberControl, 15.932644),
- mapCenterLat: withDefault(NumberControl, 50.942063),
- mapOptions: jsonControl(toObject, i18nObjs.defaultMapJsonOption),
- onMapEvent: eventHandlerControl(MapEventOptions),
- showCharts: withDefault(BoolControl, true),
-}
-
export type UIChartDataType = {
seriesName: string;
// coordinate chart
@@ -315,7 +284,6 @@ export const lineChartChildrenMap = {
onEvent: eventHandlerControl([clickEvent] as const),
...chartUiModeChildren,
...chartJsonModeChildren,
- ...chartMapModeChildren,
};
const chartUiChildrenMap = uiChildren(lineChartChildrenMap);
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index 4a6cb5cd1..4b2290d0f 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -71,7 +71,6 @@ export function isAxisChart(type: CharOptionCompType, subtype: string) {
}
export function getSeriesConfig(props: EchartsConfigProps) {
- console.log("SeriesProps:", props);
let visibleSeries = props.series.filter((s) => !s.getView().hide);
if(props.chartConfig.subtype === "waterfall") {
const seriesOn = visibleSeries[0];
@@ -108,6 +107,8 @@ export function getSeriesConfig(props: EchartsConfigProps) {
encodeX = s.getView().columnName;
encodeY = props.xAxisKey;
}
+ const markLineData = s.getView().markLines.map(line => ({type: line.getView().type}));
+ console.log("MLData", markLineData)
return {
name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
selectedMode: "single",
@@ -120,6 +121,9 @@ export function getSeriesConfig(props: EchartsConfigProps) {
x: encodeX,
y: encodeY,
},
+ markLine: {
+ data: markLineData,
+ },
// each type of chart's config
...props.chartConfig,
itemStyle: itemStyle,
@@ -215,7 +219,7 @@ export function getEchartsConfig(
.map((s) => s.getView().columnName);
// y-axis is category and time, data doesn't need to aggregate
let transformedData =
- yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.echartsOption.length && props.echartsOption || props.data : transformData(props.echartsOption.length && props.echartsOption || props.data, props.xAxisKey, seriesColumnNames);
+ yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.data : transformData(props.data, props.xAxisKey, seriesColumnNames);
if(props.chartConfig.subtype === "waterfall") {
config.legend = undefined;
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
index 9ded885b5..7593b8786 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
@@ -2,13 +2,15 @@ import {
BoolControl,
StringControl,
list,
- JSONObject,
isNumeric,
genRandomKey,
Dropdown,
- OptionsType,
+ Option,
+ RedButton,
+ CustomModal,
MultiCompBuilder,
valueComp,
+ dropdownControl,
} from "lowcoder-sdk";
import { trans } from "i18n/comps";
@@ -18,6 +20,7 @@ import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcod
export type SeriesCompType = ConstructorToComp;
export type RawSeriesCompType = ConstructorToView;
type SeriesDataType = ConstructorToDataType;
+type MarkLineDataType = ConstructorToDataType;
type ActionDataType = {
type: "chartDataChanged";
@@ -32,9 +35,46 @@ export function newSeries(name: string, columnName: string): SeriesDataType {
};
}
+export function newMarkLine(type: string): MarkLineDataType {
+ return {
+ type,
+ dataIndex: genRandomKey(),
+ };
+}
+
+export const MarkLineTypeOptions = [
+ {
+ label: trans("lineChart.max"),
+ value: "max",
+ },
+ {
+ label: trans("lineChart.average"),
+ value: "average",
+ },
+ {
+ label: trans("lineChart.min"),
+ value: "min",
+ },
+] as const;
+
+const valToLabel = (val) => MarkLineTypeOptions.find(o => o.value === val)?.label || "";
+const markLinesChildrenMap = {
+ type: dropdownControl(MarkLineTypeOptions, "max"),
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+const MarkLinesTmpComp = new MultiCompBuilder(markLinesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) => {
+ return <>{children.type.propertyView({label: trans("lineChart.type")})}>;
+ })
+ .build();
+
const seriesChildrenMap = {
columnName: StringControl,
seriesName: StringControl,
+ markLines: list(MarkLinesTmpComp),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -63,6 +103,42 @@ class SeriesComp extends SeriesTmpComp {
this.children.columnName.dispatchChangeValueAction(value);
}}
/>
+ valToLabel(s.getView().type)}
+ popoverTitle={(s) => trans("lineChart.markLineType")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ this.children.markLines.dispatch(this.children.markLines.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ this.children.markLines.dispatch(
+ this.children.markLines.pushAction(
+ newMarkLine("max")
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = this.children.markLines.arrayMoveAction(fromIndex, toIndex);
+ this.children.markLines.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
+ />
>
);
}
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 9b80cdf23..9d0b24b44 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -312,6 +312,16 @@ export const en = {
defaultBarometerPointer_Y: "-10"
},
+ lineChart: {
+ markLines: "Mark Lines",
+ stacked: "Stacked",
+ area: "Area Chart",
+ max: "Max",
+ min: "Min",
+ average: "Average",
+ markLineType: "Marker Type",
+ type: "Type",
+ },
barChart: {
title: 'Title',
barWidth: 'Bar Width(%)',
@@ -499,6 +509,7 @@ export const en = {
basicLine: "Basic Line",
stackedLine: "Stacked Line",
areaLine: "Area Line",
+ stackedAreaLine: "Stacked Area Line",
smooth: "Smooth Curve",
lineType: "Line Chart Type",
basicPie: "Basic Pie",
From 20963c794175e0bea011d4a43b4df75078fb5853 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Wed, 12 Feb 2025 10:27:52 -0500
Subject: [PATCH 015/124] Added mark areas to config
---
.../src/comps/barChartComp/barChartUtils.ts | 16 ++---
.../chartConfigs/barChartConfig.tsx | 4 +-
.../chartConfigs/lineChartConfig.tsx | 5 ++
.../src/comps/lineChartComp/lineChartUtils.ts | 10 ++-
.../src/comps/lineChartComp/seriesComp.tsx | 62 +++++++++++++++++++
.../src/i18n/comps/locales/en.ts | 4 ++
6 files changed, 89 insertions(+), 12 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 22de1df37..610d66f87 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -227,7 +227,7 @@ export function getEchartsConfig(
d[` `] = sum - d[seriesColumnNames[0]];
sum = d[` `];
})
- transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
+ transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total, [props.xAxisKey]: "Total"}, ...transformedData]
}
if(props.chartConfig.subtype === "polar") {
@@ -306,7 +306,7 @@ export function getEchartsConfig(
axisLabel: {
...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
},
- data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:undefined,
+ data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
},
// @ts-ignore
yAxis: {
@@ -337,7 +337,7 @@ export function getEchartsConfig(
//Waterfall x-label initialization
if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
//default labels
- config.xAxis.data = undefined;
+ // config.xAxis.data = undefined;
// config.xAxis.data = ["Total"];
// for(let i=1; i ({type: line.getView().type}));
- console.log("MLData", markLineData)
+ const markAreaData = s.getView().markAreas.map(area => ([{name: area.getView().name, xAxis: area.getView().from}, {xAxis: area.getView().to}]));
return {
name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
selectedMode: "single",
@@ -124,6 +124,12 @@ export function getSeriesConfig(props: EchartsConfigProps) {
markLine: {
data: markLineData,
},
+ markArea: {
+ itemStyle: {
+ color: 'rgba(255, 173, 177, 0.4)',
+ },
+ data: markAreaData,
+ },
// each type of chart's config
...props.chartConfig,
itemStyle: itemStyle,
@@ -313,7 +319,7 @@ export function getEchartsConfig(
axisLabel: {
...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
},
- data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:undefined,
+ data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
},
// @ts-ignore
yAxis: {
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
index 7593b8786..fe138239a 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/seriesComp.tsx
@@ -70,11 +70,37 @@ const MarkLinesTmpComp = new MultiCompBuilder(markLinesChildrenMap, (props) => {
return <>{children.type.propertyView({label: trans("lineChart.type")})}>;
})
.build();
+const markAreasChildrenMap = {
+ name: StringControl,
+ from: StringControl,
+ to: StringControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+const MarkAreasTmpComp = new MultiCompBuilder(markAreasChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) =>
+ (<>
+ {children.name.propertyView({label: trans("lineChart.name")})}
+ {children.from.propertyView({label: trans("lineChart.from")})}
+ {children.to.propertyView({label: trans("lineChart.to")})}
+ >)
+ )
+ .build();
+
+
+export function newMarkArea(): MarkLineDataType {
+ return {
+ dataIndex: genRandomKey(),
+ };
+}
const seriesChildrenMap = {
columnName: StringControl,
seriesName: StringControl,
markLines: list(MarkLinesTmpComp),
+ markAreas: list(MarkAreasTmpComp),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -138,6 +164,42 @@ class SeriesComp extends SeriesTmpComp {
dataIndex={(s) => {
return s.getView().dataIndex;
}}
+ />
+ s.getView().name}
+ popoverTitle={(s) => trans("lineChart.markLineType")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ this.children.markAreas.dispatch(this.children.markAreas.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ this.children.markAreas.dispatch(
+ this.children.markAreas.pushAction(
+ newMarkArea()
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = this.children.markAreas.arrayMoveAction(fromIndex, toIndex);
+ this.children.markAreas.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
/>
>
);
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 9d0b24b44..e04a62dce 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -314,6 +314,7 @@ export const en = {
},
lineChart: {
markLines: "Mark Lines",
+ markAreas: "Mark Areas",
stacked: "Stacked",
area: "Area Chart",
max: "Max",
@@ -321,6 +322,9 @@ export const en = {
average: "Average",
markLineType: "Marker Type",
type: "Type",
+ name: "Name",
+ from: "From",
+ to: "To",
},
barChart: {
title: 'Title',
From 1627cd7705da98a41f003f46138c30a9e9902675 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Wed, 12 Feb 2025 10:45:40 -0500
Subject: [PATCH 016/124] mark area label position when orientation changes
---
.../src/comps/barChartComp/barChartUtils.ts | 3 ++-
.../src/comps/lineChartComp/lineChartUtils.ts | 9 ++++++---
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 610d66f87..2173774bd 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -313,7 +313,8 @@ export function getEchartsConfig(
...finalXyConfig.yConfig,
axisLabel: {
...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
- }
+ },
+ data: finalXyConfig.yConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
},
};
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index 26050e419..b5d2227a8 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -14,7 +14,7 @@ import Big from "big.js";
import { googleMapsApiUrl } from "../basicChartComp/chartConfigs/chartUrls";
import opacityToHex from "../../util/opacityToHex";
import parseBackground from "../../util/gradientBackgroundColor";
-import {ba} from "@fullcalendar/core/internal-common";
+import {ba, s} from "@fullcalendar/core/internal-common";
import {chartStyleWrapper, styleWrapper} from "../../util/styleWrapper";
export function transformData(
@@ -108,7 +108,9 @@ export function getSeriesConfig(props: EchartsConfigProps) {
encodeY = props.xAxisKey;
}
const markLineData = s.getView().markLines.map(line => ({type: line.getView().type}));
- const markAreaData = s.getView().markAreas.map(area => ([{name: area.getView().name, xAxis: area.getView().from}, {xAxis: area.getView().to}]));
+ const markAreaData = s.getView().markAreas.map(area => ([{name: area.getView().name, [horizontalX?"xAxis":"yAxis"]: area.getView().from, label: {
+ position: horizontalX?"top":"right",
+ }}, {[horizontalX?"xAxis":"yAxis"]: area.getView().to}]));
return {
name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
selectedMode: "single",
@@ -326,7 +328,8 @@ export function getEchartsConfig(
...finalXyConfig.yConfig,
axisLabel: {
...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
- }
+ },
+ data: finalXyConfig.yConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
},
};
From 53ceb64360819794c5725b1cc80e951ded50f92c Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Wed, 12 Feb 2025 17:29:06 -0500
Subject: [PATCH 017/124] Remove variables from exposing config
---
.../lowcoder/src/comps/queries/queryComp.tsx | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
index 31e101b85..f7b405270 100644
--- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
@@ -664,23 +664,6 @@ export const QueryComp = withExposingConfigs(QueryCompTmp, [
new NameConfig("isFetching", trans("query.isFetchingExportDesc")),
new NameConfig("runTime", trans("query.runTimeExportDesc")),
new NameConfig("latestEndTime", trans("query.latestEndTimeExportDesc")),
- new DepsConfig(
- "variables",
- (children: any) => {
- return {data: children.variables.children.variables.node()};
- },
- (input) => {
- if (!input.data) {
- return undefined;
- }
- const newNode = Object.values(input.data)
- .filter((kvNode: any) => kvNode.key)
- .map((kvNode: any) => ({[kvNode.key]: kvNode.value}))
- .reduce((prev, obj) => ({...prev, ...obj}), {});
- return newNode;
- },
- trans("query.variables")
- ),
new NameConfig("triggerType", trans("query.triggerTypeExportDesc")),
]);
From bc1c2e7fe31c115423b1cc632679a9ae548fac63 Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Thu, 13 Feb 2025 11:55:26 +0100
Subject: [PATCH 018/124] Updating Docs and Readme
---
README.md | 18 +++++++-----------
.../develop-data-source-plugins.md | 8 ++++----
docs/setup-and-run/self-hosting/heroku.md | 2 +-
3 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 6ba2fff86..f0ecbb4b8 100644
--- a/README.md
+++ b/README.md
@@ -7,15 +7,15 @@
Create software applications (internal and customer-facing!) and Meeting/Collaboration tools for your Company and your Customers with minimal coding experience.
- Lowcoder is the best Retool, Appsmith or Tooljet Alternative.
+ We think, Lowcoder is simply better that Retool, Appsmith or Tooljet, Outsystems or Mendix.
-
+[](https://www.youtube.com/watch?v=AQo0iFWUWiU)
## 📢 Use Lowcoder in 3 steps
1. Connect to any data sources or APIs.
-2. Build flexible and responsive UI with 100+ components and free layout / design possibilities.
+2. Build flexible and responsive UI with 120+ components and free layout / design possibilities.
3. Share with colleagues and customers.
## 💡 Why Lowcoder
@@ -23,9 +23,9 @@ One platform for everything instead so many different softwares. (like Website B
It's cumbersome to create a single app. You had to design user interfaces, write code in multiple languages and frameworks, and understand how all of that code works together.
-NewGen Lowcode Platforms like Retool and others are great for their simplicity and flexibility - like Lowcoder too, but they can also be limited in different ways, especially when it comes to "external" applications for everyone.
+NewGen Lowcode Platforms like Retool and others are great for their simplicity and flexibility - like Lowcoder too, but they can also be limited in different ways, especially when it comes to "external" applications for everyone - because their pricing focusses to internal apps and "pay per User".
-Lowcoder wants to take a step forward. More specifically, Lowcoder is:
+With Lowcoder we did a step forward. More specifically, Lowcoder is:
- An all-in-one IDE to create internal or customer-facing (external) apps.
- A place to create, build and share building blocks of web applications and whole websites.
- The tool and community to support your business, and lower the cost and time to develop interactive applications.
@@ -34,9 +34,9 @@ Lowcoder wants to take a step forward. More specifically, Lowcoder is:
- The only platform which has extensibility plugin architecture [Check Community Contributions](https://www.npmjs.com/search?q=lowcoder-comp)
## 🪄 Features
-- **Visual UI builder** with 100+ built-in components. Save 90% of time to build apps.
+- **Visual UI builder** with 120+ built-in components. Save 90% of time to build apps.
- **Modules** for reusable (!) embedable component sets in the UI builder.
-- **Embed Lowcoder Apps as native parts of any Website** instead of iFrame (!). [Demo](https://lowcoder.cloud/about), [Docu](https://docs.lowcoder.cloud/lowcoder-documentation/lowcoder-extension/native-embed-sdk)
+- **Embed Lowcoder Apps as native parts of any Website** instead of iFrame (!). [Demo](http://demo-lowcoder.42web.io/ecommerce/), [Docu](https://docs.lowcoder.cloud/lowcoder-documentation/lowcoder-extension/native-embed-sdk)
- **Video Meeting Components** to create your own individual Web-Meeting tool.
- **Query Library** for reusable data queries of your data sources.
- **Custom components** to develop own components and use them in the UI builder.
@@ -107,7 +107,3 @@ Accelerate the growth of Lowcoder and unleash its potential with your Sponsorshi
[Be a Sponsor](https://github.com/sponsors/lowcoder-org)
Like ... [@Darkjamin](https://github.com/Darkjamin), [@spacegoats-io](https://github.com/spacegoats-io), [@Jomedya](https://github.com/Jomedya), [@CHSchuepfer](https://github.com/CHSchuepfer), Thank you very much!!
-
-## Intro Video
-
-[](https://youtu.be/s4ltAqS0hzM?feature=shared)
diff --git a/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md b/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md
index edd912076..7ff5814e8 100644
--- a/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md
+++ b/docs/lowcoder-extension/opensource-contribution/develop-data-source-plugins.md
@@ -1,6 +1,6 @@
# Develop Data Source Plugins
-This document provides basic information and guides for developing data source plugins. Developers are highly welcomed to make contributions to [Lowcoder](https://github.com/Lowcoder-dev/Lowcoder)--the open source project.
+This document provides basic information and guides for developing data source plugins. Developers are highly welcomed to make contributions to [Lowcoder](https://github.com/lowcoder-org/lowcoder)--the open source project.
## Basics
@@ -12,7 +12,7 @@ A data source plugin is described by a **JavaScript Object** which mainly consis
* Definition of the **Action list** for data source queries and the configuration form for each Action.
* Definition of the **execution logic** for Actions.
-Currently, all data source plugins are maintained in the `src/plugins` directory of the `node-service` project. Click to view [the project](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/server/node-service), and you might take a quick look at the [S3 plugin](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/server/node-service/src/plugins/s3).
+Currently, all data source plugins are maintained in the `src/plugins` directory of the `node-service` project. Click to view [the project](https://github.com/lowcoder-org/lowcoder/tree/main/server/node-service), and you might take a quick look at the [S3 plugin](https://github.com/lowcoder-org/lowcoder/tree/main/server/node-service/src/plugins/s3).
## Overall definition of a plugin
@@ -268,7 +268,7 @@ Due to various reasons, the generated plugin code needs to be correctly validate
## Testing
-Necessary testing should be done before publishing the plugin. Testing a data source plugin requires a backend environment. You can start a local environment by following the documentation [Start a local backend server](https://github.com/Lowcoder-dev/Lowcoder/tree/develop/client#readme) and test the data source plugin in following aspects:
+Necessary testing should be done before publishing the plugin. Testing a data source plugin requires a backend environment. You can start a local environment by following the documentation [Start a local backend server](https://github.com/lowcoder-org/lowcoder/tree/main/server/api-service#readme) and test the data source plugin in following aspects:
1. Make sure the data source plugin has been added to the plugin list in the file `src/plugins/index.ts`.
2. Start the node-service server in the `node-service` directory by executing `yarn dev`.
@@ -292,4 +292,4 @@ You can then use the data source plugin just developed in this environment.
## What's next
-Congrats! After testing the data source plugin, you can submit a [Pull Request](https://github.com/Lowcoder-dev/Lowcoder/pulls) now.
+Congrats! After testing the data source plugin, you can submit a [Pull Request](https://github.com/lowcoder-org/lowcoder/pulls) now.
diff --git a/docs/setup-and-run/self-hosting/heroku.md b/docs/setup-and-run/self-hosting/heroku.md
index 460850231..133252d19 100644
--- a/docs/setup-and-run/self-hosting/heroku.md
+++ b/docs/setup-and-run/self-hosting/heroku.md
@@ -3,7 +3,7 @@
## Deploy
1. [Sign up](https://signup.heroku.com/) for a new Heroku account, or [log in](https://id.heroku.com/login) to get started.
-2. Click to start Heroku [one-click deployment](https://heroku.com/deploy?template=https://github.com/Lowcoder-dev/Lowcoder).
+2. Click to start Heroku [one-click deployment](https://heroku.com/deploy?template=https://github.com/lowcoder-org/lowcoder).
3. Set the **App name** which will be part of the app URL later, and choose a region.
4. (Not required) Fill in the **Config Vars** according to the descriptions. These are all optional variables used for environment-specific configuration. You can skip this step and manage environment variables later.
5. Click the **Deploy app** button.
From 0a48951ccd10bff7e7196e4ae5c6a24cbdd813be Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Thu, 13 Feb 2025 13:29:09 +0100
Subject: [PATCH 019/124] Updating Readme
---
README.md | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index f0ecbb4b8..db490d73a 100644
--- a/README.md
+++ b/README.md
@@ -9,10 +9,17 @@
We think, Lowcoder is simply better that Retool, Appsmith or Tooljet, Outsystems or Mendix.
+---
-[](https://www.youtube.com/watch?v=AQo0iFWUWiU)
-
+## 🎥 Lowcoder Intro Video
+
+
+
+
+
Click the image above to watch the video on YouTube 📺
+
+---
## 📢 Use Lowcoder in 3 steps
1. Connect to any data sources or APIs.
2. Build flexible and responsive UI with 120+ components and free layout / design possibilities.
From f934edae7f60cbbe78fbcd1d84a390d7122b4300 Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Thu, 13 Feb 2025 13:30:30 +0100
Subject: [PATCH 020/124] Updating Readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index db490d73a..ede4bcc29 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
Create software applications (internal and customer-facing!) and Meeting/Collaboration tools for your Company and your Customers with minimal coding experience.
- We think, Lowcoder is simply better that Retool, Appsmith or Tooljet, Outsystems or Mendix.
+ We think, Lowcoder is simply better than Retool, Appsmith Tooljet, Outsystems or Mendix.
---
From 5c160a8cb1dace8d29f239f7f906dba6a4729332 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 13 Feb 2025 10:55:47 -0500
Subject: [PATCH 021/124] Fix issues on empty variable key & value and removed
placeholder variables
---
client/packages/lowcoder/src/comps/queries/queryComp.tsx | 2 +-
client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/queries/queryComp.tsx b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
index f7b405270..7e2cfaecd 100644
--- a/client/packages/lowcoder/src/comps/queries/queryComp.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryComp.tsx
@@ -363,7 +363,7 @@ QueryCompTmp = class extends QueryCompTmp {
}
if (action.type === CompActionTypes.EXECUTE_QUERY) {
if (getReduceContext().disableUpdateState) return this;
- if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {});
+ if(!action.args) action.args = this.children.variables.children.variables.toJsonValue().filter(kv => kv.key).reduce((acc, curr) => Object.assign(acc, {[curr.key as string]:curr.value}), {});
action.args.$queryName = this.children.name.getView();
return this.executeQuery(action);
diff --git a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
index d319689e7..dcb913a3d 100644
--- a/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
+++ b/client/packages/lowcoder/src/comps/queries/queryCompUtils.tsx
@@ -28,10 +28,9 @@ export function toQueryView(params: FunctionProperty[]) {
variables?: any;
timeout: InstanceType;
}): Promise => {
- console.log("toQueryView props", props, params);
const { applicationId, isViewMode } = getGlobalSettings();
- const mappedVariables = Object.keys(props.variables).map(key => ({key: `${props.args?.$queryName}.variables.${key}`, value: props.variables[key]}));
+ const mappedVariables = Object.keys(props.variables).filter(k => k !== "$queryName").map(key => ({key: `${props.args?.$queryName}.variables.${key}`, value: props.variables[key] || ""}));
let request: QueryExecuteRequest = {
path: props.applicationPath,
params: [
From aad00079269a1312d6b1265a0460230812806ae2 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 13 Feb 2025 12:25:57 -0500
Subject: [PATCH 022/124] area pieces
---
.../src/comps/barChartComp/barChartUtils.ts | 21 -----------
.../lineChartComp/lineChartConstants.tsx | 23 ++++++++++++
.../lineChartComp/lineChartPropertyView.tsx | 36 ++++++++++++++++++
.../src/comps/lineChartComp/lineChartUtils.ts | 37 +++++++------------
.../src/i18n/comps/locales/en.ts | 3 ++
5 files changed, 76 insertions(+), 44 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 2173774bd..0c5341edb 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -334,27 +334,6 @@ export function getEchartsConfig(
}
}
}
-
- //Waterfall x-label initialization
- if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
- //default labels
- // config.xAxis.data = undefined;
- // config.xAxis.data = ["Total"];
- // for(let i=1; i(""),
+};
+const AreaPiecesTmpComp = new MultiCompBuilder(areaPiecesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) =>
+ (<>
+ {children.color.propertyView({label: trans("lineChart.color")})}
+ {children.from.propertyView({label: trans("lineChart.from")})}
+ {children.to.propertyView({label: trans("lineChart.to")})}
+ >)
+ )
+ .build();
+
export type ChartSize = { w: number; h: number };
export const getDataKeys = (data: Array) => {
@@ -231,6 +253,7 @@ export const chartUiModeChildren = {
yConfig: YAxisConfig,
legendConfig: LegendConfig,
chartConfig: ChartOptionComp,
+ areaPieces: list(AreaPiecesTmpComp),
onUIEvent: eventHandlerControl(UIEventOptions),
};
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
index e002142a2..3ac05f76c 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
@@ -82,6 +82,42 @@ export function lineChartPropertyView(
onHide={(s, hide) => s.children.hide.dispatchChangeValueAction(hide)}
dataIndex={(s) => s.getView().dataIndex}
/>
+ `[${s.getView().from}-${s.getView().to}] ${s.getView().color}`}
+ popoverTitle={(s) => trans("lineChart.areaPiece")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ children.areaPieces.dispatch(children.areaPieces.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ children.areaPieces.dispatch(
+ children.areaPieces.pushAction(
+ {}
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = children.areaPieces.arrayMoveAction(fromIndex, toIndex);
+ children.areaPieces.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
+ />
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index b5d2227a8..e5e799d74 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -203,7 +203,21 @@ export function getEchartsConfig(
...gridPos,
containLabel: true,
},
+ visualMap: {
+ type: 'piecewise',
+ show: false,
+ dimension: 0,
+ seriesIndex: 0,
+ pieces: props.areaPieces?.filter(p => p.getView().from && p.getView().to && p.getView().color)?.map(p => (
+ {
+ ...(p.getView().from?{min: parseInt(p.getView().from)}:{}),
+ ...(p.getView().to?{max: parseInt(p.getView().to)}:{}),
+ ...(p.getView().color?{color: p.getView().color}:{}),
+ }
+ ))
+ },
};
+ console.log("config", config)
if(props.chartConfig.race) {
config = {
...config,
@@ -264,8 +278,6 @@ export function getEchartsConfig(
}
}
- console.log("TransformedData", transformedData);
-
config = {
...config,
dataset: [
@@ -350,27 +362,6 @@ export function getEchartsConfig(
}
}
- //Waterfall x-label initialization
- if(props.chartConfig?.subtype === "waterfall" && props.xAxisData.length === 0) {
- //default labels
- config.xAxis.data = undefined;
- // config.xAxis.data = ["Total"];
- // for(let i=1; i
Date: Thu, 13 Feb 2025 13:01:27 -0500
Subject: [PATCH 023/124] check for area pieces length
---
.../lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index e5e799d74..eff602812 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -205,7 +205,7 @@ export function getEchartsConfig(
},
visualMap: {
type: 'piecewise',
- show: false,
+ show: props.areaPieces.length > 0,
dimension: 0,
seriesIndex: 0,
pieces: props.areaPieces?.filter(p => p.getView().from && p.getView().to && p.getView().color)?.map(p => (
From a60dad03f92d56f61b734dba4b3bc1eb0cb8d806 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 13 Feb 2025 13:22:00 -0500
Subject: [PATCH 024/124] end label
---
.../basicChartComp/chartConfigs/lineChartConfig.tsx | 11 +++++++++++
.../src/comps/lineChartComp/lineChartUtils.ts | 10 ++++++----
.../lowcoder-comps/src/i18n/comps/locales/en.ts | 1 +
3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
index 89cca3685..c9d07ba26 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
@@ -32,6 +32,7 @@ export const LineChartConfig = (function () {
return new MultiCompBuilder(
{
showLabel: BoolControl,
+ showEndLabel: BoolControl,
stacked: BoolControl,
area: BoolControl,
smooth: BoolControl,
@@ -71,6 +72,13 @@ export const LineChartConfig = (function () {
if (props.smooth) {
config.smooth = true;
}
+ if (props.showEndLabel) {
+ config.endLabel = {
+ show: true,
+ formatter: '{a}',
+ distance: 20
+ }
+ }
return config;
}
)
@@ -83,6 +91,9 @@ export const LineChartConfig = (function () {
label: trans("lineChart.area"),
})}
{showLabelPropertyView(children)}
+ {children.showEndLabel.propertyView({
+ label: trans("lineChart.showEndLabel"),
+ })}
{children.smooth.propertyView({ label: trans("chart.smooth") })}
{children.itemColor.getPropertyView()}
>
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index eff602812..9b2df9042 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -203,9 +203,11 @@ export function getEchartsConfig(
...gridPos,
containLabel: true,
},
- visualMap: {
+ };
+ if (props.areaPieces.length > 0) {
+ config.visualMap = {
type: 'piecewise',
- show: props.areaPieces.length > 0,
+ show: false,
dimension: 0,
seriesIndex: 0,
pieces: props.areaPieces?.filter(p => p.getView().from && p.getView().to && p.getView().color)?.map(p => (
@@ -215,8 +217,8 @@ export function getEchartsConfig(
...(p.getView().color?{color: p.getView().color}:{}),
}
))
- },
- };
+ }
+ }
console.log("config", config)
if(props.chartConfig.race) {
config = {
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 a0e80e476..f46557f73 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -328,6 +328,7 @@ export const en = {
color: "Color",
areaPieces: "Area Pieces",
areaPiece: "Area Piece",
+ showEndLabel: "End Label",
},
barChart: {
title: 'Title',
From cece2a4deb1a842d6b474784932a0d46604a9274 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Thu, 13 Feb 2025 13:41:54 -0500
Subject: [PATCH 025/124] animation duration & symbol
---
.../chartConfigs/lineChartConfig.tsx | 51 +++++++++++++++++++
.../lineChartComp/lineChartConstants.tsx | 1 +
.../lineChartComp/lineChartPropertyView.tsx | 1 +
.../src/comps/lineChartComp/lineChartUtils.ts | 2 +-
.../src/i18n/comps/locales/en.ts | 5 ++
5 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
index c9d07ba26..91da1eea6 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
@@ -11,6 +11,8 @@ import {
withContext,
RedButton,
StringControl,
+ NumberControl,
+ withDefault,
ColorOrBoolCodeControl,
} from "lowcoder-sdk";
import { trans } from "i18n/comps";
@@ -28,6 +30,45 @@ export const ItemColorComp = withContext(
["seriesName", "value"] as const
);
+export const SymbolOptions = [
+ {
+ label: trans("chart.rect"),
+ value: "rect",
+ },
+ {
+ label: trans("chart.circle"),
+ value: "circle",
+ },
+ {
+ label: trans("chart.roundRect"),
+ value: "roundRect",
+ },
+ {
+ label: trans("chart.triangle"),
+ value: "triangle",
+ },
+ {
+ label: trans("chart.diamond"),
+ value: "diamond",
+ },
+ {
+ label: trans("chart.pin"),
+ value: "pin",
+ },
+ {
+ label: trans("chart.arrow"),
+ value: "arrow",
+ },
+ {
+ label: trans("chart.none"),
+ value: "none",
+ },
+ {
+ label: trans("chart.emptyCircle"),
+ value: "emptyCircle",
+ },
+] as const;
+
export const LineChartConfig = (function () {
return new MultiCompBuilder(
{
@@ -37,6 +78,8 @@ export const LineChartConfig = (function () {
area: BoolControl,
smooth: BoolControl,
itemColor: ItemColorComp,
+ symbol: dropdownControl(SymbolOptions, "emptyCircle"),
+ symbolSize: withDefault(NumberControl, 4),
},
(props): LineSeriesOption => {
const config: LineSeriesOption = {
@@ -44,6 +87,8 @@ export const LineChartConfig = (function () {
label: {
show: props.showLabel,
},
+ symbol: props.symbol,
+ symbolSize: props.symbolSize,
itemStyle: {
color: (params) => {
if (!params.encode || !params.dimensionNames) {
@@ -94,6 +139,12 @@ export const LineChartConfig = (function () {
{children.showEndLabel.propertyView({
label: trans("lineChart.showEndLabel"),
})}
+ {children.symbol.propertyView({
+ label: trans("lineChart.symbol"),
+ })}
+ {children.symbolSize.propertyView({
+ label: trans("lineChart.symbolSize"),
+ })}
{children.smooth.propertyView({ label: trans("chart.smooth") })}
{children.itemColor.getPropertyView()}
>
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
index 862427cb8..2685f1972 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartConstants.tsx
@@ -254,6 +254,7 @@ export const chartUiModeChildren = {
legendConfig: LegendConfig,
chartConfig: ChartOptionComp,
areaPieces: list(AreaPiecesTmpComp),
+ animationDuration: withDefault(NumberControl, 1000),
onUIEvent: eventHandlerControl(UIEventOptions),
};
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
index 3ac05f76c..d330a3888 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
@@ -27,6 +27,7 @@ export function lineChartPropertyView(
<>
{children.chartConfig.getPropertyView()}
+ {children.animationDuration.propertyView({label: "lineChart.animationDuration"})}
0) {
config.visualMap = {
@@ -219,7 +220,6 @@ export function getEchartsConfig(
))
}
}
- console.log("config", config)
if(props.chartConfig.race) {
config = {
...config,
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 f46557f73..9fa23e6c4 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,8 @@ export const en = {
areaPieces: "Area Pieces",
areaPiece: "Area Piece",
showEndLabel: "End Label",
+ symbol: "Symbol",
+ symbolSize: "Symbol Size",
},
barChart: {
title: 'Title',
@@ -555,6 +557,9 @@ export const en = {
diamond: "Diamond",
pin: "Pin",
arrow: "Arrow",
+ emptyCircle: "Empty Circle",
+ none: "None",
+ roundRect: "Round Rectangle",
pointColorLabel: "Point Color",
pointColorTooltip:
'Set point color based on series name and value. Variables: seriesName, value. Example: \'{{value < 25000 ? "red" : "green"}}\'',
From 48c10a0ebdd94b07c7ab8159913161699f3dddf3 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Fri, 14 Feb 2025 09:28:42 -0500
Subject: [PATCH 026/124] step line
---
.../lineChartComp/lineChartPropertyView.tsx | 2 +-
.../src/comps/lineChartComp/lineChartUtils.ts | 1 +
.../src/comps/lineChartComp/seriesComp.tsx | 23 +++++++++++++++++++
.../src/i18n/comps/locales/en.ts | 6 +++++
4 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
index d330a3888..5a67d8ecf 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartPropertyView.tsx
@@ -27,7 +27,7 @@ export function lineChartPropertyView(
<>
{children.chartConfig.getPropertyView()}
- {children.animationDuration.propertyView({label: "lineChart.animationDuration"})}
+ {children.animationDuration.propertyView({label: trans("lineChart.animationDuration")})}
MarkLineTypeOptions.find(o => o.value === val)?.label || "";
const markLinesChildrenMap = {
type: dropdownControl(MarkLineTypeOptions, "max"),
@@ -104,6 +123,7 @@ const seriesChildrenMap = {
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
+ step: dropdownControl(StepOptions, ""),
};
const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
@@ -129,6 +149,9 @@ class SeriesComp extends SeriesTmpComp {
this.children.columnName.dispatchChangeValueAction(value);
}}
/>
+ {this.children.step.propertyView({
+ label: trans("lineChart.step"),
+ })}
Date: Fri, 14 Feb 2025 10:46:58 -0500
Subject: [PATCH 027/124] polar line chart
---
.../src/comps/barChartComp/barChartUtils.ts | 4 +-
.../chartConfigs/lineChartConfig.tsx | 51 ++++++++++++++++++-
.../src/comps/lineChartComp/lineChartUtils.ts | 47 ++++-------------
.../src/i18n/comps/locales/en.ts | 3 +-
4 files changed, 62 insertions(+), 43 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
index 0c5341edb..60b2a9031 100644
--- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartUtils.ts
@@ -238,12 +238,12 @@ export function getEchartsConfig(
},
radiusAxis: {
type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
- data: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.labelData:undefined,
+ data: props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
},
angleAxis: {
type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
- data: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.labelData,
+ data: !props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
startAngle: props.chartConfig.polarData.polarStartAngle,
endAngle: props.chartConfig.polarData.polarEndAngle,
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
index 91da1eea6..703ebcead 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
@@ -7,6 +7,8 @@ import {
Option,
valueComp,
genRandomKey,
+ jsonControl,
+ toArray,
showLabelPropertyView,
withContext,
RedButton,
@@ -77,9 +79,17 @@ export const LineChartConfig = (function () {
stacked: BoolControl,
area: BoolControl,
smooth: BoolControl,
+ polar: BoolControl,
itemColor: ItemColorComp,
symbol: dropdownControl(SymbolOptions, "emptyCircle"),
symbolSize: withDefault(NumberControl, 4),
+ radiusAxisMax: NumberControl,
+ polarRadiusStart: withDefault(StringControl, '30'),
+ polarRadiusEnd: withDefault(StringControl, '80%'),
+ polarStartAngle: withDefault(NumberControl, 90),
+ polarEndAngle: withDefault(NumberControl, -180),
+ polarIsTangent: withDefault(BoolControl, false),
+ labelData: jsonControl(toArray, []),
},
(props): LineSeriesOption => {
const config: LineSeriesOption = {
@@ -94,7 +104,7 @@ export const LineChartConfig = (function () {
if (!params.encode || !params.dimensionNames) {
return params.color;
}
- const dataKey = params.dimensionNames[params.encode["y"][0]];
+ const dataKey = params.dimensionNames[params.encode[props.polar?"radius":"y"][0]];
const color = (props.itemColor as any)({
seriesName: params.seriesName,
value: (params.data as any)[dataKey],
@@ -107,6 +117,16 @@ export const LineChartConfig = (function () {
return color;
},
},
+ polarData: {
+ polar: props.polar,
+ radiusAxisMax: props.radiusAxisMax,
+ polarRadiusStart: props.polarRadiusStart,
+ polarRadiusEnd: props.polarRadiusEnd,
+ polarStartAngle: props.polarStartAngle,
+ polarEndAngle: props.polarEndAngle,
+ labelData: props.labelData,
+ polarIsTangent: props.polarIsTangent,
+ },
};
if (props.stacked) {
config.stack = "stackValue";
@@ -124,6 +144,9 @@ export const LineChartConfig = (function () {
distance: 20
}
}
+ if (props.polar) {
+ config.coordinateSystem = 'polar';
+ }
return config;
}
)
@@ -135,17 +158,41 @@ export const LineChartConfig = (function () {
{children.area.propertyView({
label: trans("lineChart.area"),
})}
+ {children.polar.propertyView({
+ label: trans("lineChart.polar"),
+ })}
+ {children.polar.getView() && children.polarIsTangent.propertyView({
+ label: trans("barChart.polarIsTangent"),
+ })}
+ {children.polar.getView() && children.polarStartAngle.propertyView({
+ label: trans("barChart.polarStartAngle"),
+ })}
+ {children.polar.getView() && children.polarEndAngle.propertyView({
+ label: trans("barChart.polarEndAngle"),
+ })}
+ {children.polar.getView() && children.radiusAxisMax.propertyView({
+ label: trans("barChart.radiusAxisMax"),
+ })}
+ {children.polar.getView() && children.polarRadiusStart.propertyView({
+ label: trans("barChart.polarRadiusStart"),
+ })}
+ {children.polar.getView() && children.polarRadiusEnd.propertyView({
+ label: trans("barChart.polarRadiusEnd"),
+ })}
+ {children.polar.getView() && children.labelData.propertyView({
+ label: trans("barChart.polarLabelData"),
+ })}
{showLabelPropertyView(children)}
{children.showEndLabel.propertyView({
label: trans("lineChart.showEndLabel"),
})}
+ {children.smooth.propertyView({ label: trans("chart.smooth") })}
{children.symbol.propertyView({
label: trans("lineChart.symbol"),
})}
{children.symbolSize.propertyView({
label: trans("lineChart.symbolSize"),
})}
- {children.smooth.propertyView({ label: trans("chart.smooth") })}
{children.itemColor.getPropertyView()}
>
))
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index 611810e06..b3c77f7d9 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -66,8 +66,8 @@ export const echartsConfigOmitChildren = [
type EchartsConfigProps = Omit;
-export function isAxisChart(type: CharOptionCompType, subtype: string) {
- return !notAxisChartSet.has(type) && !notAxisChartSubtypeSet.has(subtype);
+export function isAxisChart(type: CharOptionCompType, polar: boolean) {
+ return !notAxisChartSet.has(type) && !polar;
}
export function getSeriesConfig(props: EchartsConfigProps) {
@@ -79,27 +79,11 @@ export function getSeriesConfig(props: EchartsConfigProps) {
}
const seriesLength = visibleSeries.length;
return visibleSeries.map((s, index) => {
- if (isAxisChart(props.chartConfig.type, props.chartConfig.subtype)) {
+ if (isAxisChart(props.chartConfig.type, props.chartConfig.polarData.polar)) {
let encodeX: string, encodeY: string;
const horizontalX = props.xAxisDirection === "horizontal";
let itemStyle = props.chartConfig.itemStyle;
- // FIXME: need refactor... chartConfig returns a function with paramters
- if (props.chartConfig.type === "bar") {
- // barChart's border radius, depend on x-axis direction and stack state
- const borderRadius = horizontalX ? [2, 2, 0, 0] : [0, 2, 2, 0];
- if (props.chartConfig.stack && index === visibleSeries.length - 1) {
- itemStyle = { ...itemStyle, borderRadius: borderRadius };
- } else if (!props.chartConfig.stack) {
- itemStyle = { ...itemStyle, borderRadius: borderRadius };
- }
- if(props.chartConfig.subtype === "waterfall" && index === 0) {
- itemStyle = {
- borderColor: 'transparent',
- color: 'transparent'
- }
- }
- }
if (horizontalX) {
encodeX = props.xAxisKey;
encodeY = s.getView().columnName;
@@ -112,7 +96,7 @@ export function getSeriesConfig(props: EchartsConfigProps) {
position: horizontalX?"top":"right",
}}, {[horizontalX?"xAxis":"yAxis"]: area.getView().to}]));
return {
- name: props.chartConfig.subtype === "waterfall" && index === 0?" ":s.getView().seriesName,
+ name: s.getView().seriesName,
selectedMode: "single",
select: {
itemStyle: {
@@ -166,7 +150,7 @@ export function getEchartsConfig(
theme?: any,
): EChartsOptionWithMap {
// axisChart
- const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.subtype);
+ const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.polarData.polar);
const gridPos = {
left: `${props?.left}%`,
right: `${props?.right}%`,
@@ -246,21 +230,7 @@ export function getEchartsConfig(
let transformedData =
yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.data : transformData(props.data, props.xAxisKey, seriesColumnNames);
- if(props.chartConfig.subtype === "waterfall") {
- config.legend = undefined;
- let sum = transformedData.reduce((acc, item) => {
- if(typeof item[seriesColumnNames[0]] === 'number') return acc + item[seriesColumnNames[0]];
- else return acc;
- }, 0)
- const total = sum;
- transformedData.map(d => {
- d[` `] = sum - d[seriesColumnNames[0]];
- sum = d[` `];
- })
- transformedData = [{[seriesColumnNames[0] + "_placeholder"]: 0, [seriesColumnNames[0]]: total}, ...transformedData]
- }
-
- if(props.chartConfig.subtype === "polar") {
+ if(props.chartConfig.polarData.polar) {
config = {
...config,
polar: {
@@ -268,12 +238,12 @@ export function getEchartsConfig(
},
radiusAxis: {
type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
- data: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.labelData:undefined,
+ data: props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
},
angleAxis: {
type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
- data: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.labelData,
+ data: !props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
startAngle: props.chartConfig.polarData.polarStartAngle,
endAngle: props.chartConfig.polarData.polarEndAngle,
@@ -364,6 +334,7 @@ export function getEchartsConfig(
}
}
}
+ console.log("config", config);
// log.log("Echarts transformedData and config", transformedData, config);
return config;
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 6fe28a0db..1d8fd2319 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -315,7 +315,7 @@ export const en = {
lineChart: {
markLines: "Mark Lines",
markAreas: "Mark Areas",
- stacked: "Stacked",
+ stacked: "Stacked Chart",
area: "Area Chart",
max: "Max",
min: "Min",
@@ -337,6 +337,7 @@ export const en = {
middle: "Middle",
end: "End",
step: "Step",
+ polar: "Polar Chart",
},
barChart: {
title: 'Title',
From 37191aa80bb3cfe6c45da7b71ac5cdec51d02456 Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Sun, 16 Feb 2025 14:05:24 +0100
Subject: [PATCH 028/124] Update of Subscription Apis to enable user cleanup
possibility.
---
.../lowcoder/src/api/subscriptionApi.ts | 16 +++++++++
.../src/redux/sagas/subscriptionSagas.ts | 2 --
.../src/util/context/SubscriptionContext.tsx | 36 +++----------------
3 files changed, 20 insertions(+), 34 deletions(-)
diff --git a/client/packages/lowcoder/src/api/subscriptionApi.ts b/client/packages/lowcoder/src/api/subscriptionApi.ts
index 02a3ff5f6..6bfcdb259 100644
--- a/client/packages/lowcoder/src/api/subscriptionApi.ts
+++ b/client/packages/lowcoder/src/api/subscriptionApi.ts
@@ -182,6 +182,22 @@ export const createCustomer = async (subscriptionCustomer: LowcoderNewCustomer)
}
};
+export const cleanupCustomer = async (subscriptionCustomer: LowcoderSearchCustomer) => {
+ const apiBody = {
+ path: "webhook/secure/cleanup-customer",
+ data: subscriptionCustomer,
+ method: "post",
+ headers: lcHeaders
+ };
+ try {
+ const result = await SubscriptionApi.secureRequest(apiBody, 15000);
+ return result?.data as any;
+ } catch (error) {
+ console.error("Error creating customer:", error);
+ throw error;
+ }
+};
+
export const getProduct = async (productId : string) => {
const apiBody = {
path: "webhook/secure/get-product",
diff --git a/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts b/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts
index 8f43b9fc1..1e94116d7 100644
--- a/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts
+++ b/client/packages/lowcoder/src/redux/sagas/subscriptionSagas.ts
@@ -11,9 +11,7 @@ import { Subscription, LowcoderSearchCustomer } from '@lowcoder-ee/constants/sub
function* fetchSubscriptionsSaga(action: ReturnType) {
try {
const user: User = yield select(getUser);
- const currentUser: CurrentUser = yield select(getCurrentUser);
const orgID = user.currentOrgId;
- const domain = `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}`;
const deploymentId: string = yield select(getDeploymentId);
const subscriptionSearchCustomer: LowcoderSearchCustomer = {
diff --git a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx
index f89e5ff77..1d1b16d93 100644
--- a/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx
+++ b/client/packages/lowcoder/src/util/context/SubscriptionContext.tsx
@@ -1,4 +1,4 @@
-import { createCheckoutLink, createCustomer, getProducts, searchCustomer } from "@lowcoder-ee/api/subscriptionApi";
+import { createCheckoutLink, cleanupCustomer } from "@lowcoder-ee/api/subscriptionApi";
import { StripeCustomer, SubscriptionProduct, InitSubscriptionProducts, LowcoderSearchCustomer, LowcoderNewCustomer, Subscription } from "@lowcoder-ee/constants/subscriptionConstants";
import { getDeploymentId } from "@lowcoder-ee/redux/selectors/configSelectors";
import { getFetchSubscriptionsFinished, getSubscriptions, getSubscriptionsError } from "@lowcoder-ee/redux/selectors/subscriptionSelectors";
@@ -78,17 +78,6 @@ export const SubscriptionContextProvider = (props: {
userId: user.id,
};
- const subscriptionNewCustomer: LowcoderNewCustomer = {
- hostname: domain,
- hostId: deploymentId,
- email: currentUser.email,
- orgId: orgID,
- userId: user.id,
- userName: user.username,
- type: admin,
- companyName: currentOrg?.name || "Unknown",
- };
-
useEffect(() => {
// If products are already loaded in the outer context, reuse them
if (productsLoaded) {
@@ -104,28 +93,11 @@ export const SubscriptionContextProvider = (props: {
const initializeCustomer = async () => {
if (existingCustomer) {
setCustomer(existingCustomer);
+
+ cleanupCustomer(subscriptionSearchCustomer);
+
return;
}
-
- /* try {
- setIsCreatingCustomer(true);
- const subscriptionSearchCustomer: LowcoderSearchCustomer = {
- hostId: deploymentId,
- orgId: orgID,
- userId: user.id,
- };
- const existingCustomer = await searchCustomer(subscriptionSearchCustomer);
- if (existingCustomer) {
- setCustomer(existingCustomer);
- } else {
- const newCustomer = await createCustomer(subscriptionNewCustomer);
- setCustomer(newCustomer);
- }
- } catch (error) {
- setCustomerDataError(true);
- } finally {
- setIsCreatingCustomer(false);
- } */
};
if (!customer && isCustomerInitializationComplete) {
From e2b2167081500e016fece3fe945c289241c33b14 Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Sun, 16 Feb 2025 14:13:19 +0100
Subject: [PATCH 029/124] Placing Support Icon at Main
---
.../lowcoder/src/pages/ApplicationV2/index.tsx | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
index ccbdf1a78..71c13d039 100644
--- a/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
+++ b/client/packages/lowcoder/src/pages/ApplicationV2/index.tsx
@@ -10,6 +10,7 @@ import {
TRASH_URL,
NEWS_URL,
ORG_HOME_URL,
+ SUBSCRIPTION_SETTING,
} from "constants/routesURL";
import { getUser, isFetchingUser } from "redux/selectors/usersSelectors";
import { useDispatch, useSelector } from "react-redux";
@@ -57,6 +58,7 @@ import { trans } from "../../i18n";
import { foldersSelector } from "../../redux/selectors/folderSelector";
import Setting from "pages/setting";
import { Support } from "pages/support";
+import { Subscription } from "pages/setting/subscriptions"
// import { TypographyText } from "../../components/TypographyText";
// import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
import { isEE } from "util/envUtils";
@@ -248,6 +250,19 @@ export default function ApplicationHome() {
],
} : { items: [] },
+ !supportSubscription && user.orgDev ? {
+ items: [
+ {
+ text: {trans("home.support")} ,
+ routePath: SUBSCRIPTION_SETTING,
+ routeComp: Subscription,
+ routePathExact: false,
+ icon: ({ selected, ...otherProps }) => selected ? : ,
+ mobileVisible: true,
+ },
+ ],
+ } : { items: [] },
+
supportSubscription && user.orgDev ? {
items: [
{
From c42c0e0f09cdd0423e35e19658f826aea1537e7d Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Sun, 16 Feb 2025 19:20:59 +0500
Subject: [PATCH 030/124] fixed lowcoder-sdk build
---
client/packages/lowcoder-sdk/package.json | 5 +-
client/packages/lowcoder-sdk/vite.config.mts | 79 +++++++-------------
2 files changed, 29 insertions(+), 55 deletions(-)
diff --git a/client/packages/lowcoder-sdk/package.json b/client/packages/lowcoder-sdk/package.json
index f5df08715..360c79e3b 100644
--- a/client/packages/lowcoder-sdk/package.json
+++ b/client/packages/lowcoder-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "lowcoder-sdk",
- "version": "2.6.3",
+ "version": "2.6.4",
"type": "module",
"files": [
"src",
@@ -25,6 +25,9 @@
},
"./dist/style.css": {
"import": "./dist/style.css"
+ },
+ "./dist/chunks/": {
+ "import": "./dist/chunks/"
}
},
"scripts": {
diff --git a/client/packages/lowcoder-sdk/vite.config.mts b/client/packages/lowcoder-sdk/vite.config.mts
index 903b336f6..3cd7b5c45 100644
--- a/client/packages/lowcoder-sdk/vite.config.mts
+++ b/client/packages/lowcoder-sdk/vite.config.mts
@@ -36,17 +36,7 @@ export const viteConfig: UserConfig = {
},
base: ensureLastSlash(process.env.PUBLIC_URL),
build: {
- minify: "terser",
- terserOptions: {
- compress: {
- drop_console: true,
- drop_debugger: true,
- pure_funcs: ["console.info", "console.debug", "console.log"],
- },
- format: {
- comments: false,
- },
- },
+ minify: "terser",
chunkSizeWarningLimit: 500,
lib: {
formats: ["es"],
@@ -56,7 +46,7 @@ export const viteConfig: UserConfig = {
},
rollupOptions: {
treeshake: {
- moduleSideEffects: false,
+ moduleSideEffects: true,
propertyReadSideEffects: false,
tryCatchDeoptimization: false,
unknownGlobalSideEffects: false,
@@ -64,38 +54,23 @@ export const viteConfig: UserConfig = {
external: ["react", "react-dom"],
output: {
chunkFileNames: "chunks/[name]-[hash].js",
- entryFileNames: "entry/[name]-[hash].js",
- assetFileNames: "assets/[name]-[hash].[ext]",
+ entryFileNames: "lowcoder-sdk.js",
+ assetFileNames: "style.css",
manualChunks: (id) => {
if (id.includes("node_modules")) {
- // CORE FRAMEWORK CHUNKS
- if (id.includes("react")) return "react";
- if (id.includes("react-dom")) return "react-dom";
- if (id.includes("react-router")) return "react-router";
- if (id.includes("react-redux")) return "react-redux";
- if (id.includes("redux")) return "redux";
- if (id.includes("redux-saga")) return "redux-saga";
-
// UI LIBRARIES
if (id.includes("@ant-design/icons")) return "ant-design-icons";
- if (id.includes("antd")) return "antd";
+ if (id.includes("node_modules/antd")) return "antd";
if (id.includes("styled-components")) return "styled-components";
// 🔹 BARCODE & QR CODE PROCESSING
- if (id.includes("zxing") || id.includes("Barcode") || id.includes("QRCode") || id.includes("PDF417")) return "barcode";
-
- // CHARTING & DATA VISUALIZATION
- if (id.includes("echarts")) return "echarts";
- if (id.includes("echarts-wordcloud")) return "echarts-wordcloud";
- if (id.includes("d3")) return "d3";
+ if (id.includes("react-qr-barcode-scanner")) return "barcode";
// TEXT EDITORS & PARSERS
if (id.includes("codemirror")) return "codemirror";
if (id.includes("quill")) return "quill";
if (id.includes("react-json-view")) return "react-json-view";
- if (id.includes("react-markdown")) return "react-markdown";
if (id.includes("react-quill")) return "react-quill";
- if (id.includes("remark") || id.includes("rehype") || id.includes("markdown")) return "markdown-parsers";
if (id.includes("remark-gfm")) return "remark-gfm";
if (id.includes("rehype-raw")) return "rehype-raw";
if (id.includes("rehype-sanitize")) return "rehype-sanitize";
@@ -133,7 +108,6 @@ export const viteConfig: UserConfig = {
if (id.includes("xlsx")) return "xlsx";
if (id.includes("alasql")) return "alasql";
if (id.includes("sql-formatter")) return "sql-formatter";
- if (id.includes("tern")) return "tern";
// NETWORK & HTTP
if (id.includes("axios")) return "axios";
@@ -158,41 +132,38 @@ export const viteConfig: UserConfig = {
if (id.includes("cnchar")) return "cnchar";
if (id.includes("hotkeys-js")) return "hotkeys-js";
if (id.includes("loglevel")) return "loglevel";
- if (id.includes("qrcode-react")) return "qrcode-react";
+ if (id.includes("qrcode.react")) return "qrcode-react";
if (id.includes("react-joyride")) return "react-joyride";
if (id.includes("rc-trigger")) return "rc-trigger";
if (id.includes("really-relaxed-json")) return "really-relaxed-json";
if (id.includes("simplebar-react")) return "simplebar-react";
- return "vendor";
+ if (id.includes("react-documents")) return "react-documents";
+ if (id.includes("react-colorful")) return "react-colorful";
+ if (id.includes("react-best-gradient-color-picker")) return "react-best-gradient-color-picker";
+ if (id.includes("@supabase/supabase-js")) return "supabase";
+ return null;
}
- if (id.includes("src/api")) return "api";
- if (id.includes("src/appView")) return "appView";
- if (id.includes("src/base")) return "base";
- if (id.includes("src/constants")) return "constants";
- if (id.includes("src/i18n")) return "i18n";
- if (id.includes("src/ide")) return "ide";
- if (id.includes("src/layout")) return "layout";
- if (id.includes("src/pages")) return "pages";
- if (id.includes("src/redux")) return "app_redux";
- if (id.includes("src/comps")) return "comps";
- if (id.includes("comps/comps")) return "comps2";
- if (id.includes("comps/controls")) return "controls";
- if (id.includes("comps/queries")) return "queries";
- if (id.includes("comps/utils")) return "utils";
- if (id.includes("src/hooks")) return "hooks";
- if (id.includes("src/util")) return "util";
- return "common"; // 📦 Internal app shared code
- },
+ return null;
+ }
},
experimental: {
minChunkSize: 300000, // 📏 Force smaller chunks (~300KB)
},
plugins: [
- terser(),
+ terser({
+ compress: {
+ drop_console: true,
+ drop_debugger: true,
+ pure_funcs: ["console.info", "console.debug", "console.log"],
+ },
+ format: {
+ comments: /(@vite-ignore|webpackIgnore)/
+ },
+ }) as PluginOption,
strip({
functions: ["console.log", "debugger"], // ✅ Remove logs
sourceMap: true,
- }),
+ }) as PluginOption,
],
onwarn: (warning, warn) => {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
From dca89d20d1fe8593fac7724503f3ecf29ff7d4e6 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Sun, 16 Feb 2025 14:28:56 -0500
Subject: [PATCH 031/124] itemStyle
---
.../chartConfigs/lineChartConfig.tsx | 37 ++++++++++++++++---
.../src/comps/lineChartComp/lineChartUtils.ts | 4 +-
.../src/i18n/comps/locales/en.ts | 6 +++
3 files changed, 41 insertions(+), 6 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
index 703ebcead..a021639e3 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartConfigs/lineChartConfig.tsx
@@ -3,15 +3,11 @@ import {
MultiCompBuilder,
BoolControl,
dropdownControl,
- list,
- Option,
- valueComp,
- genRandomKey,
jsonControl,
toArray,
showLabelPropertyView,
withContext,
- RedButton,
+ ColorControl,
StringControl,
NumberControl,
withDefault,
@@ -71,6 +67,21 @@ export const SymbolOptions = [
},
] as const;
+export const BorderTypeOptions = [
+ {
+ label: trans("lineChart.solid"),
+ value: "solid",
+ },
+ {
+ label: trans("lineChart.dashed"),
+ value: "dashed",
+ },
+ {
+ label: trans("lineChart.dotted"),
+ value: "dotted",
+ },
+] as const;
+
export const LineChartConfig = (function () {
return new MultiCompBuilder(
{
@@ -90,6 +101,10 @@ export const LineChartConfig = (function () {
polarEndAngle: withDefault(NumberControl, -180),
polarIsTangent: withDefault(BoolControl, false),
labelData: jsonControl(toArray, []),
+ //series-line.itemStyle
+ borderColor: ColorControl,
+ borderWidth: NumberControl,
+ borderType: dropdownControl(BorderTypeOptions, 'solid'),
},
(props): LineSeriesOption => {
const config: LineSeriesOption = {
@@ -116,6 +131,9 @@ export const LineChartConfig = (function () {
}
return color;
},
+ borderColor: props.borderColor,
+ borderWidth: props.borderWidth,
+ borderType: props.borderType,
},
polarData: {
polar: props.polar,
@@ -194,6 +212,15 @@ export const LineChartConfig = (function () {
label: trans("lineChart.symbolSize"),
})}
{children.itemColor.getPropertyView()}
+ {children.borderColor.propertyView({
+ label: trans("lineChart.borderColor"),
+ })}
+ {children.borderWidth.propertyView({
+ label: trans("lineChart.borderWidth"),
+ })}
+ {children.borderType.propertyView({
+ label: trans("lineChart.borderType"),
+ })}
>
))
.build();
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index b3c77f7d9..8e585ac01 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -157,6 +157,7 @@ export function getEchartsConfig(
bottom: `${props?.bottom}%`,
top: `${props?.top}%`,
};
+
let config: any = {
title: {
text: props.title,
@@ -267,7 +268,7 @@ export function getEchartsConfig(
},
itemStyle: {
...series.itemStyle,
- ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ // ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
},
lineStyle: {
...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
@@ -275,6 +276,7 @@ export function getEchartsConfig(
data: transformedData.map((i: any) => i[series.name])
})),
};
+ console.log("config11", getSeriesConfig(props));
if (axisChart) {
// pure chart's size except the margin around
let chartRealSize;
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 1d8fd2319..7a1e6dc76 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -338,6 +338,12 @@ export const en = {
end: "End",
step: "Step",
polar: "Polar Chart",
+ solid: "Solid",
+ dashed: "Dashed",
+ dotted: "Dotted",
+ borderColor: "Border Color",
+ borderWidth: "Border Width",
+ borderType: "Border Type",
},
barChart: {
title: 'Title',
From be9ed2f66fda1af46bd9622af56a73a56b812bb7 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Sun, 16 Feb 2025 15:25:00 -0500
Subject: [PATCH 032/124] 3 Initial Pie Chart
---
client/packages/lowcoder-comps/package.json | 8 +
.../src/comps/lineChartComp/lineChartUtils.ts | 2 -
.../src/comps/pieChartComp/pieChartComp.tsx | 293 +++++++++++++
.../comps/pieChartComp/pieChartConstants.tsx | 312 ++++++++++++++
.../pieChartComp/pieChartPropertyView.tsx | 187 +++++++++
.../src/comps/pieChartComp/pieChartUtils.ts | 397 ++++++++++++++++++
.../src/comps/pieChartComp/seriesComp.tsx | 280 ++++++++++++
client/packages/lowcoder-comps/src/index.ts | 2 +
8 files changed, 1479 insertions(+), 2 deletions(-)
create mode 100644 client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
create mode 100644 client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
create mode 100644 client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json
index d96a31913..3950f1468 100644
--- a/client/packages/lowcoder-comps/package.json
+++ b/client/packages/lowcoder-comps/package.json
@@ -74,6 +74,14 @@
"h": 40
}
},
+ "pieChart": {
+ "name": "Pie Chart",
+ "icon": "./icons/icon-chart.svg",
+ "layoutInfo": {
+ "w": 12,
+ "h": 40
+ }
+ },
"imageEditor": {
"name": "Image Editor",
"icon": "./icons/icon-chart.svg",
diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
index 8e585ac01..64d640d75 100644
--- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartUtils.ts
@@ -276,7 +276,6 @@ export function getEchartsConfig(
data: transformedData.map((i: any) => i[series.name])
})),
};
- console.log("config11", getSeriesConfig(props));
if (axisChart) {
// pure chart's size except the margin around
let chartRealSize;
@@ -336,7 +335,6 @@ export function getEchartsConfig(
}
}
}
- console.log("config", config);
// log.log("Echarts transformedData and config", transformedData, config);
return config;
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx
new file mode 100644
index 000000000..66b73ad30
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx
@@ -0,0 +1,293 @@
+import {
+ changeChildAction,
+ changeValueAction,
+ CompAction,
+ CompActionTypes,
+ wrapChildAction,
+} from "lowcoder-core";
+import { AxisFormatterComp, EchartsAxisType } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { pieChartChildrenMap, ChartSize, getDataKeys } from "./pieChartConstants";
+import { pieChartPropertyView } from "./pieChartPropertyView";
+import _ from "lodash";
+import { useContext, useEffect, useMemo, useRef, useState } from "react";
+import ReactResizeDetector from "react-resize-detector";
+import ReactECharts from "../basicChartComp/reactEcharts";
+import {
+ childrenToProps,
+ depsConfig,
+ genRandomKey,
+ NameConfig,
+ UICompBuilder,
+ withDefault,
+ withExposingConfigs,
+ withViewFn,
+ ThemeContext,
+ chartColorPalette,
+ getPromiseAfterDispatch,
+ dropdownControl,
+} from "lowcoder-sdk";
+import { getEchartsLocale, trans } from "i18n/comps";
+import {
+ echartsConfigOmitChildren,
+ getEchartsConfig,
+ getSelectedPoints,
+} from "./pieChartUtils";
+import 'echarts-extension-gmap';
+import log from "loglevel";
+
+let clickEventCallback = () => {};
+
+const chartModeOptions = [
+ {
+ label: "UI",
+ value: "ui",
+ }
+] as const;
+
+let PieChartTmpComp = (function () {
+ return new UICompBuilder({mode:dropdownControl(chartModeOptions,'ui'),...pieChartChildrenMap}, () => null)
+ .setPropertyViewFn(pieChartPropertyView)
+ .build();
+})();
+
+PieChartTmpComp = withViewFn(PieChartTmpComp, (comp) => {
+ const mode = comp.children.mode.getView();
+ const onUIEvent = comp.children.onUIEvent.getView();
+ const onEvent = comp.children.onEvent.getView();
+ const echartsCompRef = useRef();
+ const [chartSize, setChartSize] = useState();
+ const firstResize = useRef(true);
+ const theme = useContext(ThemeContext);
+ const defaultChartTheme = {
+ color: chartColorPalette,
+ backgroundColor: "#fff",
+ };
+
+ let themeConfig = defaultChartTheme;
+ try {
+ themeConfig = theme?.theme.chart ? JSON.parse(theme?.theme.chart) : defaultChartTheme;
+ } catch (error) {
+ log.error('theme chart error: ', error);
+ }
+
+ const triggerClickEvent = async (dispatch: any, action: CompAction) => {
+ await getPromiseAfterDispatch(
+ dispatch,
+ action,
+ { autoHandleAfterReduce: true }
+ );
+ onEvent('click');
+ }
+
+ useEffect(() => {
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("click", (param: any) => {
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: 'click',
+ data: param.data,
+ }
+ }));
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", param.data, false)
+ );
+ });
+ return () => {
+ echartsCompInstance?.off("click");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, []);
+
+ useEffect(() => {
+ // bind events
+ const echartsCompInstance = echartsCompRef?.current?.getEchartsInstance();
+ if (!echartsCompInstance) {
+ return _.noop;
+ }
+ echartsCompInstance?.on("selectchanged", (param: any) => {
+ const option: any = echartsCompInstance?.getOption();
+ document.dispatchEvent(new CustomEvent("clickEvent", {
+ bubbles: true,
+ detail: {
+ action: param.fromAction,
+ data: getSelectedPoints(param, option)
+ }
+ }));
+
+ if (param.fromAction === "select") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("select");
+ } else if (param.fromAction === "unselect") {
+ comp.dispatch(changeChildAction("selectedPoints", getSelectedPoints(param, option), false));
+ onUIEvent("unselect");
+ }
+
+ triggerClickEvent(
+ comp.dispatch,
+ changeChildAction("lastInteractionData", getSelectedPoints(param, option), false)
+ );
+ });
+ // unbind
+ return () => {
+ echartsCompInstance?.off("selectchanged");
+ document.removeEventListener('clickEvent', clickEventCallback)
+ };
+ }, [onUIEvent]);
+
+ const echartsConfigChildren = _.omit(comp.children, echartsConfigOmitChildren);
+ const childrenProps = childrenToProps(echartsConfigChildren);
+ const option = useMemo(() => {
+ return getEchartsConfig(
+ childrenProps as ToViewReturn,
+ chartSize,
+ themeConfig
+ );
+ }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]);
+
+ return (
+ {
+ if (w && h) {
+ setChartSize({ w: w, h: h });
+ }
+ if (!firstResize.current) {
+ // ignore the first resize, which will impact the loading animation
+ echartsCompRef.current?.getEchartsInstance().resize();
+ } else {
+ firstResize.current = false;
+ }
+ }}
+ >
+ (echartsCompRef.current = e)}
+ style={{ height: "100%" }}
+ notMerge
+ lazyUpdate
+ opts={{ locale: getEchartsLocale() }}
+ option={option}
+ mode={mode}
+ />
+
+ );
+});
+
+function getYAxisFormatContextValue(
+ data: Array,
+ yAxisType: EchartsAxisType,
+ yAxisName?: string
+) {
+ const dataSample = yAxisName && data.length > 0 && data[0][yAxisName];
+ let contextValue = dataSample;
+ if (yAxisType === "time") {
+ // to timestamp
+ const time =
+ typeof dataSample === "number" || typeof dataSample === "string"
+ ? new Date(dataSample).getTime()
+ : null;
+ if (time) contextValue = time;
+ }
+ return contextValue;
+}
+
+PieChartTmpComp = class extends PieChartTmpComp {
+ private lastYAxisFormatContextVal?: JSONValue;
+ private lastColorContext?: JSONObject;
+
+ updateContext(comp: this) {
+ // the context value of axis format
+ let resultComp = comp;
+ const data = comp.children.data.getView();
+ const sampleSeries = comp.children.series.getView().find((s) => !s.getView().hide);
+ const yAxisContextValue = getYAxisFormatContextValue(
+ data,
+ comp.children.yConfig.children.yAxisType.getView(),
+ sampleSeries?.children.columnName.getView()
+ );
+ if (yAxisContextValue !== comp.lastYAxisFormatContextVal) {
+ comp.lastYAxisFormatContextVal = yAxisContextValue;
+ resultComp = comp.setChild(
+ "yConfig",
+ comp.children.yConfig.reduce(
+ wrapChildAction(
+ "formatter",
+ AxisFormatterComp.changeContextDataAction({ value: yAxisContextValue })
+ )
+ )
+ );
+ }
+ return resultComp;
+ }
+
+ override reduce(action: CompAction): this {
+ const comp = super.reduce(action);
+ if (action.type === CompActionTypes.UPDATE_NODES_V2) {
+ const newData = comp.children.data.getView();
+ // data changes
+ if (comp.children.data !== this.children.data) {
+ setTimeout(() => {
+ // update x-axis value
+ const keys = getDataKeys(newData);
+ if (keys.length > 0 && !keys.includes(comp.children.xAxisKey.getView())) {
+ comp.children.xAxisKey.dispatch(changeValueAction(keys[0] || ""));
+ }
+ // pass to child series comp
+ comp.children.series.dispatchDataChanged(newData);
+ }, 0);
+ }
+ return this.updateContext(comp);
+ }
+ return comp;
+ }
+
+ override autoHeight(): boolean {
+ return false;
+ }
+};
+
+let PieChartComp = withExposingConfigs(PieChartTmpComp, [
+ depsConfig({
+ name: "selectedPoints",
+ desc: trans("chart.selectedPointsDesc"),
+ depKeys: ["selectedPoints"],
+ func: (input) => {
+ return input.selectedPoints;
+ },
+ }),
+ depsConfig({
+ name: "lastInteractionData",
+ desc: trans("chart.lastInteractionDataDesc"),
+ depKeys: ["lastInteractionData"],
+ func: (input) => {
+ return input.lastInteractionData;
+ },
+ }),
+ depsConfig({
+ name: "data",
+ desc: trans("chart.dataDesc"),
+ depKeys: ["data", "mode"],
+ func: (input) =>[] ,
+ }),
+ new NameConfig("title", trans("chart.titleDesc")),
+]);
+
+
+export const PieChartCompWithDefault = withDefault(PieChartComp, {
+ xAxisKey: "date",
+ series: [
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.spending"),
+ columnName: "spending",
+ },
+ {
+ dataIndex: genRandomKey(),
+ seriesName: trans("chart.budget"),
+ columnName: "budget",
+ },
+ ],
+});
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
new file mode 100644
index 000000000..c8eb6b778
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
@@ -0,0 +1,312 @@
+import {
+ jsonControl,
+ stateComp,
+ toJSONObjectArray,
+ toObject,
+ BoolControl,
+ ColorControl,
+ withDefault,
+ StringControl,
+ NumberControl,
+ dropdownControl,
+ list,
+ eventHandlerControl,
+ valueComp,
+ withType,
+ uiChildren,
+ clickEvent,
+ toArray,
+ styleControl,
+ EchartDefaultTextStyle,
+ EchartDefaultChartStyle,
+ MultiCompBuilder,
+} from "lowcoder-sdk";
+import { RecordConstructorToComp, RecordConstructorToView } from "lowcoder-core";
+import { BarChartConfig } from "../basicChartComp/chartConfigs/barChartConfig";
+import { XAxisConfig, YAxisConfig } from "../basicChartComp/chartConfigs/cartesianAxisConfig";
+import { LegendConfig } from "../basicChartComp/chartConfigs/legendConfig";
+import { EchartsLegendConfig } from "../basicChartComp/chartConfigs/echartsLegendConfig";
+import { EchartsLabelConfig } from "../basicChartComp/chartConfigs/echartsLabelConfig";
+import { PieChartConfig } from "../basicChartComp/chartConfigs/pieChartConfig";
+import { ScatterChartConfig } from "../basicChartComp/chartConfigs/scatterChartConfig";
+import { SeriesListComp } from "./seriesComp";
+import { EChartsOption } from "echarts";
+import { i18nObjs, trans } from "i18n/comps";
+import { GaugeChartConfig } from "../basicChartComp/chartConfigs/gaugeChartConfig";
+import { FunnelChartConfig } from "../basicChartComp/chartConfigs/funnelChartConfig";
+import {EchartsTitleVerticalConfig} from "../chartComp/chartConfigs/echartsTitleVerticalConfig";
+import {EchartsTitleConfig} from "../basicChartComp/chartConfigs/echartsTitleConfig";
+
+export const ChartTypeOptions = [
+ {
+ label: trans("chart.bar"),
+ value: "bar",
+ },
+ {
+ label: trans("chart.line"),
+ value: "line",
+ },
+ {
+ label: trans("chart.scatter"),
+ value: "scatter",
+ },
+ {
+ label: trans("chart.pie"),
+ value: "pie",
+ },
+] as const;
+
+export const UIEventOptions = [
+ {
+ label: trans("chart.select"),
+ value: "select",
+ description: trans("chart.selectDesc"),
+ },
+ {
+ label: trans("chart.unSelect"),
+ value: "unselect",
+ description: trans("chart.unselectDesc"),
+ },
+] as const;
+
+export const XAxisDirectionOptions = [
+ {
+ label: trans("chart.horizontal"),
+ value: "horizontal",
+ },
+ {
+ label: trans("chart.vertical"),
+ value: "vertical",
+ },
+] as const;
+
+export type XAxisDirectionType = ValueFromOption;
+
+export const noDataAxisConfig = {
+ animation: false,
+ xAxis: {
+ type: "category",
+ name: trans("chart.noData"),
+ nameLocation: "middle",
+ data: [],
+ axisLine: {
+ lineStyle: {
+ color: "#8B8FA3",
+ },
+ },
+ },
+ yAxis: {
+ type: "value",
+ axisLabel: {
+ color: "#8B8FA3",
+ },
+ splitLine: {
+ lineStyle: {
+ color: "#F0F0F0",
+ },
+ },
+ },
+ tooltip: {
+ show: false,
+ },
+ series: [
+ {
+ data: [700],
+ type: "line",
+ itemStyle: {
+ opacity: 0,
+ },
+ },
+ ],
+} as EChartsOption;
+
+export const noDataPieChartConfig = {
+ animation: false,
+ tooltip: {
+ show: false,
+ },
+ legend: {
+ formatter: trans("chart.unknown"),
+ top: "bottom",
+ selectedMode: false,
+ },
+ color: ["#B8BBCC", "#CED0D9", "#DCDEE6", "#E6E6EB"],
+ series: [
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["25%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ {
+ type: "pie",
+ radius: "35%",
+ center: ["75%", "50%"],
+ silent: true,
+ label: {
+ show: false,
+ },
+ data: [
+ {
+ name: "1",
+ value: 70,
+ },
+ {
+ name: "2",
+ value: 68,
+ },
+ {
+ name: "3",
+ value: 48,
+ },
+ {
+ name: "4",
+ value: 40,
+ },
+ ],
+ },
+ ],
+} as EChartsOption;
+
+const areaPiecesChildrenMap = {
+ color: ColorControl,
+ from: StringControl,
+ to: StringControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+const AreaPiecesTmpComp = new MultiCompBuilder(areaPiecesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) =>
+ (<>
+ {children.color.propertyView({label: trans("pieChart.color")})}
+ {children.from.propertyView({label: trans("pieChart.from")})}
+ {children.to.propertyView({label: trans("pieChart.to")})}
+ >)
+ )
+ .build();
+
+export type ChartSize = { w: number; h: number };
+
+export const getDataKeys = (data: Array) => {
+ if (!data) {
+ return [];
+ }
+ const dataKeys: Array = [];
+ data.slice(0, 50).forEach((d) => {
+ Object.keys(d).forEach((key) => {
+ if (!dataKeys.includes(key)) {
+ dataKeys.push(key);
+ }
+ });
+ });
+ return dataKeys;
+};
+
+const ChartOptionMap = {
+ pie: PieChartConfig,
+ scatter: ScatterChartConfig,
+};
+
+const EchartsOptionMap = {
+ funnel: FunnelChartConfig,
+ gauge: GaugeChartConfig,
+};
+
+const ChartOptionComp = withType(ChartOptionMap, "pie");
+const EchartsOptionComp = withType(EchartsOptionMap, "funnel");
+export type CharOptionCompType = keyof typeof ChartOptionMap;
+
+export const chartUiModeChildren = {
+ title: withDefault(StringControl, trans("echarts.defaultTitle")),
+ data: jsonControl(toJSONObjectArray, i18nObjs.defaultDataSource),
+ xAxisKey: valueComp(""), // x-axis, key from data
+ xAxisDirection: dropdownControl(XAxisDirectionOptions, "horizontal"),
+ xAxisData: jsonControl(toArray, []),
+ series: SeriesListComp,
+ xConfig: XAxisConfig,
+ yConfig: YAxisConfig,
+ legendConfig: LegendConfig,
+ chartConfig: ChartOptionComp,
+ areaPieces: list(AreaPiecesTmpComp),
+ animationDuration: withDefault(NumberControl, 1000),
+ onUIEvent: eventHandlerControl(UIEventOptions),
+};
+
+let chartJsonModeChildren: any = {
+ echartsOption: jsonControl(toObject, i18nObjs.defaultEchartsJsonOption),
+ echartsTitle: withDefault(StringControl, trans("echarts.defaultTitle")),
+ echartsLegendConfig: EchartsLegendConfig,
+ echartsLabelConfig: EchartsLabelConfig,
+ echartsConfig: EchartsOptionComp,
+ echartsTitleVerticalConfig: EchartsTitleVerticalConfig,
+ echartsTitleConfig:EchartsTitleConfig,
+
+ left:withDefault(NumberControl,trans('chart.defaultLeft')),
+ right:withDefault(NumberControl,trans('chart.defaultRight')),
+ top:withDefault(NumberControl,trans('chart.defaultTop')),
+ bottom:withDefault(NumberControl,trans('chart.defaultBottom')),
+
+ tooltip: withDefault(BoolControl, true),
+ legendVisibility: withDefault(BoolControl, true),
+}
+
+if (EchartDefaultChartStyle && EchartDefaultTextStyle) {
+ chartJsonModeChildren = {
+ ...chartJsonModeChildren,
+ chartStyle: styleControl(EchartDefaultChartStyle, 'chartStyle'),
+ titleStyle: styleControl(EchartDefaultTextStyle, 'titleStyle'),
+ xAxisStyle: styleControl(EchartDefaultTextStyle, 'xAxis'),
+ yAxisStyle: styleControl(EchartDefaultTextStyle, 'yAxisStyle'),
+ legendStyle: styleControl(EchartDefaultTextStyle, 'legendStyle'),
+ }
+}
+
+export type UIChartDataType = {
+ seriesName: string;
+ // coordinate chart
+ x?: any;
+ y?: any;
+ // pie or funnel
+ itemName?: any;
+ value?: any;
+};
+
+export type NonUIChartDataType = {
+ name: string;
+ value: any;
+}
+
+export const pieChartChildrenMap = {
+ selectedPoints: stateComp>([]),
+ lastInteractionData: stateComp | NonUIChartDataType>({}),
+ onEvent: eventHandlerControl([clickEvent] as const),
+ ...chartUiModeChildren,
+ ...chartJsonModeChildren,
+};
+
+const chartUiChildrenMap = uiChildren(pieChartChildrenMap);
+export type ChartCompPropsType = RecordConstructorToView;
+export type ChartCompChildrenType = RecordConstructorToComp;
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
new file mode 100644
index 000000000..95b39cb60
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
@@ -0,0 +1,187 @@
+import { changeChildAction, CompAction } from "lowcoder-core";
+import { ChartCompChildrenType, ChartTypeOptions,getDataKeys } from "./pieChartConstants";
+import { newSeries } from "./seriesComp";
+import {
+ CustomModal,
+ Dropdown,
+ hiddenPropertyView,
+ Option,
+ RedButton,
+ Section,
+ sectionNames,
+ controlItem,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+export function pieChartPropertyView(
+ children: ChartCompChildrenType,
+ dispatch: (action: CompAction) => void
+) {
+ const series = children.series.getView();
+ const columnOptions = getDataKeys(children.data.getView()).map((key) => ({
+ label: key,
+ value: key,
+ }));
+
+ const uiModePropertyView = (
+ <>
+
+ {children.chartConfig.getPropertyView()}
+ {children.animationDuration.propertyView({label: trans("pieChart.animationDuration")})}
+ {
+ dispatch(changeChildAction("xAxisKey", value));
+ }}
+ />
+ {children.chartConfig.getView().subtype === "waterfall" && children.xAxisData.propertyView({
+ label: "X-Label-Data"
+ })}
+ s.getView().seriesName}
+ popoverTitle={(s) => s.getView().columnName}
+ content={(s, index) => (
+ <>
+ {s.getPropertyViewWithData(columnOptions)}
+ {
+ {
+ CustomModal.confirm({
+ title: trans("chart.delete"),
+ content: trans("chart.confirmDelete") + `${s.getView().seriesName}?`,
+ onConfirm: () =>
+ children.series.dispatch(children.series.deleteAction(index)),
+ confirmBtnType: "delete",
+ okText: trans("chart.delete"),
+ });
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ if (columnOptions.length <= 0) {
+ return;
+ }
+ children.series.dispatch(
+ children.series.pushAction(
+ newSeries(trans("chart.customSeries"), columnOptions[0].value)
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = children.series.arrayMoveAction(fromIndex, toIndex);
+ children.series.dispatch(action);
+ }}
+ hide={(s) => s.getView().hide}
+ onHide={(s, hide) => s.children.hide.dispatchChangeValueAction(hide)}
+ dataIndex={(s) => s.getView().dataIndex}
+ />
+ `[${s.getView().from}-${s.getView().to}] ${s.getView().color}`}
+ popoverTitle={(s) => trans("pieChart.areaPiece")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ children.areaPieces.dispatch(children.areaPieces.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ children.areaPieces.dispatch(
+ children.areaPieces.pushAction(
+ {}
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = children.areaPieces.arrayMoveAction(fromIndex, toIndex);
+ children.areaPieces.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
+ />
+
+
+
+ {children.onUIEvent.propertyView({title: trans("chart.chartEventHandlers")})}
+
+
+ {children.onEvent.propertyView()}
+
+
+
+ {children.echartsTitleConfig.getPropertyView()}
+ {children.echartsTitleVerticalConfig.getPropertyView()}
+ {children.legendConfig.getPropertyView()}
+ {children.title.propertyView({ label: trans("chart.title") })}
+ {children.left.propertyView({ label: trans("chart.left"), tooltip: trans("echarts.leftTooltip") })}
+ {children.right.propertyView({ label: trans("chart.right"), tooltip: trans("echarts.rightTooltip") })}
+ {children.top.propertyView({ label: trans("chart.top"), tooltip: trans("echarts.topTooltip") })}
+ {children.bottom.propertyView({ label: trans("chart.bottom"), tooltip: trans("echarts.bottomTooltip") })}
+ {children.chartConfig.children.compType.getView() !== "pie" && (
+ <>
+ {children.xAxisDirection.propertyView({
+ label: trans("chart.xAxisDirection"),
+ radioButton: true,
+ })}
+ {children.xConfig.getPropertyView()}
+ {children.yConfig.getPropertyView()}
+ >
+ )}
+ {hiddenPropertyView(children)}
+ {children.tooltip.propertyView({label: trans("echarts.tooltip"), tooltip: trans("echarts.tooltipTooltip")})}
+
+
+ {children.chartStyle?.getPropertyView()}
+
+
+ {children.titleStyle?.getPropertyView()}
+
+
+ {children.xAxisStyle?.getPropertyView()}
+
+
+ {children.yAxisStyle?.getPropertyView()}
+
+
+ {children.legendStyle?.getPropertyView()}
+
+
+ {children.data.propertyView({
+ label: trans("chart.data"),
+ })}
+
+ >
+ );
+
+ const getChatConfigByMode = (mode: string) => {
+ switch(mode) {
+ case "ui":
+ return uiModePropertyView;
+ }
+ }
+ return (
+ <>
+ {getChatConfigByMode(children.mode.getView())}
+ >
+ );
+}
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
new file mode 100644
index 000000000..8b72bca0d
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -0,0 +1,397 @@
+import {
+ CharOptionCompType,
+ ChartCompPropsType,
+ ChartSize,
+ noDataAxisConfig,
+ noDataPieChartConfig,
+} from "comps/pieChartComp/pieChartConstants";
+import { getPieRadiusAndCenter } from "comps/basicChartComp/chartConfigs/pieChartConfig";
+import { EChartsOptionWithMap } from "../basicChartComp/reactEcharts/types";
+import _ from "lodash";
+import { chartColorPalette, isNumeric, JSONObject, loadScript } from "lowcoder-sdk";
+import { calcXYConfig } from "comps/basicChartComp/chartConfigs/cartesianAxisConfig";
+import Big from "big.js";
+import { googleMapsApiUrl } from "../basicChartComp/chartConfigs/chartUrls";
+import opacityToHex from "../../util/opacityToHex";
+import parseBackground from "../../util/gradientBackgroundColor";
+import {ba, s} from "@fullcalendar/core/internal-common";
+import {chartStyleWrapper, styleWrapper} from "../../util/styleWrapper";
+
+export function transformData(
+ originData: JSONObject[],
+ xAxis: string,
+ seriesColumnNames: string[]
+) {
+ // aggregate data by x-axis
+ const transformedData: JSONObject[] = [];
+ originData.reduce((prev, cur) => {
+ if (cur === null || cur === undefined) {
+ return prev;
+ }
+ const groupValue = cur[xAxis] as string;
+ if (!prev[groupValue]) {
+ // init as 0
+ const initValue: any = {};
+ seriesColumnNames.forEach((name) => {
+ initValue[name] = 0;
+ });
+ prev[groupValue] = initValue;
+ transformedData.push(prev[groupValue]);
+ }
+ // remain the x-axis data
+ prev[groupValue][xAxis] = groupValue;
+ seriesColumnNames.forEach((key) => {
+ if (key === xAxis) {
+ return;
+ } else if (isNumeric(cur[key])) {
+ const bigNum = Big(cur[key]);
+ prev[groupValue][key] = bigNum.add(prev[groupValue][key]).toNumber();
+ } else {
+ prev[groupValue][key] += 1;
+ }
+ });
+ return prev;
+ }, {} as any);
+ return transformedData;
+}
+
+const notAxisChartSet: Set = new Set(["pie"] as const);
+const notAxisChartSubtypeSet: Set = new Set(["polar"] as const);
+export const echartsConfigOmitChildren = [
+ "hidden",
+ "selectedPoints",
+ "onUIEvent",
+ "mapInstance"
+] as const;
+type EchartsConfigProps = Omit;
+
+
+export function isAxisChart(type: CharOptionCompType, polar: boolean) {
+ return !notAxisChartSet.has(type) && !polar;
+}
+
+export function getSeriesConfig(props: EchartsConfigProps) {
+ let visibleSeries = props.series.filter((s) => !s.getView().hide);
+ if(props.chartConfig.subtype === "waterfall") {
+ const seriesOn = visibleSeries[0];
+ const seriesPlaceholder = visibleSeries[0];
+ visibleSeries = [seriesPlaceholder, seriesOn];
+ }
+ const seriesLength = visibleSeries.length;
+ return visibleSeries.map((s, index) => {
+ if (isAxisChart(props.chartConfig.type, props.chartConfig.polarData?.polar)) {
+ let encodeX: string, encodeY: string;
+ const horizontalX = props.xAxisDirection === "horizontal";
+ let itemStyle = props.chartConfig.itemStyle;
+
+ if (horizontalX) {
+ encodeX = props.xAxisKey;
+ encodeY = s.getView().columnName;
+ } else {
+ encodeX = s.getView().columnName;
+ encodeY = props.xAxisKey;
+ }
+ const markLineData = s.getView().markLines.map(line => ({type: line.getView().type}));
+ const markAreaData = s.getView().markAreas.map(area => ([{name: area.getView().name, [horizontalX?"xAxis":"yAxis"]: area.getView().from, label: {
+ position: horizontalX?"top":"right",
+ }}, {[horizontalX?"xAxis":"yAxis"]: area.getView().to}]));
+ return {
+ name: s.getView().seriesName,
+ selectedMode: "single",
+ select: {
+ itemStyle: {
+ borderColor: "#000",
+ },
+ },
+ step: s.getView().step,
+ encode: {
+ x: encodeX,
+ y: encodeY,
+ },
+ markLine: {
+ data: markLineData,
+ },
+ markArea: {
+ itemStyle: {
+ color: 'rgba(255, 173, 177, 0.4)',
+ },
+ data: markAreaData,
+ },
+ // each type of chart's config
+ ...props.chartConfig,
+ itemStyle: itemStyle,
+ label: {
+ ...props.chartConfig.label,
+ ...(!horizontalX && { position: "outside" }),
+ },
+ };
+ } else {
+ // pie
+ const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
+ return {
+ ...props.chartConfig,
+ radius: radiusAndCenter.radius,
+ center: radiusAndCenter.center,
+ name: s.getView().seriesName,
+ selectedMode: "single",
+ encode: {
+ itemName: props.xAxisKey,
+ value: s.getView().columnName,
+ },
+ };
+ }
+ });
+}
+
+// https://echarts.apache.org/en/option.html
+export function getEchartsConfig(
+ props: EchartsConfigProps,
+ chartSize?: ChartSize,
+ theme?: any,
+): EChartsOptionWithMap {
+ // axisChart
+ const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.polarData?.polar);
+ const gridPos = {
+ left: `${props?.left}%`,
+ right: `${props?.right}%`,
+ bottom: `${props?.bottom}%`,
+ top: `${props?.top}%`,
+ };
+
+ let config: any = {
+ title: {
+ text: props.title,
+ top: props.echartsTitleVerticalConfig.top,
+ left:props.echartsTitleConfig.top,
+ textStyle: {
+ ...styleWrapper(props?.titleStyle, theme?.titleStyle)
+ }
+ },
+ backgroundColor: parseBackground( props?.chartStyle?.background || theme?.chartStyle?.backgroundColor || "#FFFFFF"),
+ legend: {
+ ...props.legendConfig,
+ textStyle: {
+ ...styleWrapper(props?.legendStyle, theme?.legendStyle, 15)
+ }
+ },
+ tooltip: props.tooltip && {
+ trigger: "axis",
+ axisPointer: {
+ type: "line",
+ lineStyle: {
+ color: "rgba(0,0,0,0.2)",
+ width: 2,
+ type: "solid"
+ }
+ }
+ },
+ grid: {
+ ...gridPos,
+ containLabel: true,
+ },
+ animationDuration: props.animationDuration,
+ };
+ if (props.areaPieces.length > 0) {
+ config.visualMap = {
+ type: 'piecewise',
+ show: false,
+ dimension: 0,
+ seriesIndex: 0,
+ pieces: props.areaPieces?.filter(p => p.getView().from && p.getView().to && p.getView().color)?.map(p => (
+ {
+ ...(p.getView().from?{min: parseInt(p.getView().from)}:{}),
+ ...(p.getView().to?{max: parseInt(p.getView().to)}:{}),
+ ...(p.getView().color?{color: p.getView().color}:{}),
+ }
+ ))
+ }
+ }
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ // Disable init animation.
+ animationDuration: 0,
+ animationDurationUpdate: 2000,
+ animationEasing: 'linear',
+ animationEasingUpdate: 'linear',
+ }
+ }
+ if (props.data.length <= 0) {
+ // no data
+ return {
+ ...config,
+ ...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
+ };
+ }
+ const yAxisConfig = props.yConfig();
+ const seriesColumnNames = props.series
+ .filter((s) => !s.getView().hide)
+ .map((s) => s.getView().columnName);
+ // y-axis is category and time, data doesn't need to aggregate
+ let transformedData =
+ yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.data : transformData(props.data, props.xAxisKey, seriesColumnNames);
+
+ if(props.chartConfig.polarData?.polar) {
+ config = {
+ ...config,
+ polar: {
+ radius: [props.chartConfig.polarData.polarRadiusStart, props.chartConfig.polarData.polarRadiusEnd],
+ },
+ radiusAxis: {
+ type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
+ data: props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
+ max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
+ },
+ angleAxis: {
+ type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
+ data: !props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
+ max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
+ startAngle: props.chartConfig.polarData.polarStartAngle,
+ endAngle: props.chartConfig.polarData.polarEndAngle,
+ },
+ }
+ }
+
+ config = {
+ ...config,
+ dataset: [
+ {
+ source: transformedData,
+ sourceHeader: false,
+ },
+ ],
+ series: getSeriesConfig(props).map(series => ({
+ ...series,
+ encode: {
+ ...series.encode,
+ y: series.name,
+ },
+ itemStyle: {
+ ...series.itemStyle,
+ // ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ },
+ lineStyle: {
+ ...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
+ },
+ data: transformedData.map((i: any) => i[series.name])
+ })),
+ };
+ if (axisChart) {
+ // pure chart's size except the margin around
+ let chartRealSize;
+ if (chartSize) {
+ const rightSize =
+ typeof gridPos.right === "number"
+ ? gridPos.right
+ : (chartSize.w * parseFloat(gridPos.right)) / 100.0;
+ chartRealSize = {
+ // actually it's self-adaptive with the x-axis label on the left, not that accurate but work
+ w: chartSize.w - gridPos.left - rightSize,
+ // also self-adaptive on the bottom
+ h: chartSize.h - gridPos.top - gridPos.bottom,
+ right: rightSize,
+ };
+ }
+ const finalXyConfig = calcXYConfig(
+ props.xConfig,
+ yAxisConfig,
+ props.xAxisDirection,
+ transformedData.map((d) => d[props.xAxisKey]),
+ chartRealSize
+ );
+ config = {
+ ...config,
+ // @ts-ignore
+ xAxis: {
+ ...finalXyConfig.xConfig,
+ axisLabel: {
+ ...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
+ },
+ data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
+ },
+ // @ts-ignore
+ yAxis: {
+ ...finalXyConfig.yConfig,
+ axisLabel: {
+ ...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
+ },
+ data: finalXyConfig.yConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
+ },
+ };
+
+ if(props.chartConfig.race) {
+ config = {
+ ...config,
+ xAxis: {
+ ...config.xAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ yAxis: {
+ ...config.yAxis,
+ animationDuration: 300,
+ animationDurationUpdate: 300
+ },
+ }
+ }
+ }
+
+ // log.log("Echarts transformedData and config", transformedData, config);
+ return config;
+}
+
+export function getSelectedPoints(param: any, option: any) {
+ const series = option.series;
+ const dataSource = _.isArray(option.dataset) && option.dataset[0]?.source;
+ if (series && dataSource) {
+ return param.selected.flatMap((selectInfo: any) => {
+ const seriesInfo = series[selectInfo.seriesIndex];
+ if (!seriesInfo || !seriesInfo.encode) {
+ return [];
+ }
+ return selectInfo.dataIndex.map((index: any) => {
+ const commonResult = {
+ seriesName: seriesInfo.name,
+ };
+ if (seriesInfo.encode.itemName && seriesInfo.encode.value) {
+ return {
+ ...commonResult,
+ itemName: dataSource[index][seriesInfo.encode.itemName],
+ value: dataSource[index][seriesInfo.encode.value],
+ };
+ } else {
+ return {
+ ...commonResult,
+ x: dataSource[index][seriesInfo.encode.x],
+ y: dataSource[index][seriesInfo.encode.y],
+ };
+ }
+ });
+ });
+ }
+ return [];
+}
+
+export function loadGoogleMapsScript(apiKey: string) {
+ const mapsUrl = `${googleMapsApiUrl}?key=${apiKey}`;
+ const scripts = document.getElementsByTagName('script');
+ // is script already loaded
+ let scriptIndex = _.findIndex(scripts, (script) => script.src.endsWith(mapsUrl));
+ if(scriptIndex > -1) {
+ return scripts[scriptIndex];
+ }
+ // is script loaded with diff api_key, remove the script and load again
+ scriptIndex = _.findIndex(scripts, (script) => script.src.startsWith(googleMapsApiUrl));
+ if(scriptIndex > -1) {
+ scripts[scriptIndex].remove();
+ }
+
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.src = mapsUrl;
+ script.async = true;
+ script.defer = true;
+ window.document.body.appendChild(script);
+
+ return script;
+}
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
new file mode 100644
index 000000000..24bd8e774
--- /dev/null
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -0,0 +1,280 @@
+import {
+ BoolControl,
+ StringControl,
+ list,
+ isNumeric,
+ genRandomKey,
+ Dropdown,
+ Option,
+ RedButton,
+ CustomModal,
+ MultiCompBuilder,
+ valueComp,
+ dropdownControl,
+} from "lowcoder-sdk";
+import { trans } from "i18n/comps";
+
+import { ConstructorToComp, ConstructorToDataType, ConstructorToView } from "lowcoder-core";
+import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcoder-core";
+
+export type SeriesCompType = ConstructorToComp;
+export type RawSeriesCompType = ConstructorToView;
+type SeriesDataType = ConstructorToDataType;
+type MarkLineDataType = ConstructorToDataType;
+
+type ActionDataType = {
+ type: "chartDataChanged";
+ chartData: Array;
+};
+
+export function newSeries(name: string, columnName: string): SeriesDataType {
+ return {
+ seriesName: name,
+ columnName: columnName,
+ dataIndex: genRandomKey(),
+ };
+}
+
+export function newMarkLine(type: string): MarkLineDataType {
+ return {
+ type,
+ dataIndex: genRandomKey(),
+ };
+}
+
+export const MarkLineTypeOptions = [
+ {
+ label: trans("pieChart.max"),
+ value: "max",
+ },
+ {
+ label: trans("pieChart.average"),
+ value: "average",
+ },
+ {
+ label: trans("pieChart.min"),
+ value: "min",
+ },
+] as const;
+
+export const StepOptions = [
+ {
+ label: trans("pieChart.none"),
+ value: "",
+ },
+ {
+ label: trans("pieChart.start"),
+ value: "start",
+ },
+ {
+ label: trans("pieChart.middle"),
+ value: "middle",
+ },
+ {
+ label: trans("pieChart.end"),
+ value: "end",
+ },
+] as const;
+
+const valToLabel = (val) => MarkLineTypeOptions.find(o => o.value === val)?.label || "";
+const markLinesChildrenMap = {
+ type: dropdownControl(MarkLineTypeOptions, "max"),
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+const MarkLinesTmpComp = new MultiCompBuilder(markLinesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) => {
+ return <>{children.type.propertyView({label: trans("pieChart.type")})}>;
+ })
+ .build();
+const markAreasChildrenMap = {
+ name: StringControl,
+ from: StringControl,
+ to: StringControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+};
+const MarkAreasTmpComp = new MultiCompBuilder(markAreasChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn((children: any) =>
+ (<>
+ {children.name.propertyView({label: trans("pieChart.name")})}
+ {children.from.propertyView({label: trans("pieChart.from")})}
+ {children.to.propertyView({label: trans("pieChart.to")})}
+ >)
+ )
+ .build();
+
+
+export function newMarkArea(): MarkLineDataType {
+ return {
+ dataIndex: genRandomKey(),
+ };
+}
+
+const seriesChildrenMap = {
+ columnName: StringControl,
+ seriesName: StringControl,
+ markLines: list(MarkLinesTmpComp),
+ markAreas: list(MarkAreasTmpComp),
+ hide: BoolControl,
+ // unique key, for sort
+ dataIndex: valueComp(""),
+ step: dropdownControl(StepOptions, ""),
+};
+
+const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
+ return props;
+})
+ .setPropertyViewFn(() => {
+ return <>>;
+ })
+ .build();
+
+class SeriesComp extends SeriesTmpComp {
+ getPropertyViewWithData(columnOptions: OptionsType): React.ReactNode {
+ return (
+ <>
+ {this.children.seriesName.propertyView({
+ label: trans("chart.seriesName"),
+ })}
+ {
+ this.children.columnName.dispatchChangeValueAction(value);
+ }}
+ />
+ {this.children.step.propertyView({
+ label: trans("pieChart.step"),
+ })}
+ valToLabel(s.getView().type)}
+ popoverTitle={(s) => trans("pieChart.markLineType")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ this.children.markLines.dispatch(this.children.markLines.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ this.children.markLines.dispatch(
+ this.children.markLines.pushAction(
+ newMarkLine("max")
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = this.children.markLines.arrayMoveAction(fromIndex, toIndex);
+ this.children.markLines.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
+ />
+ s.getView().name}
+ popoverTitle={(s) => trans("pieChart.markLineType")}
+ content={(s, index) => (
+ <>
+ {s.getPropertyView({label: "Type"})}
+ {
+ {
+ this.children.markAreas.dispatch(this.children.markAreas.deleteAction(index));
+ }}
+ >
+ {trans("chart.delete")}
+
+ }
+ >
+ )}
+ onAdd={() => {
+ this.children.markAreas.dispatch(
+ this.children.markAreas.pushAction(
+ newMarkArea()
+ )
+ );
+ }}
+ onMove={(fromIndex, toIndex) => {
+ const action = this.children.markAreas.arrayMoveAction(fromIndex, toIndex);
+ this.children.markAreas.dispatch(action);
+ }}
+ hide={(s) => true}
+ onHide={(s, hide) => console.log("onHide")}
+ dataIndex={(s) => {
+ return s.getView().dataIndex;
+ }}
+ />
+ >
+ );
+ }
+}
+
+const SeriesListTmpComp = list(SeriesComp);
+
+export class SeriesListComp extends SeriesListTmpComp {
+ override reduce(action: CompAction): this {
+ if (isMyCustomAction(action, "chartDataChanged")) {
+ // auto generate series
+ const actions = this.genExampleSeriesActions(action.value.chartData);
+ return this.reduce(this.multiAction(actions));
+ }
+ return super.reduce(action);
+ }
+
+ private genExampleSeriesActions(chartData: Array) {
+ const actions: CustomAction[] = [];
+ if (!chartData || chartData.length <= 0 || !chartData[0]) {
+ return actions;
+ }
+ let delCnt = 0;
+ const existColumns = this.getView().map((s) => s.getView().columnName);
+ // delete series not in data
+ existColumns.forEach((columnName) => {
+ if (chartData[0]?.[columnName] === undefined) {
+ actions.push(this.deleteAction(0));
+ delCnt++;
+ }
+ });
+ if (existColumns.length > delCnt) {
+ // don't generate example if exists
+ return actions;
+ }
+ // generate example series
+ const exampleKeys = Object.keys(chartData[0])
+ .filter((key) => {
+ return !existColumns.includes(key) && isNumeric(chartData[0][key]);
+ })
+ .slice(0, 3);
+ exampleKeys.forEach((key) => actions.push(this.pushAction(newSeries(key, key))));
+ return actions;
+ }
+
+ dispatchDataChanged(chartData: Array): void {
+ this.dispatch(
+ customAction({
+ type: "chartDataChanged",
+ chartData: chartData,
+ })
+ );
+ }
+}
diff --git a/client/packages/lowcoder-comps/src/index.ts b/client/packages/lowcoder-comps/src/index.ts
index 2b8dc8615..03517b1a2 100644
--- a/client/packages/lowcoder-comps/src/index.ts
+++ b/client/packages/lowcoder-comps/src/index.ts
@@ -21,12 +21,14 @@ import { VideoSharingStreamComp } from "./comps/agoraMeetingComp/videoSharingStr
import { BasicChartCompWithDefault } from "comps/basicChartComp/chartComp";
import { BarChartCompWithDefault } from "comps/barChartComp/barChartComp";
import { LineChartCompWithDefault } from "comps/lineChartComp/lineChartComp";
+import { PieChartCompWithDefault } from "comps/pieChartComp/pieChartComp";
export default {
chart: ChartCompWithDefault,
basicChart: BasicChartCompWithDefault,
barChart: BarChartCompWithDefault,
lineChart: LineChartCompWithDefault,
+ pieChart: PieChartCompWithDefault,
chartsGeoMap: ChartsGeoMapComp,
funnelChart: FunnelChartCompWithDefault,
gaugeChart: GaugeChartCompWithDefault,
From 8d2427a3c6415e44b036a3de944e7e329e1e5658 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 17 Feb 2025 02:37:27 -0500
Subject: [PATCH 033/124] show legends
---
.../src/comps/basicChartComp/chartUtils.ts | 2 +-
.../comps/pieChartComp/pieChartConstants.tsx | 1 -
.../pieChartComp/pieChartPropertyView.tsx | 37 ----
.../src/comps/pieChartComp/pieChartUtils.ts | 200 ++----------------
.../src/comps/pieChartComp/seriesComp.tsx | 135 ------------
5 files changed, 17 insertions(+), 358 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartUtils.ts b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartUtils.ts
index 402011e6c..6c5020690 100644
--- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartUtils.ts
@@ -276,7 +276,7 @@ export function getEchartsConfig(
},
};
}
- // log.log("Echarts transformedData and config", transformedData, config);
+ // console.log("Echarts transformedData and config", transformedData, config);
return config;
}
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
index c8eb6b778..62e5be971 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartConstants.tsx
@@ -251,7 +251,6 @@ export const chartUiModeChildren = {
legendConfig: LegendConfig,
chartConfig: ChartOptionComp,
areaPieces: list(AreaPiecesTmpComp),
- animationDuration: withDefault(NumberControl, 1000),
onUIEvent: eventHandlerControl(UIEventOptions),
};
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
index 95b39cb60..7c74958e3 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartPropertyView.tsx
@@ -27,7 +27,6 @@ export function pieChartPropertyView(
<>
{children.chartConfig.getPropertyView()}
- {children.animationDuration.propertyView({label: trans("pieChart.animationDuration")})}
s.children.hide.dispatchChangeValueAction(hide)}
dataIndex={(s) => s.getView().dataIndex}
/>
- `[${s.getView().from}-${s.getView().to}] ${s.getView().color}`}
- popoverTitle={(s) => trans("pieChart.areaPiece")}
- content={(s, index) => (
- <>
- {s.getPropertyView({label: "Type"})}
- {
- {
- children.areaPieces.dispatch(children.areaPieces.deleteAction(index));
- }}
- >
- {trans("chart.delete")}
-
- }
- >
- )}
- onAdd={() => {
- children.areaPieces.dispatch(
- children.areaPieces.pushAction(
- {}
- )
- );
- }}
- onMove={(fromIndex, toIndex) => {
- const action = children.areaPieces.arrayMoveAction(fromIndex, toIndex);
- children.areaPieces.dispatch(action);
- }}
- hide={(s) => true}
- onHide={(s, hide) => console.log("onHide")}
- dataIndex={(s) => {
- return s.getView().dataIndex;
- }}
- />
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
index 8b72bca0d..a97c50c96 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -55,8 +55,6 @@ export function transformData(
return transformedData;
}
-const notAxisChartSet: Set
= new Set(["pie"] as const);
-const notAxisChartSubtypeSet: Set = new Set(["polar"] as const);
export const echartsConfigOmitChildren = [
"hidden",
"selectedPoints",
@@ -65,81 +63,23 @@ export const echartsConfigOmitChildren = [
] as const;
type EchartsConfigProps = Omit;
-
-export function isAxisChart(type: CharOptionCompType, polar: boolean) {
- return !notAxisChartSet.has(type) && !polar;
-}
-
export function getSeriesConfig(props: EchartsConfigProps) {
let visibleSeries = props.series.filter((s) => !s.getView().hide);
- if(props.chartConfig.subtype === "waterfall") {
- const seriesOn = visibleSeries[0];
- const seriesPlaceholder = visibleSeries[0];
- visibleSeries = [seriesPlaceholder, seriesOn];
- }
const seriesLength = visibleSeries.length;
return visibleSeries.map((s, index) => {
- if (isAxisChart(props.chartConfig.type, props.chartConfig.polarData?.polar)) {
- let encodeX: string, encodeY: string;
- const horizontalX = props.xAxisDirection === "horizontal";
- let itemStyle = props.chartConfig.itemStyle;
-
- if (horizontalX) {
- encodeX = props.xAxisKey;
- encodeY = s.getView().columnName;
- } else {
- encodeX = s.getView().columnName;
- encodeY = props.xAxisKey;
- }
- const markLineData = s.getView().markLines.map(line => ({type: line.getView().type}));
- const markAreaData = s.getView().markAreas.map(area => ([{name: area.getView().name, [horizontalX?"xAxis":"yAxis"]: area.getView().from, label: {
- position: horizontalX?"top":"right",
- }}, {[horizontalX?"xAxis":"yAxis"]: area.getView().to}]));
- return {
- name: s.getView().seriesName,
- selectedMode: "single",
- select: {
- itemStyle: {
- borderColor: "#000",
- },
- },
- step: s.getView().step,
- encode: {
- x: encodeX,
- y: encodeY,
- },
- markLine: {
- data: markLineData,
- },
- markArea: {
- itemStyle: {
- color: 'rgba(255, 173, 177, 0.4)',
- },
- data: markAreaData,
- },
- // each type of chart's config
- ...props.chartConfig,
- itemStyle: itemStyle,
- label: {
- ...props.chartConfig.label,
- ...(!horizontalX && { position: "outside" }),
- },
- };
- } else {
- // pie
- const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
- return {
- ...props.chartConfig,
- radius: radiusAndCenter.radius,
- center: radiusAndCenter.center,
- name: s.getView().seriesName,
- selectedMode: "single",
- encode: {
- itemName: props.xAxisKey,
- value: s.getView().columnName,
- },
- };
- }
+ // pie
+ const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
+ return {
+ ...props.chartConfig,
+ radius: radiusAndCenter.radius,
+ center: radiusAndCenter.center,
+ name: s.getView().seriesName,
+ selectedMode: "single",
+ encode: {
+ itemName: props.xAxisKey,
+ value: s.getView().columnName,
+ },
+ };
});
}
@@ -149,8 +89,6 @@ export function getEchartsConfig(
chartSize?: ChartSize,
theme?: any,
): EChartsOptionWithMap {
- // axisChart
- const axisChart = isAxisChart(props.chartConfig.type, props.chartConfig.polarData?.polar);
const gridPos = {
left: `${props?.left}%`,
right: `${props?.right}%`,
@@ -189,38 +127,13 @@ export function getEchartsConfig(
...gridPos,
containLabel: true,
},
- animationDuration: props.animationDuration,
};
- if (props.areaPieces.length > 0) {
- config.visualMap = {
- type: 'piecewise',
- show: false,
- dimension: 0,
- seriesIndex: 0,
- pieces: props.areaPieces?.filter(p => p.getView().from && p.getView().to && p.getView().color)?.map(p => (
- {
- ...(p.getView().from?{min: parseInt(p.getView().from)}:{}),
- ...(p.getView().to?{max: parseInt(p.getView().to)}:{}),
- ...(p.getView().color?{color: p.getView().color}:{}),
- }
- ))
- }
- }
- if(props.chartConfig.race) {
- config = {
- ...config,
- // Disable init animation.
- animationDuration: 0,
- animationDurationUpdate: 2000,
- animationEasing: 'linear',
- animationEasingUpdate: 'linear',
- }
- }
+
if (props.data.length <= 0) {
// no data
return {
...config,
- ...(axisChart ? noDataAxisConfig : noDataPieChartConfig),
+ ...noDataPieChartConfig,
};
}
const yAxisConfig = props.yConfig();
@@ -231,27 +144,6 @@ export function getEchartsConfig(
let transformedData =
yAxisConfig.type === "category" || yAxisConfig.type === "time" ? props.data : transformData(props.data, props.xAxisKey, seriesColumnNames);
- if(props.chartConfig.polarData?.polar) {
- config = {
- ...config,
- polar: {
- radius: [props.chartConfig.polarData.polarRadiusStart, props.chartConfig.polarData.polarRadiusEnd],
- },
- radiusAxis: {
- type: props.chartConfig.polarData.polarIsTangent?'category':undefined,
- data: props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
- max: props.chartConfig.polarData.polarIsTangent?undefined:props.chartConfig.polarData.radiusAxisMax || undefined,
- },
- angleAxis: {
- type: props.chartConfig.polarData.polarIsTangent?undefined:'category',
- data: !props.chartConfig.polarData.polarIsTangent && props.chartConfig.polarData.labelData.length!==0?props.chartConfig.polarData.labelData:undefined,
- max: props.chartConfig.polarData.polarIsTangent?props.chartConfig.polarData.radiusAxisMax || undefined:undefined,
- startAngle: props.chartConfig.polarData.polarStartAngle,
- endAngle: props.chartConfig.polarData.polarEndAngle,
- },
- }
- }
-
config = {
...config,
dataset: [
@@ -273,70 +165,10 @@ export function getEchartsConfig(
lineStyle: {
...chartStyleWrapper(props?.chartStyle, theme?.chartStyle)
},
- data: transformedData.map((i: any) => i[series.name])
})),
};
- if (axisChart) {
- // pure chart's size except the margin around
- let chartRealSize;
- if (chartSize) {
- const rightSize =
- typeof gridPos.right === "number"
- ? gridPos.right
- : (chartSize.w * parseFloat(gridPos.right)) / 100.0;
- chartRealSize = {
- // actually it's self-adaptive with the x-axis label on the left, not that accurate but work
- w: chartSize.w - gridPos.left - rightSize,
- // also self-adaptive on the bottom
- h: chartSize.h - gridPos.top - gridPos.bottom,
- right: rightSize,
- };
- }
- const finalXyConfig = calcXYConfig(
- props.xConfig,
- yAxisConfig,
- props.xAxisDirection,
- transformedData.map((d) => d[props.xAxisKey]),
- chartRealSize
- );
- config = {
- ...config,
- // @ts-ignore
- xAxis: {
- ...finalXyConfig.xConfig,
- axisLabel: {
- ...styleWrapper(props?.xAxisStyle, theme?.xAxisStyle, 11)
- },
- data: finalXyConfig.xConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
- },
- // @ts-ignore
- yAxis: {
- ...finalXyConfig.yConfig,
- axisLabel: {
- ...styleWrapper(props?.yAxisStyle, theme?.yAxisStyle, 11)
- },
- data: finalXyConfig.yConfig.type === "category" && (props.xAxisData as []).length!==0?props?.xAxisData:transformedData.map((i: any) => i[props.xAxisKey]),
- },
- };
-
- if(props.chartConfig.race) {
- config = {
- ...config,
- xAxis: {
- ...config.xAxis,
- animationDuration: 300,
- animationDurationUpdate: 300
- },
- yAxis: {
- ...config.yAxis,
- animationDuration: 300,
- animationDurationUpdate: 300
- },
- }
- }
- }
- // log.log("Echarts transformedData and config", transformedData, config);
+ // console.log("Echarts transformedData and config", transformedData, config);
return config;
}
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
index 24bd8e774..4c0240502 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -5,12 +5,8 @@ import {
isNumeric,
genRandomKey,
Dropdown,
- Option,
- RedButton,
- CustomModal,
MultiCompBuilder,
valueComp,
- dropdownControl,
} from "lowcoder-sdk";
import { trans } from "i18n/comps";
@@ -20,7 +16,6 @@ import { CompAction, CustomAction, customAction, isMyCustomAction } from "lowcod
export type SeriesCompType = ConstructorToComp;
export type RawSeriesCompType = ConstructorToView;
type SeriesDataType = ConstructorToDataType;
-type MarkLineDataType = ConstructorToDataType;
type ActionDataType = {
type: "chartDataChanged";
@@ -57,58 +52,6 @@ export const MarkLineTypeOptions = [
},
] as const;
-export const StepOptions = [
- {
- label: trans("pieChart.none"),
- value: "",
- },
- {
- label: trans("pieChart.start"),
- value: "start",
- },
- {
- label: trans("pieChart.middle"),
- value: "middle",
- },
- {
- label: trans("pieChart.end"),
- value: "end",
- },
-] as const;
-
-const valToLabel = (val) => MarkLineTypeOptions.find(o => o.value === val)?.label || "";
-const markLinesChildrenMap = {
- type: dropdownControl(MarkLineTypeOptions, "max"),
- // unique key, for sort
- dataIndex: valueComp(""),
-};
-const MarkLinesTmpComp = new MultiCompBuilder(markLinesChildrenMap, (props) => {
- return props;
-})
- .setPropertyViewFn((children: any) => {
- return <>{children.type.propertyView({label: trans("pieChart.type")})}>;
- })
- .build();
-const markAreasChildrenMap = {
- name: StringControl,
- from: StringControl,
- to: StringControl,
- // unique key, for sort
- dataIndex: valueComp(""),
-};
-const MarkAreasTmpComp = new MultiCompBuilder(markAreasChildrenMap, (props) => {
- return props;
-})
- .setPropertyViewFn((children: any) =>
- (<>
- {children.name.propertyView({label: trans("pieChart.name")})}
- {children.from.propertyView({label: trans("pieChart.from")})}
- {children.to.propertyView({label: trans("pieChart.to")})}
- >)
- )
- .build();
-
-
export function newMarkArea(): MarkLineDataType {
return {
dataIndex: genRandomKey(),
@@ -118,12 +61,9 @@ export function newMarkArea(): MarkLineDataType {
const seriesChildrenMap = {
columnName: StringControl,
seriesName: StringControl,
- markLines: list(MarkLinesTmpComp),
- markAreas: list(MarkAreasTmpComp),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
- step: dropdownControl(StepOptions, ""),
};
const SeriesTmpComp = new MultiCompBuilder(seriesChildrenMap, (props) => {
@@ -149,81 +89,6 @@ class SeriesComp extends SeriesTmpComp {
this.children.columnName.dispatchChangeValueAction(value);
}}
/>
- {this.children.step.propertyView({
- label: trans("pieChart.step"),
- })}
- valToLabel(s.getView().type)}
- popoverTitle={(s) => trans("pieChart.markLineType")}
- content={(s, index) => (
- <>
- {s.getPropertyView({label: "Type"})}
- {
- {
- this.children.markLines.dispatch(this.children.markLines.deleteAction(index));
- }}
- >
- {trans("chart.delete")}
-
- }
- >
- )}
- onAdd={() => {
- this.children.markLines.dispatch(
- this.children.markLines.pushAction(
- newMarkLine("max")
- )
- );
- }}
- onMove={(fromIndex, toIndex) => {
- const action = this.children.markLines.arrayMoveAction(fromIndex, toIndex);
- this.children.markLines.dispatch(action);
- }}
- hide={(s) => true}
- onHide={(s, hide) => console.log("onHide")}
- dataIndex={(s) => {
- return s.getView().dataIndex;
- }}
- />
- s.getView().name}
- popoverTitle={(s) => trans("pieChart.markLineType")}
- content={(s, index) => (
- <>
- {s.getPropertyView({label: "Type"})}
- {
- {
- this.children.markAreas.dispatch(this.children.markAreas.deleteAction(index));
- }}
- >
- {trans("chart.delete")}
-
- }
- >
- )}
- onAdd={() => {
- this.children.markAreas.dispatch(
- this.children.markAreas.pushAction(
- newMarkArea()
- )
- );
- }}
- onMove={(fromIndex, toIndex) => {
- const action = this.children.markAreas.arrayMoveAction(fromIndex, toIndex);
- this.children.markAreas.dispatch(action);
- }}
- hide={(s) => true}
- onHide={(s, hide) => console.log("onHide")}
- dataIndex={(s) => {
- return s.getView().dataIndex;
- }}
- />
>
);
}
From 109bb8f96353aeb3e997856648bc0412cdc03655 Mon Sep 17 00:00:00 2001
From: danhnv7
Date: Mon, 17 Feb 2025 16:59:11 +0700
Subject: [PATCH 034/124] fix autoFormat
---
.../src/base/codeEditor/autoFormat.tsx | 28 ++++++++++---------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/client/packages/lowcoder/src/base/codeEditor/autoFormat.tsx b/client/packages/lowcoder/src/base/codeEditor/autoFormat.tsx
index 17b5820a3..8a1c63caa 100644
--- a/client/packages/lowcoder/src/base/codeEditor/autoFormat.tsx
+++ b/client/packages/lowcoder/src/base/codeEditor/autoFormat.tsx
@@ -2,30 +2,31 @@ import type { CodeType } from "lowcoder-core";
import { relaxedJSONToJSON } from "lowcoder-core";
import { getDynamicStringSegments, isDynamicSegment } from "lowcoder-core";
import { format as formatSQL } from "sql-formatter";
+import estree from "prettier/plugins/estree";
import type { Language } from "./codeEditorTypes";
export async function cssFormatter(text: string) {
- const prettier = await require("prettier/standalone");
- const parserPlugin = await require("prettier/parser-postcss");
+ const prettier = await import("prettier/standalone");
+ const parserPlugin = await import("prettier/plugins/postcss");
return (await prettier.format(text, { parser: "css", plugins: [parserPlugin], semi: false })).trim();
}
export async function htmlFormatter(text: string) {
- const prettier = await require("prettier/standalone");
- const parserPlugin = await require("prettier/parser-html");
+ const prettier = await import("prettier/standalone");
+ const parserPlugin = await import("prettier/plugins/html");
return (await prettier.format(text, { parser: "html", plugins: [parserPlugin], semi: false })).trim();
}
async function getJavascriptFormatter() {
- const prettier = await require("prettier/standalone");
- const parserBabel = await require("prettier/parser-babel");
+ const prettier = await import("prettier/standalone");
+ const parserBabel = await import("prettier/plugins/babel");
return async (text: string) =>
- (await prettier.format(text, { parser: "babel", plugins: [parserBabel], semi: false })).trim();
+ (await prettier.format(text, { parser: "babel", plugins: [parserBabel, estree], semi: false })).trim();
}
export async function getJsonFormatter() {
- const prettier = await require("prettier/standalone");
- const parserBabel = await require("prettier/parser-babel");
+ const prettier = await import("prettier/standalone");
+ const parserBabel = await import("prettier/plugins/babel");
return async (text: string) => (await prettier.format(text, { parser: "json", plugins: [parserBabel] })).trim();
}
@@ -46,15 +47,16 @@ async function formatJsSegment(formatter: (text: string) => Promise, scr
async function getJsSegmentFormatter() {
const formatter = await getJavascriptFormatter();
return async (segment: string) => {
- return "{{" + formatJsSegment(formatter, segment.slice(2, -2)) + "}}";
+ return "{{" + await formatJsSegment(formatter, segment.slice(2, -2)) + "}}";
};
}
export async function formatStringWithJsSnippets(text: string): Promise {
const jsSegmentFormatter = await getJsSegmentFormatter();
- return getDynamicStringSegments(text)
- .map((s) => (isDynamicSegment(s) ? jsSegmentFormatter(s) : s))
- .join("");
+ const formatedSegments = await Promise.all(
+ getDynamicStringSegments(text).map((s) => (isDynamicSegment(s) ? jsSegmentFormatter(s) : s))
+ );
+ return formatedSegments.join("");
}
export async function formatSqlWithJsSnippets(text: string) {
From d7ab34f2abd6832ef022bf95d3859cb52cbe4636 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 17 Feb 2025 09:46:34 -0500
Subject: [PATCH 035/124] start/end angle
---
.../src/comps/pieChartComp/pieChartUtils.ts | 2 ++
.../src/comps/pieChartComp/seriesComp.tsx | 10 ++++++++++
.../lowcoder-comps/src/i18n/comps/locales/en.ts | 4 ++++
3 files changed, 16 insertions(+)
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
index a97c50c96..2161b5da0 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -73,6 +73,8 @@ export function getSeriesConfig(props: EchartsConfigProps) {
...props.chartConfig,
radius: radiusAndCenter.radius,
center: radiusAndCenter.center,
+ startAngle: s.getView().startAngle,
+ endAngle: s.getView().endAngle,
name: s.getView().seriesName,
selectedMode: "single",
encode: {
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
index 4c0240502..64f0a3035 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -2,6 +2,8 @@ import {
BoolControl,
StringControl,
list,
+ withDefault,
+ NumberControl,
isNumeric,
genRandomKey,
Dropdown,
@@ -61,6 +63,8 @@ export function newMarkArea(): MarkLineDataType {
const seriesChildrenMap = {
columnName: StringControl,
seriesName: StringControl,
+ startAngle: withDefault(NumberControl, 0),
+ endAngle: withDefault(NumberControl, 360),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -89,6 +93,12 @@ class SeriesComp extends SeriesTmpComp {
this.children.columnName.dispatchChangeValueAction(value);
}}
/>
+ {this.children.startAngle.propertyView({
+ label: trans("pieChart.startAngle"),
+ })}
+ {this.children.endAngle.propertyView({
+ label: trans("pieChart.endAngle"),
+ })}
>
);
}
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 7a1e6dc76..ab01e44a1 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -345,6 +345,10 @@ export const en = {
borderWidth: "Border Width",
borderType: "Border Type",
},
+ pieChart: {
+ startAngle: "Start Angle",
+ endAngle: "End Angle",
+ },
barChart: {
title: 'Title',
barWidth: 'Bar Width(%)',
From af0b5992cd6d638b7237b79d86edee15bc0e78ac Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 17 Feb 2025 10:34:06 -0500
Subject: [PATCH 036/124] Rose Type
---
.../src/comps/pieChartComp/pieChartUtils.ts | 6 +++++-
.../src/comps/pieChartComp/seriesComp.tsx | 20 +++++++++++++++++++
.../src/i18n/comps/locales/en.ts | 4 ++++
3 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
index 2161b5da0..4fe5801aa 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -69,7 +69,7 @@ export function getSeriesConfig(props: EchartsConfigProps) {
return visibleSeries.map((s, index) => {
// pie
const radiusAndCenter = getPieRadiusAndCenter(seriesLength, index, props.chartConfig);
- return {
+ const config = {
...props.chartConfig,
radius: radiusAndCenter.radius,
center: radiusAndCenter.center,
@@ -82,6 +82,10 @@ export function getSeriesConfig(props: EchartsConfigProps) {
value: s.getView().columnName,
},
};
+ if(s.getView().roseType !== "none") {
+ config.roseType = s.getView().roseType;
+ }
+ return config;
});
}
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
index 64f0a3035..15a788af7 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -2,6 +2,7 @@ import {
BoolControl,
StringControl,
list,
+ dropdownControl,
withDefault,
NumberControl,
isNumeric,
@@ -54,6 +55,21 @@ export const MarkLineTypeOptions = [
},
] as const;
+export const RoseTypeOptions = [
+ {
+ label: trans("pieChart.radius"),
+ value: "radius",
+ },
+ {
+ label: trans("pieChart.area"),
+ value: "area",
+ },
+ {
+ label: trans("pieChart.none"),
+ value: "none",
+ },
+] as const;
+
export function newMarkArea(): MarkLineDataType {
return {
dataIndex: genRandomKey(),
@@ -65,6 +81,7 @@ const seriesChildrenMap = {
seriesName: StringControl,
startAngle: withDefault(NumberControl, 0),
endAngle: withDefault(NumberControl, 360),
+ roseType: dropdownControl(RoseTypeOptions, "none"),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -99,6 +116,9 @@ class SeriesComp extends SeriesTmpComp {
{this.children.endAngle.propertyView({
label: trans("pieChart.endAngle"),
})}
+ {this.children.roseType.propertyView({
+ label: trans("pieChart.roseType"),
+ })}
>
);
}
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 ab01e44a1..a67575c75 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -348,6 +348,10 @@ export const en = {
pieChart: {
startAngle: "Start Angle",
endAngle: "End Angle",
+ roseType: "Rose Type",
+ area: "Area",
+ radius: "Radius",
+ none: "None",
},
barChart: {
title: 'Title',
From bedbbc136f3179ec757c7a0dbd900eb8e7fd0a03 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 17 Feb 2025 11:11:39 -0500
Subject: [PATCH 037/124] label style
---
.../src/comps/pieChartComp/pieChartUtils.ts | 6 ++
.../src/comps/pieChartComp/seriesComp.tsx | 70 ++++++++++++-------
.../src/i18n/comps/locales/en.ts | 11 +++
3 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
index 4fe5801aa..81ec8c3b6 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -77,6 +77,12 @@ export function getSeriesConfig(props: EchartsConfigProps) {
endAngle: s.getView().endAngle,
name: s.getView().seriesName,
selectedMode: "single",
+ label: {
+ position: s.getView().labelPosition,
+ alignTo: s.getView().labelAlignTo,
+ bleedMargin: s.getView().labelBleedMargin,
+ edgeDistance: s.getView().labelEdgeDistance,
+ },
encode: {
itemName: props.xAxisKey,
value: s.getView().columnName,
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
index 15a788af7..6592b498c 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -33,48 +33,50 @@ export function newSeries(name: string, columnName: string): SeriesDataType {
};
}
-export function newMarkLine(type: string): MarkLineDataType {
- return {
- type,
- dataIndex: genRandomKey(),
- };
-}
-
-export const MarkLineTypeOptions = [
+export const RoseTypeOptions = [
{
- label: trans("pieChart.max"),
- value: "max",
+ label: trans("pieChart.radius"),
+ value: "radius",
},
{
- label: trans("pieChart.average"),
- value: "average",
+ label: trans("pieChart.area"),
+ value: "area",
},
{
- label: trans("pieChart.min"),
- value: "min",
+ label: trans("pieChart.none"),
+ value: "none",
},
] as const;
-export const RoseTypeOptions = [
+export const LabelAlignToOptions = [
{
- label: trans("pieChart.radius"),
- value: "radius",
+ label: trans("pieChart.none"),
+ value: "none",
},
{
- label: trans("pieChart.area"),
- value: "area",
+ label: trans("pieChart.labelLine"),
+ value: "labelLine",
},
{
- label: trans("pieChart.none"),
- value: "none",
+ label: trans("pieChart.edge"),
+ value: "edge",
},
] as const;
-export function newMarkArea(): MarkLineDataType {
- return {
- dataIndex: genRandomKey(),
- };
-}
+export const LabelPositionOptions = [
+ {
+ label: trans("pieChart.outer"),
+ value: "outer",
+ },
+ {
+ label: trans("pieChart.inner"),
+ value: "inner",
+ },
+ {
+ label: trans("pieChart.center"),
+ value: "center",
+ },
+] as const;
const seriesChildrenMap = {
columnName: StringControl,
@@ -82,6 +84,10 @@ const seriesChildrenMap = {
startAngle: withDefault(NumberControl, 0),
endAngle: withDefault(NumberControl, 360),
roseType: dropdownControl(RoseTypeOptions, "none"),
+ labelAlignTo: dropdownControl(LabelAlignToOptions, "none"),
+ labelPosition: dropdownControl(LabelPositionOptions, "outer"),
+ labelBleedMargin: withDefault(NumberControl, 5),
+ labelEdgeDistance: withDefault(StringControl, '25%'),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -119,6 +125,18 @@ class SeriesComp extends SeriesTmpComp {
{this.children.roseType.propertyView({
label: trans("pieChart.roseType"),
})}
+ {this.children.labelPosition.propertyView({
+ label: trans("pieChart.labelPosition"),
+ })}
+ {this.children.labelAlignTo.propertyView({
+ label: trans("pieChart.labelAlignTo"),
+ })}
+ {this.children.labelBleedMargin.propertyView({
+ label: trans("pieChart.labelBleedMargin"),
+ })}
+ {this.children.labelEdgeDistance.propertyView({
+ label: trans("pieChart.labelEdgeDistance"),
+ })}
>
);
}
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 a67575c75..e9787d8a0 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -352,6 +352,17 @@ export const en = {
area: "Area",
radius: "Radius",
none: "None",
+ labelPosition: "Position",
+ labelAlignTo: "AlignTo",
+ labelBleedMargin: "Bleed Margin",
+ labelEdgeDistance: "Edge Distance",
+ labelLine: "Label Line",
+ edge: "Edge",
+ outside: "Outside",
+ outer: "Outer",
+ inside: "Inside",
+ inner: "Inner",
+ center: "Center",
},
barChart: {
title: 'Title',
From 7644006a6bcde5f7bdc091d2ae2054d6acd6e470 Mon Sep 17 00:00:00 2001
From: Imiss-U1025
Date: Mon, 17 Feb 2025 11:22:03 -0500
Subject: [PATCH 038/124] label line property
---
.../src/comps/pieChartComp/pieChartUtils.ts | 4 ++++
.../src/comps/pieChartComp/seriesComp.tsx | 10 +++++++++-
.../lowcoder-comps/src/i18n/comps/locales/en.ts | 2 ++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
index 81ec8c3b6..6dba487bc 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartUtils.ts
@@ -83,6 +83,10 @@ export function getSeriesConfig(props: EchartsConfigProps) {
bleedMargin: s.getView().labelBleedMargin,
edgeDistance: s.getView().labelEdgeDistance,
},
+ labelLine: {
+ length: s.getView().labelLineLength,
+ length2: s.getView().labelLineLength2,
+ },
encode: {
itemName: props.xAxisKey,
value: s.getView().columnName,
diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
index 6592b498c..4a3b2775d 100644
--- a/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
+++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/seriesComp.tsx
@@ -88,6 +88,8 @@ const seriesChildrenMap = {
labelPosition: dropdownControl(LabelPositionOptions, "outer"),
labelBleedMargin: withDefault(NumberControl, 5),
labelEdgeDistance: withDefault(StringControl, '25%'),
+ labelLineLength: withDefault(NumberControl, 10),
+ labelLineLength2: withDefault(NumberControl, 10),
hide: BoolControl,
// unique key, for sort
dataIndex: valueComp(""),
@@ -134,9 +136,15 @@ class SeriesComp extends SeriesTmpComp {
{this.children.labelBleedMargin.propertyView({
label: trans("pieChart.labelBleedMargin"),
})}
- {this.children.labelEdgeDistance.propertyView({
+ {this.children.labelAlignTo.getView() === "edge" && this.children.labelEdgeDistance.propertyView({
label: trans("pieChart.labelEdgeDistance"),
})}
+ {this.children.labelLineLength.propertyView({
+ label: trans("pieChart.labelLineLength"),
+ })}
+ {this.children.labelAlignTo.getView() === "labelLine" && this.children.labelLineLength2.propertyView({
+ label: trans("pieChart.labelLineLength2"),
+ })}
>
);
}
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 e9787d8a0..17a3aabdf 100644
--- a/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
+++ b/client/packages/lowcoder-comps/src/i18n/comps/locales/en.ts
@@ -363,6 +363,8 @@ export const en = {
inside: "Inside",
inner: "Inner",
center: "Center",
+ labelLineLength: "Label Line Length",
+ labelLineLength2: "Label Line Length2",
},
barChart: {
title: 'Title',
From a81374029948355ab28ccc54a6111d5db1fd354d Mon Sep 17 00:00:00 2001
From: Thomasr
Date: Wed, 19 Feb 2025 11:32:23 -0500
Subject: [PATCH 039/124] Added following application events -
APPLICATION_PERMISSION_CHANGE - APPLICATION_SHARING_CHANGE -
APPLICATION_VERSION_CHANGE - APPLICATION_PUBLISHING
---
.../infra/event/ApplicationCommonEvent.java | 9 +
.../application/ApplicationController.java | 16 +-
.../api/util/BusinessEventPublisher.java | 170 ++++++++++++++++++
3 files changed, 192 insertions(+), 3 deletions(-)
diff --git a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/ApplicationCommonEvent.java b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/ApplicationCommonEvent.java
index 3adb8d7d1..a53879bfe 100644
--- a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/ApplicationCommonEvent.java
+++ b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/ApplicationCommonEvent.java
@@ -5,6 +5,8 @@
import lombok.Getter;
import lombok.experimental.SuperBuilder;
+import java.util.Set;
+
@Getter
@SuperBuilder
public class ApplicationCommonEvent extends AbstractEvent {
@@ -23,6 +25,13 @@ public class ApplicationCommonEvent extends AbstractEvent {
private final String oldFolderId;
@Nullable
private final String oldFolderName;
+ private final String permissionId;
+ private final String role;
+ private final Set userIds;
+ private final Set groupIds;
+ private final String shareType;
+ private final String tag;
+ private final String commitMessage;
@Override
public EventType getEventType() {
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
index 582cb2803..9962783cf 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
@@ -4,7 +4,6 @@
import org.lowcoder.api.application.view.*;
import org.lowcoder.api.framework.view.PageResponseView;
import org.lowcoder.api.framework.view.ResponseView;
-import org.lowcoder.api.home.SessionUserService;
import org.lowcoder.api.home.UserHomeApiService;
import org.lowcoder.api.home.UserHomepageView;
import org.lowcoder.api.util.BusinessEventPublisher;
@@ -14,7 +13,6 @@
import org.lowcoder.domain.application.model.ApplicationStatus;
import org.lowcoder.domain.application.model.ApplicationType;
import org.lowcoder.domain.application.service.ApplicationRecordService;
-import org.lowcoder.domain.folder.service.FolderElementRelationService;
import org.lowcoder.domain.permission.model.ResourceRole;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -37,7 +35,6 @@ public class ApplicationController implements ApplicationEndpoints {
private final UserHomeApiService userHomeApiService;
private final ApplicationApiService applicationApiService;
private final BusinessEventPublisher businessEventPublisher;
- private final SessionUserService sessionUserService;
private final GidService gidService;
private final ApplicationRecordService applicationRecordService;
@@ -152,6 +149,14 @@ public Mono> publish(@PathVariable String applicat
return newtag;
})
.switchIfEmpty(Mono.just("1.0.0"))
+ .delayUntil(newtag -> {
+ ApplicationPublishRequest req = Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag));
+ return businessEventPublisher.publishApplicationPublishEvent(appId, req).then(Mono.defer(() -> {
+ if(newtag.equals(req.tag())) {
+ return businessEventPublisher.publishApplicationVersionChangeEvent(appId, newtag);
+ } else return Mono.empty();
+ }));
+ })
.flatMap(newtag -> applicationApiService.publish(appId, Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag))))
.map(ResponseView::success));
}
@@ -221,6 +226,7 @@ public Mono> updatePermission(@PathVariable String applica
}
return gidService.convertApplicationIdToObjectId(applicationId).flatMap(appId ->
applicationApiService.updatePermission(appId, permissionId, role)
+ .delayUntil(__ -> businessEventPublisher.publishApplicationPermissionEvent(applicationId, null, null, permissionId, role.getValue()))
.map(ResponseView::success));
}
@@ -230,6 +236,7 @@ public Mono> removePermission(
@PathVariable String permissionId) {
return gidService.convertApplicationIdToObjectId(applicationId).flatMap(appId ->
applicationApiService.removePermission(appId, permissionId)
+ .delayUntil(__ -> businessEventPublisher.publishApplicationPermissionEvent(applicationId, null, null, permissionId, null))
.map(ResponseView::success));
}
@@ -246,6 +253,7 @@ public Mono> grantPermission(
emptyIfNull(request.userIds()),
emptyIfNull(request.groupIds()),
role)
+ .delayUntil(__ -> businessEventPublisher.publishApplicationPermissionEvent(applicationId, emptyIfNull(request.userIds()), emptyIfNull(request.groupIds()), null, role.getValue()))
.map(ResponseView::success));
}
@@ -262,6 +270,7 @@ public Mono> setApplicationPublicToAll(@PathVariable Strin
@RequestBody ApplicationPublicToAllRequest request) {
return gidService.convertApplicationIdToObjectId(applicationId).flatMap(appId ->
applicationApiService.setApplicationPublicToAll(appId, request.publicToAll())
+ .delayUntil(__ -> businessEventPublisher.publishApplicationSharingEvent(applicationId, "PublicToAll"))
.map(ResponseView::success));
}
@@ -270,6 +279,7 @@ public Mono> setApplicationPublicToMarketplace(@PathVariab
@RequestBody ApplicationPublicToMarketplaceRequest request) {
return gidService.convertApplicationIdToObjectId(applicationId).flatMap(appId ->
applicationApiService.setApplicationPublicToMarketplace(appId, request)
+ .delayUntil(__ -> businessEventPublisher.publishApplicationSharingEvent(applicationId, "PublicToMarketplace"))
.map(ResponseView::success));
}
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java
index fe3119d32..e6902a460 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java
@@ -7,10 +7,12 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.lowcoder.api.application.view.ApplicationInfoView;
+import org.lowcoder.api.application.view.ApplicationPublishRequest;
import org.lowcoder.api.application.view.ApplicationView;
import org.lowcoder.api.home.SessionUserService;
import org.lowcoder.api.usermanagement.view.AddMemberRequest;
import org.lowcoder.api.usermanagement.view.UpdateRoleRequest;
+import org.lowcoder.domain.application.model.Application;
import org.lowcoder.domain.application.service.ApplicationRecordServiceImpl;
import org.lowcoder.domain.application.service.ApplicationService;
import org.lowcoder.domain.datasource.model.Datasource;
@@ -186,6 +188,174 @@ public Mono publishApplicationCommonEvent(ApplicationView applicationView,
});
}
+ public Mono publishApplicationPermissionEvent(String applicationId, Set userIds, Set groupIds, String permissionId, String role) {
+ return sessionUserService.isAnonymousUser()
+ .flatMap(anonymous -> {
+ if (anonymous) {
+ return Mono.empty();
+ }
+ return sessionUserService.getVisitorOrgMemberCache()
+ .zipWith(sessionUserService.getVisitorToken())
+ .zipWith(Mono.defer(() -> applicationService.findById(applicationId)
+ .zipWhen(application -> application.getCategory(applicationRecordServiceImpl))
+ .zipWhen(application -> application.getT1().getDescription(applicationRecordServiceImpl))))
+ .doOnNext(tuple -> {
+ OrgMember orgMember = tuple.getT1().getT1();
+ String token = tuple.getT1().getT2();
+ String category = tuple.getT2().getT1().getT2();
+ String description = tuple.getT2().getT2();
+ Application application = tuple.getT2().getT1().getT1();
+ ApplicationCommonEvent event = ApplicationCommonEvent.builder()
+ .orgId(orgMember.getOrgId())
+ .userId(orgMember.getUserId())
+ .applicationId(application.getId())
+ .applicationGid(application.getGid())
+ .applicationName(application.getName())
+ .applicationCategory(category)
+ .applicationDescription(description)
+ .type(EventType.APPLICATION_PERMISSION_CHANGE)
+ .permissionId(permissionId)
+ .role(role)
+ .userIds(userIds)
+ .groupIds(groupIds)
+ .isAnonymous(anonymous)
+ .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString())
+ .build();
+ event.populateDetails();
+ applicationEventPublisher.publishEvent(event);
+ })
+ .then()
+ .onErrorResume(throwable -> {
+ log.error("publishApplicationPermissionEvent error. {}, {}, {}", applicationId, permissionId, role, throwable);
+ return Mono.empty();
+ });
+ });
+ }
+
+ public Mono publishApplicationSharingEvent(String applicationId, String shareType) {
+ return sessionUserService.isAnonymousUser()
+ .flatMap(anonymous -> {
+ if (anonymous) {
+ return Mono.empty();
+ }
+ return sessionUserService.getVisitorOrgMemberCache()
+ .zipWith(sessionUserService.getVisitorToken())
+ .zipWith(Mono.defer(() -> applicationService.findById(applicationId)
+ .zipWhen(application -> application.getCategory(applicationRecordServiceImpl))
+ .zipWhen(application -> application.getT1().getDescription(applicationRecordServiceImpl))))
+ .doOnNext(tuple -> {
+ OrgMember orgMember = tuple.getT1().getT1();
+ String token = tuple.getT1().getT2();
+ String category = tuple.getT2().getT1().getT2();
+ String description = tuple.getT2().getT2();
+ Application application = tuple.getT2().getT1().getT1();
+ ApplicationCommonEvent event = ApplicationCommonEvent.builder()
+ .orgId(orgMember.getOrgId())
+ .userId(orgMember.getUserId())
+ .applicationId(application.getId())
+ .applicationGid(application.getGid())
+ .applicationName(application.getName())
+ .applicationCategory(category)
+ .applicationDescription(description)
+ .type(EventType.APPLICATION_SHARING_CHANGE)
+ .shareType(shareType)
+ .isAnonymous(anonymous)
+ .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString())
+ .build();
+ event.populateDetails();
+ applicationEventPublisher.publishEvent(event);
+ })
+ .then()
+ .onErrorResume(throwable -> {
+ log.error("publishApplicationSharingEvent error. {}, {}", applicationId, shareType, throwable);
+ return Mono.empty();
+ });
+ });
+ }
+
+ public Mono publishApplicationPublishEvent(String applicationId, ApplicationPublishRequest request) {
+ return sessionUserService.isAnonymousUser()
+ .flatMap(anonymous -> {
+ if (anonymous) {
+ return Mono.empty();
+ }
+ return sessionUserService.getVisitorOrgMemberCache()
+ .zipWith(sessionUserService.getVisitorToken())
+ .zipWith(Mono.defer(() -> applicationService.findById(applicationId)
+ .zipWhen(application -> application.getCategory(applicationRecordServiceImpl))
+ .zipWhen(application -> application.getT1().getDescription(applicationRecordServiceImpl))))
+ .doOnNext(tuple -> {
+ OrgMember orgMember = tuple.getT1().getT1();
+ String token = tuple.getT1().getT2();
+ String category = tuple.getT2().getT1().getT2();
+ String description = tuple.getT2().getT2();
+ Application application = tuple.getT2().getT1().getT1();
+ ApplicationCommonEvent event = ApplicationCommonEvent.builder()
+ .orgId(orgMember.getOrgId())
+ .userId(orgMember.getUserId())
+ .applicationId(application.getId())
+ .applicationGid(application.getGid())
+ .applicationName(application.getName())
+ .applicationCategory(category)
+ .applicationDescription(description)
+ .type(EventType.APPLICATION_SHARING_CHANGE)
+ .commitMessage(request.commitMessage())
+ .tag(request.tag())
+ .isAnonymous(anonymous)
+ .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString())
+ .build();
+ event.populateDetails();
+ applicationEventPublisher.publishEvent(event);
+ })
+ .then()
+ .onErrorResume(throwable -> {
+ log.error("publishApplicationPublishEvent error. {}, {}, {}", applicationId, request.tag(), request.commitMessage(), throwable);
+ return Mono.empty();
+ });
+ });
+ }
+
+ public Mono publishApplicationVersionChangeEvent(String applicationId, String newtag) {
+ return sessionUserService.isAnonymousUser()
+ .flatMap(anonymous -> {
+ if (anonymous) {
+ return Mono.empty();
+ }
+ return sessionUserService.getVisitorOrgMemberCache()
+ .zipWith(sessionUserService.getVisitorToken())
+ .zipWith(Mono.defer(() -> applicationService.findById(applicationId)
+ .zipWhen(application -> application.getCategory(applicationRecordServiceImpl))
+ .zipWhen(application -> application.getT1().getDescription(applicationRecordServiceImpl))))
+ .doOnNext(tuple -> {
+ OrgMember orgMember = tuple.getT1().getT1();
+ String token = tuple.getT1().getT2();
+ String category = tuple.getT2().getT1().getT2();
+ String description = tuple.getT2().getT2();
+ Application application = tuple.getT2().getT1().getT1();
+ ApplicationCommonEvent event = ApplicationCommonEvent.builder()
+ .orgId(orgMember.getOrgId())
+ .userId(orgMember.getUserId())
+ .applicationId(application.getId())
+ .applicationGid(application.getGid())
+ .applicationName(application.getName())
+ .applicationCategory(category)
+ .applicationDescription(description)
+ .type(EventType.APPLICATION_SHARING_CHANGE)
+ .tag(newtag)
+ .isAnonymous(anonymous)
+ .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString())
+ .build();
+ event.populateDetails();
+ applicationEventPublisher.publishEvent(event);
+ })
+ .then()
+ .onErrorResume(throwable -> {
+ log.error("publishApplicationPublishEvent error. {}, {}", applicationId, newtag, throwable);
+ return Mono.empty();
+ });
+ });
+ }
+
public Mono publishUserLoginEvent(String source) {
return sessionUserService.getVisitorOrgMember().zipWith(sessionUserService.getVisitorToken())
.doOnNext(tuple -> {
From 34c252c3af5934fce720ef77d9b6e6e693d359ec Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Fri, 14 Feb 2025 01:08:53 +0500
Subject: [PATCH 040/124] match lazy comp's loading skeleton height as per
comp's default height
---
.../lowcoder-design/src/components/Loading.tsx | 7 ++++++-
.../lowcoder/src/comps/comps/gridItemComp.tsx | 1 +
.../src/comps/comps/lazyLoadComp/lazyLoadComp.tsx | 11 +++++++++--
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/client/packages/lowcoder-design/src/components/Loading.tsx b/client/packages/lowcoder-design/src/components/Loading.tsx
index 414096e33..d5ffc6710 100644
--- a/client/packages/lowcoder-design/src/components/Loading.tsx
+++ b/client/packages/lowcoder-design/src/components/Loading.tsx
@@ -74,6 +74,7 @@ type LoadingProps = {
size?: number; // circle's size
className?: string;
style?: CSSProperties;
+ compHeight?: number;
};
export const Loading = (props: LoadingProps) => {
@@ -92,7 +93,11 @@ export const Loading = (props: LoadingProps) => {
*/}
-
+
);
};
diff --git a/client/packages/lowcoder/src/comps/comps/gridItemComp.tsx b/client/packages/lowcoder/src/comps/comps/gridItemComp.tsx
index 7ef5440cc..2b4d17ad6 100644
--- a/client/packages/lowcoder/src/comps/comps/gridItemComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/gridItemComp.tsx
@@ -55,6 +55,7 @@ const TmpComp = withTypeAndChildren<
undefined,
undefined,
manifest.withoutLoading,
+ manifest.layoutInfo?.h,
)
}
const comp = manifest.withoutLoading ? manifest.comp : withIsLoading(manifest.comp!);
diff --git a/client/packages/lowcoder/src/comps/comps/lazyLoadComp/lazyLoadComp.tsx b/client/packages/lowcoder/src/comps/comps/lazyLoadComp/lazyLoadComp.tsx
index 7a45714b3..0b2ac21f6 100644
--- a/client/packages/lowcoder/src/comps/comps/lazyLoadComp/lazyLoadComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/lazyLoadComp/lazyLoadComp.tsx
@@ -49,6 +49,7 @@ interface LazyCompViewProps {
loadComp: () => Promise;
loadingElement?: () => React.ReactNode;
errorElement?: (error: any) => React.ReactNode;
+ height?: number,
}
const LazyCompView = React.memo((props: React.PropsWithChildren) => {
@@ -82,7 +83,7 @@ const LazyCompView = React.memo((props: React.PropsWithChildren
+
);
});
@@ -94,6 +95,7 @@ export function lazyLoadComp(
loader?: LazyloadCompLoader,
loadingElement?: () => React.ReactNode,
withoutLoading?: boolean,
+ height?: number,
) {
class LazyLoadComp extends simpleMultiComp({}) {
compValue: any;
@@ -145,7 +147,12 @@ export function lazyLoadComp(
// const key = `${remoteInfo?.packageName}-${remoteInfo?.packageVersion}-${remoteInfo?.compName}`;
const key = `${compName}`;
return (
- this.load()} loadingElement={loadingElement} />
+ this.load()}
+ loadingElement={loadingElement}
+ height={height}
+ />
);
}
From 9d3f8b9bd61c1f1e0ebcf108976314b608814dd2 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Fri, 14 Feb 2025 01:10:06 +0500
Subject: [PATCH 041/124] fixed tooltip warnings after antd upgrade
---
.../lowcoder-design/src/components/control.tsx | 4 +++-
.../src/components/iconSelect/index.tsx | 10 ++++++----
.../lowcoder-design/src/components/popover.tsx | 18 ++++++++++++------
.../src/components/shapeSelect/index.tsx | 10 ++++++----
.../lowcoder-design/src/components/toolTip.tsx | 6 ++++--
.../comps/comps/tableComp/tableToolbarComp.tsx | 6 ++++--
6 files changed, 35 insertions(+), 19 deletions(-)
diff --git a/client/packages/lowcoder-design/src/components/control.tsx b/client/packages/lowcoder-design/src/components/control.tsx
index a25f9df8b..cae47a3f3 100644
--- a/client/packages/lowcoder-design/src/components/control.tsx
+++ b/client/packages/lowcoder-design/src/components/control.tsx
@@ -159,7 +159,9 @@ export const ControlPropertyViewWrapper = (
)}
diff --git a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx
index 6eac2f7b6..23c73b200 100644
--- a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx
+++ b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx
@@ -363,10 +363,12 @@ export const IconSelectBase = (props: {
onOpenChange={setVisible}
getPopupContainer={parent ? () => parent : undefined}
// hide the original background when dragging the popover is allowed
- overlayInnerStyle={{
- border: "none",
- boxShadow: "none",
- background: "transparent",
+ styles={{
+ body: {
+ border: "none",
+ boxShadow: "none",
+ background: "transparent",
+ }
}}
// when dragging is allowed, always re-location to avoid the popover exceeds the screen
destroyTooltipOnHide
diff --git a/client/packages/lowcoder-design/src/components/popover.tsx b/client/packages/lowcoder-design/src/components/popover.tsx
index c7e745d06..4a6d0cbb5 100644
--- a/client/packages/lowcoder-design/src/components/popover.tsx
+++ b/client/packages/lowcoder-design/src/components/popover.tsx
@@ -64,7 +64,6 @@ const SimplePopover = (props: {
);
return (
{props.children}
@@ -101,16 +103,18 @@ const CustomPopover = (props: {
);
return (
{props.children}
@@ -167,8 +171,10 @@ const EditPopover = (props: EditPopoverProps) => {
return (
(
<>
diff --git a/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx b/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx
index a4a71964b..060945977 100644
--- a/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx
+++ b/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx
@@ -444,10 +444,12 @@ export const ShapeSelectBase = (props: {
onOpenChange={setVisible}
getPopupContainer={parent ? () => parent : undefined}
// hide the original background when dragging the popover is allowed
- overlayInnerStyle={{
- border: "none",
- boxShadow: "none",
- background: "transparent",
+ styles={{
+ body: {
+ border: "none",
+ boxShadow: "none",
+ background: "transparent",
+ }
}}
// when dragging is allowed, always re-location to avoid the popover exceeds the screen
destroyTooltipOnHide
diff --git a/client/packages/lowcoder-design/src/components/toolTip.tsx b/client/packages/lowcoder-design/src/components/toolTip.tsx
index 9b5a624c2..3a6b53843 100644
--- a/client/packages/lowcoder-design/src/components/toolTip.tsx
+++ b/client/packages/lowcoder-design/src/components/toolTip.tsx
@@ -155,7 +155,7 @@ export const UnderlineCss = css`
`;
function Tooltip(props: TooltipProps) {
- return ;
+ return ;
}
const Label = styled.div<{ $border?: boolean }>`
@@ -181,7 +181,9 @@ function ToolTipLabel(
<>{title}>}
- overlayInnerStyle={{ maxWidth: "232px", whiteSpace: "break-spaces" }}
+ styles={{
+ body: { maxWidth: "232px", whiteSpace: "break-spaces" }
+ }}
arrow={{
pointAtCenter: true
}}
diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
index 85703fd11..64d0b6fae 100644
--- a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
@@ -686,8 +686,10 @@ function ToolbarPopover(props: {
return (
Date: Fri, 14 Feb 2025 01:11:09 +0500
Subject: [PATCH 042/124] memoize timer comp's view and propertyView
---
.../lowcoder/src/comps/comps/timerComp.tsx | 101 ++++++++++--------
1 file changed, 55 insertions(+), 46 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/timerComp.tsx b/client/packages/lowcoder/src/comps/comps/timerComp.tsx
index 907566e12..a749cb068 100644
--- a/client/packages/lowcoder/src/comps/comps/timerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/timerComp.tsx
@@ -1,7 +1,7 @@
-import { CompAction, RecordConstructorToView, changeChildAction } from "lowcoder-core";
+import { CompAction, RecordConstructorToComp, RecordConstructorToView, changeChildAction } from "lowcoder-core";
import { styleControl } from "comps/controls/styleControl";
import { AnimationStyle, AnimationStyleType, startButtonStyle, StartButtonStyleType, timerStyle, timerStyleType } from "comps/controls/styleControlConstants";
-import { UICompBuilder } from "comps/generators/uiCompBuilder";
+import { NewChildren, UICompBuilder } from "comps/generators/uiCompBuilder";
import { NameConfig, NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing";
import { Section, sectionNames } from "lowcoder-design";
import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils";
@@ -15,6 +15,7 @@ import { EditorContext } from "comps/editorState";
import { dropdownControl } from "../controls/dropdownControl";
import { stringExposingStateControl } from "comps/controls/codeStateControl";
import { BoolControl } from "comps/controls/boolControl";
+import React from "react";
const Container = styled.div<{
$style: timerStyleType | undefined;
@@ -113,7 +114,9 @@ const childrenMap = {
hideButton: BoolControl,
};
-const AvatarGroupView = (props: RecordConstructorToView & { dispatch: (action: CompAction) => void; }) => {
+type ChildrenType = NewChildren>;
+
+const TimerCompView = React.memo((props: RecordConstructorToView & { dispatch: (action: CompAction) => void; }) => {
const [startTime, setStartTime] = useState(0)
const [timerState, setTimerState] = useState('stoped')
const [elapsedTime, setElapsedTime] = useState(0)
@@ -219,51 +222,57 @@ const AvatarGroupView = (props: RecordConstructorToView & {
);
-};
+});
-let AvatarGroupBasicComp = (function () {
- return new UICompBuilder(childrenMap, (props, dispatch) => )
- .setPropertyViewFn((children) => (
- <>
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
- <>
-
- {children.timerType.propertyView({
- label: trans('timer.timerType')
- })}
- {children.defaultValue.propertyView({
- label: trans('timer.defaultValue')
- })}
- {children.hideButton.propertyView({
- label: trans('timer.hideButton')
- })}
-
-
- {children.onEvent.propertyView()}
- {hiddenPropertyView(children)}
- {showDataLoadingIndicatorsPropertyView(children)}
-
- >
- )}
-
- {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
- <>
-
- {children.style.getPropertyView()}
-
-
- {children.animationStyle.getPropertyView()}
-
-
- {children.startButtonStyle.getPropertyView()}
+const TimerCompPropertyView = React.memo((props: {
+ children: ChildrenType
+}) => {
+ return (
+ <>
+ {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
+ <>
+
+ {props.children.timerType.propertyView({
+ label: trans('timer.timerType')
+ })}
+ {props.children.defaultValue.propertyView({
+ label: trans('timer.defaultValue')
+ })}
+ {props.children.hideButton.propertyView({
+ label: trans('timer.hideButton')
+ })}
-
- {children.resetButtonStyle.getPropertyView()}
+
+ {props.children.onEvent.propertyView()}
+ {hiddenPropertyView(props.children)}
+ {showDataLoadingIndicatorsPropertyView(props.children)}
- >
- )}
- >
- ))
+ >
+ )}
+
+ {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
+ <>
+
+ {props.children.style.getPropertyView()}
+
+
+ {props.children.animationStyle.getPropertyView()}
+
+
+ {props.children.startButtonStyle.getPropertyView()}
+
+
+ {props.children.resetButtonStyle.getPropertyView()}
+
+ >
+ )}
+ >
+ )
+});
+
+let TimerCompBasic = (function () {
+ return new UICompBuilder(childrenMap, (props, dispatch) => )
+ .setPropertyViewFn((children) => )
.setExposeMethodConfigs([
{
method: {
@@ -294,7 +303,7 @@ let AvatarGroupBasicComp = (function () {
.build();
})();
-export const TimerComp = withExposingConfigs(AvatarGroupBasicComp, [
+export const TimerComp = withExposingConfigs(TimerCompBasic, [
new NameConfig("defaultValue", trans("timer.defaultValue")),
new NameConfig("elapsedTime", trans("timer.elapsedTime")),
new NameConfig("timerState", trans("timer.timerState")),
From 23113105ec9d72246f91a6263f0ac24eef35a1e6 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Fri, 14 Feb 2025 01:11:24 +0500
Subject: [PATCH 043/124] memoize transfer comp's view and propertyView
---
.../lowcoder/src/comps/comps/transferComp.tsx | 89 ++++++++++---------
1 file changed, 49 insertions(+), 40 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/transferComp.tsx b/client/packages/lowcoder/src/comps/comps/transferComp.tsx
index 300020bd7..56a20d064 100644
--- a/client/packages/lowcoder/src/comps/comps/transferComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/transferComp.tsx
@@ -1,9 +1,9 @@
-import { CompAction, RecordConstructorToView, changeChildAction } from "lowcoder-core";
+import { CompAction, RecordConstructorToComp, RecordConstructorToView, changeChildAction } from "lowcoder-core";
import { BoolControl } from "comps/controls/boolControl";
import { arrayObjectExposingStateControl, arrayStringExposingStateControl } from "comps/controls/codeStateControl";
import { styleControl } from "comps/controls/styleControl";
import { TransferStyle, TransferStyleType, heightCalculator, widthCalculator } from "comps/controls/styleControlConstants";
-import { UICompBuilder } from "comps/generators/uiCompBuilder";
+import { NewChildren, UICompBuilder } from "comps/generators/uiCompBuilder";
import { NameConfig, NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing";
import { Section, sectionNames } from "lowcoder-design";
import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils";
@@ -17,6 +17,7 @@ import styled, { css } from "styled-components";
import { useContext, useEffect, useRef, useState } from "react";
import { valueComp, withDefault } from "../generators";
import type { TransferDirection } from 'antd/es/transfer';
+import React from "react";
const Container = styled.div<{ $style: TransferStyleType }>`
height: 100%;
@@ -71,7 +72,9 @@ const childrenMap = {
searchInfo: valueComp(['', '']),
};
-const TransferView = (props: RecordConstructorToView & {
+type ChildrenType = NewChildren>;
+
+const TransferView = React.memo((props: RecordConstructorToView & {
dispatch: (action: CompAction) => void;
}) => {
const conRef = useRef(null);
@@ -136,48 +139,54 @@ const TransferView = (props: RecordConstructorToView & {
);
-};
+});
+
+const TransferCompPropertyView = React.memo((props: {
+ children: ChildrenType
+}) => {
+ return (
+ <>
+
+ {props.children.items.propertyView({
+ label: trans("transfer.items"),
+ })}
+ {props.children.targetKeys.propertyView({
+ label: trans("transfer.targetKeys"),
+ })}
+ {props.children.sourceTitle.propertyView({
+ label: trans("transfer.sourceTitle"),
+ })}
+ {props.children.targetTitle.propertyView({
+ label: trans("transfer.targetTitle"),
+ })}
+ {props.children.showSearch.propertyView({
+ label: trans("transfer.allowSearch"),
+ })}
+ {props.children.oneWay.propertyView({
+ label: trans("transfer.oneWay"),
+ })}
+ {props.children.pagination.propertyView({
+ label: trans("transfer.pagination"),
+ })}
+ {props.children.pagination.getView() && props.children.pageSize.propertyView({
+ label: trans("transfer.pageSize"),
+ })}
+
+
+ {props.children.onEvent.propertyView()}
+ {hiddenPropertyView(props.children)}
+ {showDataLoadingIndicatorsPropertyView(props.children)}
+
+ {props.children.style.getPropertyView()}
+ >
+ )
+});
let TransferBasicComp = (function () {
return new UICompBuilder(childrenMap, (props, dispatch) => {
return (
)})
- .setPropertyViewFn((children) => (
- <>
-
- {children.items.propertyView({
- label: trans("transfer.items"),
- })}
- {children.targetKeys.propertyView({
- label: trans("transfer.targetKeys"),
- })}
- {children.sourceTitle.propertyView({
- label: trans("transfer.sourceTitle"),
- })}
- {children.targetTitle.propertyView({
- label: trans("transfer.targetTitle"),
- })}
- {children.showSearch.propertyView({
- label: trans("transfer.allowSearch"),
- })}
- {children.oneWay.propertyView({
- label: trans("transfer.oneWay"),
- })}
- {children.pagination.propertyView({
- label: trans("transfer.pagination"),
- })}
- {children.pagination.getView() && children.pageSize.propertyView({
- label: trans("transfer.pageSize"),
- })}
-
-
- {children.onEvent.propertyView()}
- {hiddenPropertyView(children)}
- {showDataLoadingIndicatorsPropertyView(children)}
-
- {children.style.getPropertyView()}
- >
- ))
+ .setPropertyViewFn((children) => )
.build();
})();
From ad3733aefaf30b5405829a5e9b000cd27f08cbe6 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Fri, 14 Feb 2025 01:20:39 +0500
Subject: [PATCH 044/124] reduce re-rendering by adding memoziation
---
.../comps/containerComp/containerView.tsx | 6 +-
.../responsiveLayout/responsiveLayout.tsx | 4 +-
.../lowcoder/src/layout/calculateUtils.tsx | 1 -
.../src/layout/compSelectionWrapper.tsx | 88 +++++++++++++------
.../packages/lowcoder/src/layout/gridItem.tsx | 58 ++++++++----
.../lowcoder/src/layout/gridLayout.tsx | 8 +-
.../lowcoder/src/layout/gridLines.tsx | 5 +-
.../lowcoder/src/layout/layoutOpUtils.tsx | 4 +
client/packages/lowcoder/src/layout/utils.ts | 1 +
9 files changed, 116 insertions(+), 59 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
index ff00f7fc7..f89964b93 100644
--- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
@@ -439,9 +439,9 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
isRowCountLocked,
onPositionParamsChange,
onRowCountChange,
- positionParams,
+ JSON.stringify(positionParams),
+ JSON.stringify(props.containerPadding),
props.dispatch,
- props.containerPadding,
]
);
const setSelectedNames = useCallback(
@@ -454,6 +454,8 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
const { width, ref } = useResizeDetector({
onResize,
handleHeight: isRowCountLocked,
+ refreshMode: 'debounce',
+ refreshRate: 100,
});
const itemViewRef = useRef({});
diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
index fc8e47536..d8c0e2864 100644
--- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
@@ -136,7 +136,7 @@ type ColumnContainerProps = Omit & {
style: ResponsiveLayoutColStyleType,
}
-const ColumnContainer = (props: ColumnContainerProps) => {
+const ColumnContainer = React.memo((props: ColumnContainerProps) => {
return (
{
style={props.style}
/>
);
-};
+});
const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
const screenInfo = useScreenInfo();
diff --git a/client/packages/lowcoder/src/layout/calculateUtils.tsx b/client/packages/lowcoder/src/layout/calculateUtils.tsx
index 1d919bd1b..44a661e00 100644
--- a/client/packages/lowcoder/src/layout/calculateUtils.tsx
+++ b/client/packages/lowcoder/src/layout/calculateUtils.tsx
@@ -93,7 +93,6 @@ export function calcGridItemSizePx(
const width = calcGridItemWHPx(w, colWidth, margin[0], false);
const isTouchSBound = top ? isTouchBound(maxRows, rowHeight, h, top) : false;
- // console.log('positionParams',positionParams);
const height = calcGridItemWHPx(h, rowHeight, margin[1], isTouchSBound);
return { width, height };
}
diff --git a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
index 4b347907e..64765b9ba 100644
--- a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
+++ b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
@@ -11,6 +11,7 @@ import React, {
MouseEventHandler,
useCallback,
useContext,
+ useMemo,
useRef,
useState,
} from "react";
@@ -18,7 +19,8 @@ import ReactResizeDetector, { useResizeDetector } from "react-resize-detector";
import styled, { css } from "styled-components";
import { EllipsisTextCss } from "lowcoder-design";
import { draggingUtils } from "./draggingUtils";
-import { ResizeHandleAxis } from "./gridLayoutPropTypes";
+import type { ResizeHandleAxis } from "./gridLayoutPropTypes";
+import { isEqual } from "lodash";
export type DragHandleName = "w" | "e" | "nw" | "ne" | "sw" | "se";
type NamePos = "top" | "bottom" | "bottomInside";
@@ -273,7 +275,7 @@ export const CompSelectionWrapper = React.memo((props: {
}
setHover(true);
},
- [setHover]
+ [nameDivRef.current, setHover]
);
const onMouseOut = useCallback(
(e: MouseEvent) => {
@@ -287,39 +289,57 @@ export const CompSelectionWrapper = React.memo((props: {
}
setHover(false);
},
- [setHover]
+ [nameDivRef.current, setHover]
);
- const selectableDivProps = props.isSelectable
- ? {
- onMouseOver,
- onMouseOut,
- onClick: props.onClick,
- $hover: hover || undefined,
- $showDashLine: editorState.showGridLines() || props.hidden,
- $isSelected: props.isSelected,
- $isHidden: props.hidden,
- }
- : {
- $hover: undefined,
- $showDashLine: false,
- $isSelected: false,
- $isHidden: false,
- };
+ const selectableDivProps = useMemo(() => {
+ return props.isSelectable
+ ? {
+ onMouseOver,
+ onMouseOut,
+ onClick: props.onClick,
+ $hover: hover || undefined,
+ $showDashLine: editorState.showGridLines() || props.hidden,
+ $isSelected: props.isSelected,
+ $isHidden: props.hidden,
+ }
+ : {
+ $hover: undefined,
+ $showDashLine: false,
+ $isSelected: false,
+ $isHidden: false,
+ };
+ }, [
+ hover,
+ props.hidden,
+ props.isSelected,
+ props.isSelectable,
+ ]);
+
+ const zIndex = useMemo(() => {
+ return props.isSelected
+ ? Layers.compSelected
+ : hover
+ ? Layers.compHover
+ : props.hidden
+ ? Layers.compHidden
+ : undefined;
+ }, [
+ hover,
+ props.hidden,
+ props.isSelected
+ ]);
- const zIndex = props.isSelected
- ? Layers.compSelected
- : hover
- ? Layers.compHover
- : props.hidden
- ? Layers.compHidden
- : undefined;
+ const needResizeDetector = useMemo(() => {
+ return props.autoHeight && !props.placeholder;
+ }, [props.autoHeight, props.placeholder]);
- const needResizeDetector = props.autoHeight && !props.placeholder;
const { ref: wrapperRef } = useResizeDetector({
onResize: props.onWrapperResize,
handleHeight: needResizeDetector,
handleWidth: false,
+ refreshMode: 'debounce',
+ refreshRate: 100,
});
// log.debug("CompSelectionWrapper. name: ", props.name, " zIndex: ", zIndex);
const { nameConfig, resizeIconSize } = props;
@@ -369,8 +389,18 @@ export const CompSelectionWrapper = React.memo((props: {
{!needResizeDetector && props.children}
{needResizeDetector && (
@@ -380,4 +410,4 @@ export const CompSelectionWrapper = React.memo((props: {
);
-});
+}, (prev, next) => isEqual(prev, next));
diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx
index 963c1c056..fcfdeb5ac 100644
--- a/client/packages/lowcoder/src/layout/gridItem.tsx
+++ b/client/packages/lowcoder/src/layout/gridItem.tsx
@@ -104,6 +104,19 @@ const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boole
*/
export const GridItem = React.memo((props: GridItemProps) => {
const position = useMemo(() =>{
+ console.log('calculateGridItemPOistion', JSON.stringify({
+ name: props.name,
+ margin: props.margin,
+ containerPadding: props.containerPadding,
+ containerWidth: props.containerWidth,
+ cols: props.cols,
+ rowHeight: props.rowHeight,
+ maxRows: props.maxRows,
+ x: props.x,
+ y: props.y,
+ w: props.w,
+ h: props.h,
+ }));
return calcGridItemPosition({
margin: props.margin,
containerPadding: props.containerPadding,
@@ -113,8 +126,8 @@ export const GridItem = React.memo((props: GridItemProps) => {
maxRows: props.maxRows,
}, props.x, props.y, props.w, props.h)},
[
- props.margin,
- props.containerPadding,
+ JSON.stringify(props.margin),
+ JSON.stringify(props.containerPadding),
props.containerWidth,
props.cols,
props.rowHeight,
@@ -123,7 +136,6 @@ export const GridItem = React.memo((props: GridItemProps) => {
props.y,
props.w,
props.h,
- calcGridItemPosition,
]
);
@@ -230,8 +242,8 @@ export const GridItem = React.memo((props: GridItemProps) => {
y: yy,
});
}, [
- resizing,
- dragging,
+ JSON.stringify(resizing),
+ JSON.stringify(dragging),
props.cols,
props.maxRows,
props.x,
@@ -417,7 +429,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
itemHeightRef.current = height;
}
adjustWrapperHeight(width, height);
- }, [itemHeightRef, adjustWrapperHeight]);
+ }, [itemHeightRef.current, adjustWrapperHeight]);
/**
* re-calculate the occupied gird-cells.
@@ -427,7 +439,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
*/
const onWrapperSizeChange = useCallback(() => {
adjustWrapperHeight(undefined, itemHeightRef.current);
- }, [itemHeightRef, adjustWrapperHeight]);
+ }, [itemHeightRef.current, adjustWrapperHeight]);
const mixinChildWrapper = useCallback((child: React.ReactElement): React.ReactElement => {
const {
@@ -535,6 +547,21 @@ export const GridItem = React.memo((props: GridItemProps) => {
const pos = useMemo(calcPosition, [calcPosition]);
+ const transform = useMemo(() => {
+ return setTransform(
+ pos,
+ props.name,
+ props.autoHeight,
+ props.hidden,
+ Boolean(draggingUtils.isDragging())
+ )
+ }, [
+ JSON.stringify(pos),
+ props.name,
+ props.autoHeight,
+ props.hidden
+ ]);
+
const render = useMemo(() => {
let child = React.Children.only(children);
// Create the child element. We clone the existing element but modify its className and style.
@@ -563,13 +590,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
cssTransforms: true,
}),
style: {
- ...setTransform(
- pos,
- props.name,
- props.autoHeight,
- props.hidden,
- Boolean(draggingUtils.isDragging())
- ),
+ ...transform,
opacity: layoutHide ? 0 : undefined,
pointerEvents: layoutHide ? "none" : "auto",
},
@@ -580,11 +601,12 @@ export const GridItem = React.memo((props: GridItemProps) => {
newChild = mixinDraggable(newChild, isDraggable);
return newChild;
}, [
- pos,
+ JSON.stringify(transform),
+ JSON.stringify(pos),
children,
elementRef,
- resizing,
- dragging,
+ Boolean(resizing),
+ Boolean(dragging),
isDraggable,
layoutHide,
zIndex,
@@ -593,8 +615,6 @@ export const GridItem = React.memo((props: GridItemProps) => {
props.className,
props.style,
props.static,
- props.autoHeight,
- props.hidden,
setTransform,
mixinChildWrapper,
mixinResizable,
diff --git a/client/packages/lowcoder/src/layout/gridLayout.tsx b/client/packages/lowcoder/src/layout/gridLayout.tsx
index 0b88786c2..0b25763b7 100644
--- a/client/packages/lowcoder/src/layout/gridLayout.tsx
+++ b/client/packages/lowcoder/src/layout/gridLayout.tsx
@@ -1,8 +1,8 @@
import clsx from "clsx";
import { colord } from "colord";
-import { UICompType } from "comps/uiCompRegistry";
+import type { UICompType } from "comps/uiCompRegistry";
import { ModulePrimaryColor, PrimaryColor } from "constants/style";
-import _ from "lodash";
+import _, { isEqual } from "lodash";
import log from "loglevel";
import React, { DragEvent, DragEventHandler, MouseEventHandler, ReactElement } from "react";
import ReactResizeDetector from "react-resize-detector";
@@ -21,7 +21,7 @@ import {
import { draggingUtils } from "./draggingUtils";
import { FlyOverInfo, FlyStartInfo } from "./flyInfo";
import { GridItem } from "./gridItem";
-import { GridLayoutProps } from "./gridLayoutPropTypes";
+import type { GridLayoutProps } from "./gridLayoutPropTypes";
import { GridLines } from "./gridLines";
import { changeItemOp, deleteItemOp, LayoutOp, renameItemOp } from "./layoutOp";
import { getUILayout, LayoutOps, layoutOpUtils } from "./layoutOpUtils";
@@ -1102,7 +1102,7 @@ const LayoutContainer = styled.div<{
}`}
`;
-export const ReactGridLayout = React.memo(GridLayout);
+export const ReactGridLayout = React.memo(GridLayout, (prev, next) => isEqual(prev, next));
function moveOrResize(
e: React.KeyboardEvent,
diff --git a/client/packages/lowcoder/src/layout/gridLines.tsx b/client/packages/lowcoder/src/layout/gridLines.tsx
index c7cfc1366..33b5eefec 100644
--- a/client/packages/lowcoder/src/layout/gridLines.tsx
+++ b/client/packages/lowcoder/src/layout/gridLines.tsx
@@ -1,6 +1,7 @@
import { CSSProperties } from "react";
import { calcGridColWidth, PositionParams } from "./calculateUtils";
import { Position, setTransform } from "./utils";
+import React from "react";
interface GridLineProps {
position: Position;
@@ -19,10 +20,10 @@ function setBackgroundProps(positionParams: PositionParams, lineColor: string):
};
}
-export function GridLines(props: GridLineProps) {
+export const GridLines = React.memo((props: GridLineProps) => {
const style = {
...setTransform(props.position),
...setBackgroundProps(props.positionParams, props.lineColor),
};
return
;
-}
+})
diff --git a/client/packages/lowcoder/src/layout/layoutOpUtils.tsx b/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
index 8d10aa459..cde38066a 100644
--- a/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
+++ b/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
@@ -92,6 +92,7 @@ export namespace layoutOpUtils {
}
function reduce(layout: Layout, op: LayoutOp, stickyItemMap?: Record>): Layout {
+ // console.log(op.type);
let newLayout = layout;
switch (op.type) {
case LayoutOpTypes.CHANGE_ITEM: {
@@ -146,6 +147,7 @@ export let getUILayout = (
ops: LayoutOps | undefined,
setHiddenCompHeightZero: boolean = false
): Layout => {
+ // console.log('layout->before', changedHs, layout);
// log.log("getUILayout. layout: ", layout, " extraLayout: ", extraLayout, " changedHs: ", changedHs, " ops: ", ops);
const stickyItemMap = getStickyItemMap(layout);
const hiddenItemHeight = _.fromPairs(
@@ -163,7 +165,9 @@ export let getUILayout = (
realOps.forEach((op) => {
layout = reduce(layout, op, stickyItemMap);
});
+ // console.log('layout->after', layout);
layout = cascade(layout);
+ // console.log('layout->after->2', layout);
if (!setHiddenCompHeightZero) {
const recoverHiddenOps = _.toPairs(hiddenItemHeight).map(([i, h]) => changeItemOp(i, { h }));
recoverHiddenOps.forEach((op) => {
diff --git a/client/packages/lowcoder/src/layout/utils.ts b/client/packages/lowcoder/src/layout/utils.ts
index 44a77d15e..420ba7fe1 100644
--- a/client/packages/lowcoder/src/layout/utils.ts
+++ b/client/packages/lowcoder/src/layout/utils.ts
@@ -207,6 +207,7 @@ export function setTransform(
hidden?: boolean,
isDragging?: boolean,
): Record {
+ console.log(name, {top, left, width, height })
// Replace unitless items with px
const translate = `translate(${left}px,${top}px)`;
function containsChart(str:string) {
From 2ee3c48f86d2fd669cfbdb155bdf0591295f375a Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Thu, 20 Feb 2025 00:16:26 +0500
Subject: [PATCH 045/124] reduced actions for global variables
---
.../lowcoder/src/comps/generators/hookToComp.tsx | 8 +++++---
.../lowcoder/src/comps/hooks/screenInfoComp.tsx | 8 ++++++--
.../lowcoder/src/comps/hooks/themeComp.tsx | 15 +++++++++------
3 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/generators/hookToComp.tsx b/client/packages/lowcoder/src/comps/generators/hookToComp.tsx
index 40e92bf4b..68dce72cd 100644
--- a/client/packages/lowcoder/src/comps/generators/hookToComp.tsx
+++ b/client/packages/lowcoder/src/comps/generators/hookToComp.tsx
@@ -36,9 +36,11 @@ export function hookToStateComp(useHookFn: () => JSONObject) {
const hookValue = useHookFn();
const stateValue = useMemo(() => comp.children.stateValue.getView(), [comp.children.stateValue]);
- if (!isEqual(hookValue, stateValue)) {
- comp.children.stateValue.dispatchChangeValueAction(hookValue);
- }
+ useEffect(() => {
+ if (!isEqual(hookValue, stateValue)) {
+ comp.children.stateValue.dispatchChangeValueAction(hookValue);
+ }
+ }, [hookValue, stateValue]);
return null;
}
);
diff --git a/client/packages/lowcoder/src/comps/hooks/screenInfoComp.tsx b/client/packages/lowcoder/src/comps/hooks/screenInfoComp.tsx
index 61652fb0f..0d4fb1df0 100644
--- a/client/packages/lowcoder/src/comps/hooks/screenInfoComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/screenInfoComp.tsx
@@ -22,7 +22,9 @@ type ScreenInfo = {
export function useScreenInfo() {
const canvasContainer = document.getElementById(CanvasContainerID);
const canvas = document.getElementsByClassName('lowcoder-app-canvas')?.[0];
- const canvasWidth = canvasContainer?.clientWidth || canvas?.clientWidth;
+ const canvasWidth = useMemo(() => {
+ return canvasContainer?.clientWidth || canvas?.clientWidth;
+ }, [canvasContainer?.clientWidth, canvas?.clientWidth]);
const getDeviceType = (width: number) => {
if (width < 768) return ScreenTypes.Mobile;
@@ -71,7 +73,9 @@ export function useScreenInfo() {
}, [ updateScreenInfo ])
useEffect(() => {
- updateScreenInfo();
+ if (canvasWidth) {
+ updateScreenInfo();
+ }
}, [canvasWidth]);
return screenInfo;
diff --git a/client/packages/lowcoder/src/comps/hooks/themeComp.tsx b/client/packages/lowcoder/src/comps/hooks/themeComp.tsx
index 7fdff5ed0..8e11723fa 100644
--- a/client/packages/lowcoder/src/comps/hooks/themeComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/themeComp.tsx
@@ -64,12 +64,15 @@ let ThemeTempComp = withViewFn(
return stateValue;
}, [themeList, currentTheme, stateValue])
- if (!isEqual(themeValue, stateValue)) {
- comp.children.stateValue.dispatchChangeValueAction({
- ...exposingTheme(currentTheme),
- allThemes: themeList.map((t) => exposingTheme(t)),
- })
- }
+ useEffect(() => {
+ if (!isEqual(themeValue, stateValue)) {
+ comp.children.stateValue.dispatchChangeValueAction({
+ ...exposingTheme(currentTheme),
+ allThemes: themeList.map((t) => exposingTheme(t)),
+ })
+ }
+ }, [themeValue, stateValue]);
+
return null;
}
);
From cf5f7a95471949ef957d213da8da542470f77082 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Thu, 20 Feb 2025 00:17:14 +0500
Subject: [PATCH 046/124] fixed console errors from avatar comp
---
.../packages/lowcoder/src/comps/comps/avatar.tsx | 14 +++++++-------
.../lowcoder/src/comps/comps/avatarGroup.tsx | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/avatar.tsx b/client/packages/lowcoder/src/comps/comps/avatar.tsx
index fc7f6f433..a1da4984a 100644
--- a/client/packages/lowcoder/src/comps/comps/avatar.tsx
+++ b/client/packages/lowcoder/src/comps/comps/avatar.tsx
@@ -41,12 +41,12 @@ const AvatarWrapper = styled(Avatar) props.$cursorPointer ? 'pointer' : ''};
`;
-const Wrapper = styled.div <{ iconSize: number, labelPosition: string,$style: AvatarContainerStyleType}>`
+const Wrapper = styled.div <{ $iconSize: number, $labelPosition: string,$style: AvatarContainerStyleType}>`
display: flex;
width: 100%;
height: 100%;
align-items: center;
-flex-direction: ${(props) => props.labelPosition === 'left' ? 'row' : 'row-reverse'};
+flex-direction: ${(props) => props.$labelPosition === 'left' ? 'row' : 'row-reverse'};
${(props) => {
return (
props.$style && {
@@ -57,14 +57,14 @@ ${(props) => {
}}
`
-const LabelWrapper = styled.div<{ iconSize: number, alignmentPosition: string }>`
-width: calc(100% - ${(props) => props.iconSize}px);
+const LabelWrapper = styled.div<{ $iconSize: number, $alignmentPosition: string }>`
+width: calc(100% - ${(props) => props.$iconSize}px);
display: flex;
padding-left: 5px;
padding-right: 5px;
flex-direction: column;
justify-content: flex-end;
-align-items: ${(props) => props.alignmentPosition === 'left' ? 'flex-start' : 'flex-end'};
+align-items: ${(props) => props.$alignmentPosition === 'left' ? 'flex-start' : 'flex-end'};
`
const LabelSpan = styled.span<{ $style:AvatarLabelStyleType }>`
max-width: 100%;
@@ -166,7 +166,7 @@ const AvatarView = (props: RecordConstructorToView) => {
disabled={!props.enableDropdownMenu}
dropdownRender={() => menu}
>
-
+
) => {
{title.value}
-
+
{props.avatarLabel.value}
{props.avatarCatption.value}
diff --git a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx
index cba007ee1..4cc2567c6 100644
--- a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx
+++ b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx
@@ -111,11 +111,11 @@ const AvatarGroupView = (props: RecordConstructorToView & {
alignment={props.alignment}
>
{
-
+
{
props.avatars.map((item, index) => {
return (
-
+
Date: Thu, 20 Feb 2025 00:23:53 +0500
Subject: [PATCH 047/124] reduce delay in height calculation
---
.../lowcoder/src/comps/utils/useCompInstance.tsx | 6 +++---
.../lowcoder/src/layout/compSelectionWrapper.tsx | 3 ++-
client/packages/lowcoder/src/layout/gridItem.tsx | 13 -------------
client/packages/lowcoder/src/layout/utils.ts | 1 -
4 files changed, 5 insertions(+), 18 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/utils/useCompInstance.tsx b/client/packages/lowcoder/src/comps/utils/useCompInstance.tsx
index 812ae7389..c15e7c0db 100644
--- a/client/packages/lowcoder/src/comps/utils/useCompInstance.tsx
+++ b/client/packages/lowcoder/src/comps/utils/useCompInstance.tsx
@@ -304,11 +304,11 @@ export function useCompInstance(
let updateHandler = () => setComp(container.comp);
- if (UPDATE_ROOT_VIEW_DEBOUNCE > 0) {
+ // if (UPDATE_ROOT_VIEW_DEBOUNCE > 0) {
updateHandler = debounce(() => {
setComp(container.comp);
- }, UPDATE_ROOT_VIEW_DEBOUNCE);
- }
+ }, 50 /* UPDATE_ROOT_VIEW_DEBOUNCE */);
+ // }
const finalHandlers = [...(handlers || []), updateHandler];
finalHandlers.forEach((handler) => container.addChangeListener(handler));
diff --git a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
index 64765b9ba..d9eb475d4 100644
--- a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
+++ b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
@@ -343,6 +343,7 @@ export const CompSelectionWrapper = React.memo((props: {
});
// log.debug("CompSelectionWrapper. name: ", props.name, " zIndex: ", zIndex);
const { nameConfig, resizeIconSize } = props;
+
return (
diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx
index fcfdeb5ac..3594f3acd 100644
--- a/client/packages/lowcoder/src/layout/gridItem.tsx
+++ b/client/packages/lowcoder/src/layout/gridItem.tsx
@@ -104,19 +104,6 @@ const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boole
*/
export const GridItem = React.memo((props: GridItemProps) => {
const position = useMemo(() =>{
- console.log('calculateGridItemPOistion', JSON.stringify({
- name: props.name,
- margin: props.margin,
- containerPadding: props.containerPadding,
- containerWidth: props.containerWidth,
- cols: props.cols,
- rowHeight: props.rowHeight,
- maxRows: props.maxRows,
- x: props.x,
- y: props.y,
- w: props.w,
- h: props.h,
- }));
return calcGridItemPosition({
margin: props.margin,
containerPadding: props.containerPadding,
diff --git a/client/packages/lowcoder/src/layout/utils.ts b/client/packages/lowcoder/src/layout/utils.ts
index 420ba7fe1..44a77d15e 100644
--- a/client/packages/lowcoder/src/layout/utils.ts
+++ b/client/packages/lowcoder/src/layout/utils.ts
@@ -207,7 +207,6 @@ export function setTransform(
hidden?: boolean,
isDragging?: boolean,
): Record {
- console.log(name, {top, left, width, height })
// Replace unitless items with px
const translate = `translate(${left}px,${top}px)`;
function containsChart(str:string) {
From 71b40545a68240c810c05cb099f66870f4424986 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Thu, 20 Feb 2025 00:24:19 +0500
Subject: [PATCH 048/124] remove lazy loading from comps
---
client/packages/lowcoder/src/comps/index.tsx | 328 ++++++++-----------
1 file changed, 130 insertions(+), 198 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx
index 4acecfd14..f00ebaa5a 100644
--- a/client/packages/lowcoder/src/comps/index.tsx
+++ b/client/packages/lowcoder/src/comps/index.tsx
@@ -128,6 +128,67 @@ import { TextComp } from "./comps/textComp";
import { SelectComp } from "./comps/selectInputComp/selectComp";
import { InputComp } from "./comps/textInputComp/inputComp";
import { TextAreaComp } from "./comps/textInputComp/textAreaComp";
+import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp";
+import { AvatarComp } from "./comps/avatar";
+import { AvatarGroupComp } from "./comps/avatarGroup";
+import { DropdownComp } from "./comps/buttonComp/dropdownComp";
+import { FloatButtonComp } from "./comps/buttonComp/floatButtonComp";
+import { LinkComp } from "./comps/buttonComp/linkComp";
+import { ScannerComp } from "./comps/buttonComp/scannerComp";
+import { ToggleButtonComp } from "./comps/buttonComp/toggleButtonComp";
+import { CarouselComp } from "./comps/carouselComp";
+import { CommentComp } from "./comps/commentComp/commentComp";
+import { CardComp } from "./comps/containerComp/cardComp";
+import { CustomComp } from "./comps/customComp/customComp";
+import { DatePickerComp, DateRangeComp } from "./comps/dateComp/dateComp";
+import { TimePickerComp, TimeRangeComp } from "./comps/dateComp/timeComp";
+import { DividerComp } from "./comps/dividerComp";
+import { FileComp } from "./comps/fileComp/fileComp";
+import { FileViewerComp } from "./comps/fileViewerComp";
+import { FormComp } from "./comps/formComp/formComp";
+import { IconComp } from "./comps/iconComp";
+import { IFrameComp } from "./comps/iframeComp";
+import { ImageComp } from "./comps/imageComp";
+import { JsonEditorComp } from "./comps/jsonComp/jsonEditorComp";
+import { JsonExplorerComp } from "./comps/jsonComp/jsonExplorerComp";
+import { JsonLottieComp } from "./comps/jsonComp/jsonLottieComp";
+import { JsonSchemaFormComp } from "./comps/jsonSchemaFormComp/jsonSchemaFormComp";
+import { ListViewComp, GridComp } from "./comps/listViewComp";
+import { AudioComp } from "./comps/mediaComp/audioComp";
+import { ColorPickerComp } from "./comps/mediaComp/colorPickerComp";
+import { VideoComp } from "./comps/mediaComp/videoComp";
+import { ControlButton } from "./comps/meetingComp/controlButton";
+import { NavComp } from "./comps/navComp/navComp";
+import { NumberInputComp } from "./comps/numberInputComp/numberInputComp";
+import { RangeSliderComp } from "./comps/numberInputComp/rangeSliderComp";
+import { SliderComp } from "./comps/numberInputComp/sliderComp";
+import { PageLayoutComp } from "./comps/containerComp/pageLayoutComp";
+import { ProgressCircleComp } from "./comps/progressCircleComp";
+import { ProgressComp } from "./comps/progressComp";
+import { QRCodeComp } from "./comps/qrCodeComp";
+import { RatingComp } from "./comps/ratingComp";
+import { ResponsiveLayoutComp } from "./comps/responsiveLayout";
+import { RichTextEditorComp } from "./comps/richTextEditorComp";
+import { CascaderWithDefault } from "./comps/selectInputComp/cascaderComp";
+import { CheckboxComp } from "./comps/selectInputComp/checkboxComp";
+import { MultiSelectComp } from "./comps/selectInputComp/multiSelectComp";
+import { RadioComp } from "./comps/selectInputComp/radioComp";
+import { SegmentedControlComp } from "./comps/selectInputComp/segmentedControl";
+import { StepComp } from "./comps/selectInputComp/stepControl";
+import { ShapeComp } from "./comps/shapeComp/shapeComp";
+import { SignatureComp } from "./comps/signatureComp";
+import { SplitLayoutComp } from "./comps/splitLayout";
+import { SwitchComp } from "./comps/switchComp";
+import { MentionComp } from "./comps/textInputComp/mentionComp";
+import { PasswordComp } from "./comps/textInputComp/passwordComp";
+import { TimeLineComp } from "./comps/timelineComp/timelineComp";
+import { TimerComp } from "./comps/timerComp";
+import { TourComp } from "./comps/tourComp/tourComp";
+import { transferComp } from "./comps/transferComp";
+import { TreeComp } from "./comps/treeComp/treeComp";
+import { TreeSelectComp } from "./comps/treeComp/treeSelectComp";
+import { DrawerComp } from "./hooks/drawerComp";
+import { ModalComp } from "./hooks/modalComp";
type Registry = {
[key in UICompType]?: UICompManifest;
@@ -407,9 +468,7 @@ export var uiCompMap: Registry = {
categories: ["dashboards"],
icon: TimeLineCompIcon,
keywords: trans("uiComp.timelineCompKeywords"),
- lazyLoad: true,
- compName: "TimeLineComp",
- compPath: "comps/timelineComp/timelineComp",
+ comp: TimeLineComp,
layoutInfo: {
w: 12,
h: 40,
@@ -425,9 +484,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: ResponsiveLayoutCompIcon,
keywords: trans("uiComp.responsiveLayoutCompKeywords"),
- lazyLoad: true,
- compName: "ResponsiveLayoutComp",
- compPath: "comps/responsiveLayout/index",
+ comp: ResponsiveLayoutComp,
withoutLoading: true,
layoutInfo: {
w: 24,
@@ -442,9 +499,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: PageLayoutCompIcon,
keywords: trans("uiComp.pageLayoutCompKeywords"),
- lazyLoad: true,
- compName: "PageLayoutComp",
- compPath: "comps/containerComp/pageLayoutComp",
+ comp: PageLayoutComp,
withoutLoading: true,
layoutInfo: {
w: 12,
@@ -477,9 +532,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: SplitLayoutCompIcon,
keywords: trans("uiComp.splitLayoutCompKeywords"),
- lazyLoad: true,
- compName: "SplitLayoutComp",
- compPath: "comps/splitLayout/index",
+ comp: SplitLayoutComp,
withoutLoading: true,
layoutInfo: {
w: 24,
@@ -494,9 +547,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: FloatingTextCompIcon,
keywords: trans("uiComp.floatTextContainerCompKeywords"),
- lazyLoad: true,
- compName: "ContainerComp",
- compPath: "comps/containerComp/textContainerComp",
+ comp: ContainerComp,
withoutLoading: true,
layoutInfo: {
w: 12,
@@ -514,9 +565,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.cardCompDesc"),
categories: ["layout"],
keywords: trans("uiComp.cardCompKeywords"),
- lazyLoad: true,
- compName: "CardComp",
- compPath: "comps/containerComp/cardComp",
+ comp: CardComp,
layoutInfo: {
h: 44,
w: 6,
@@ -545,9 +594,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: CollapsibleContainerCompIcon,
keywords: trans("uiComp.collapsibleContainerCompKeywords"),
- lazyLoad: true,
- compName: "ContainerComp",
- compPath: "comps/containerComp/containerComp",
+ comp: ContainerComp,
withoutLoading: true,
layoutInfo: {
w: 12,
@@ -582,9 +629,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.listViewCompDesc"),
categories: ["layout"],
keywords: trans("uiComp.listViewCompKeywords"),
- lazyLoad: true,
- compName: "ListViewComp",
- compPath: "comps/listViewComp/index",
+ comp: ListViewComp,
layoutInfo: {
w: 12,
h: 40,
@@ -600,9 +645,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.gridCompDesc"),
categories: ["layout"],
keywords: trans("uiComp.gridCompKeywords"),
- lazyLoad: true,
- compName: "GridComp",
- compPath: "comps/listViewComp/index",
+ comp: GridComp,
layoutInfo: {
w: 12,
h: 40,
@@ -618,9 +661,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.modalCompDesc"),
categories: ["layout"],
keywords: trans("uiComp.modalCompKeywords"),
- lazyLoad: true,
- compName: "ModalComp",
- compPath: "hooks/modalComp",
+ comp: ModalComp,
withoutLoading: true,
},
drawer: {
@@ -630,9 +671,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: DrawerCompIcon,
keywords: trans("uiComp.drawerCompKeywords"),
- lazyLoad: true,
- compName: "DrawerComp",
- compPath: "hooks/drawerComp",
+ comp: DrawerComp,
withoutLoading: true,
},
divider: {
@@ -642,9 +681,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: DividerCompIcon,
keywords: trans("uiComp.dividerCompKeywords"),
- lazyLoad: true,
- compName: "DividerComp",
- compPath: "comps/dividerComp",
+ comp: DividerComp,
layoutInfo: {
w: 12,
h: 1,
@@ -657,9 +694,7 @@ export var uiCompMap: Registry = {
icon: NavComIcon,
categories: ["layout"],
keywords: trans("uiComp.navigationCompKeywords"),
- lazyLoad: true,
- compName: "NavComp",
- compPath: "comps/navComp/navComp",
+ comp: NavComp,
layoutInfo: {
w: 24,
h: 5,
@@ -672,9 +707,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: StepCompIcon,
keywords: trans("uiComp.stepControlCompKeywords"),
- lazyLoad: true,
- compName: "StepComp",
- compPath: "comps/selectInputComp/stepControl",
+ comp: StepComp,
layoutInfo: {
w: 19,
h: 6,
@@ -687,9 +720,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: CascaderCompIcon,
keywords: trans("uiComp.cascaderCompKeywords"),
- lazyLoad: true,
- compName: "CascaderWithDefault",
- compPath: "comps/selectInputComp/cascaderComp",
+ comp: CascaderWithDefault,
layoutInfo: {
w: 9,
h: 5,
@@ -702,9 +733,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: LinkCompIcon,
keywords: trans("uiComp.linkCompKeywords"),
- lazyLoad: true,
- compName: "LinkComp",
- compPath: "comps/buttonComp/linkComp",
+ comp: LinkComp,
layoutInfo: {
w: 6,
h: 5,
@@ -717,9 +746,7 @@ export var uiCompMap: Registry = {
categories: ["layout"],
icon: FloatingButtonCompIcon,
keywords: trans("uiComp.floatButtonCompKeywords"),
- lazyLoad: true,
- compName: "FloatButtonComp",
- compPath: "comps/buttonComp/floatButtonComp",
+ comp: FloatButtonComp,
layoutInfo: {
w: 1,
h: 1,
@@ -749,9 +776,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.timerCompDesc"),
categories: ["scheduling", "projectmanagement"],
keywords: trans("uiComp.timerCompKeywords"),
- lazyLoad: true,
- compName: "TimerComp",
- compPath: "comps/timerComp",
+ comp: TimerComp,
layoutInfo: {
h: 14,
w: 6,
@@ -807,9 +832,7 @@ export var uiCompMap: Registry = {
categories: ["collaboration"],
icon: AvatarCompIcon,
keywords: trans("uiComp.avatarCompKeywords"),
- lazyLoad: true,
- compName: "AvatarComp",
- compPath: "comps/avatar",
+ comp: AvatarComp,
layoutInfo: {
w: 6,
h: 6,
@@ -823,9 +846,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.avatarGroupCompDesc"),
categories: ["collaboration"],
keywords: trans("uiComp.avatarGroupCompKeywords"),
- lazyLoad: true,
- compName: "AvatarGroupComp",
- compPath: "comps/avatarGroup",
+ comp: AvatarGroupComp,
withoutLoading: true,
layoutInfo: {
w: 6,
@@ -840,9 +861,7 @@ export var uiCompMap: Registry = {
categories: ["collaboration"],
icon: CommentCompIcon,
keywords: trans("uiComp.commentCompKeywords"),
- lazyLoad: true,
- compName: "CommentComp",
- compPath: "comps/commentComp/commentComp",
+ comp: CommentComp,
layoutInfo: {
w: 12,
h: 55,
@@ -855,9 +874,7 @@ export var uiCompMap: Registry = {
categories: ["collaboration"],
icon: MentionCompIcon,
keywords: trans("uiComp.mentionCompKeywords"),
- lazyLoad: true,
- compName: "MentionComp",
- compPath: "comps/textInputComp/mentionComp",
+ comp: MentionComp,
},
// Forms
@@ -869,9 +886,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: FormCompIcon,
keywords: trans("uiComp.formCompKeywords"),
- lazyLoad: true,
- compName: "FormComp",
- compPath: "comps/formComp/formComp",
+ comp: FormComp,
withoutLoading: true,
layoutInfo: {
w: 12,
@@ -889,9 +904,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: JsonFormCompIcon,
keywords: trans("uiComp.jsonSchemaFormCompKeywords"),
- lazyLoad: true,
- compName: "JsonSchemaFormComp",
- compPath: "comps/jsonSchemaFormComp/jsonSchemaFormComp",
+ comp: JsonSchemaFormComp,
layoutInfo: {
w: 12,
h: 50,
@@ -904,9 +917,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: JsonEditorCompIcon,
keywords: trans("uiComp.jsonEditorCompKeywords"),
- lazyLoad: true,
- compName: "JsonEditorComp",
- compPath: "comps/jsonComp/jsonEditorComp",
+ comp: JsonEditorComp,
layoutInfo: {
w: 12,
h: 50,
@@ -919,9 +930,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: JsonExplorerCompIcon,
keywords: trans("uiComp.jsonExplorerCompKeywords"),
- lazyLoad: true,
- compName: "JsonExplorerComp",
- compPath: "comps/jsonComp/jsonExplorerComp",
+ comp: JsonExplorerComp,
layoutInfo: {
w: 12,
h: 50,
@@ -934,9 +943,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.richTextEditorCompDesc"),
icon: RichTextEditorCompIcon,
keywords: trans("uiComp.richTextEditorCompKeywords"),
- lazyLoad: true,
- compName: "RichTextEditorComp",
- compPath: "comps/richTextEditorComp",
+ comp: RichTextEditorComp,
layoutInfo: {
w: 12,
h: 50,
@@ -962,9 +969,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: PasswordCompIcon,
keywords: trans("uiComp.passwordCompKeywords"),
- lazyLoad: true,
- compName: "PasswordComp",
- compPath: "comps/textInputComp/passwordComp",
+ comp: PasswordComp,
layoutInfo: {
w: 6,
h: 6,
@@ -977,9 +982,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: NumberInputCompIcon,
keywords: trans("uiComp.numberInputCompKeywords"),
- lazyLoad: true,
- compName: "NumberInputComp",
- compPath: "comps/numberInputComp/numberInputComp",
+ comp: NumberInputComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1007,9 +1010,10 @@ export var uiCompMap: Registry = {
keywords: cnchar
.spell(trans("uiComp.autoCompleteCompName"), "first", "low")
.toString(),
- lazyLoad: true,
- compName: "AutoCompleteComp",
- compPath: "comps/autoCompleteComp/autoCompleteComp",
+ comp: AutoCompleteComp,
+ // lazyLoad: true,
+ // compName: "AutoCompleteComp",
+ // compPath: "comps/autoCompleteComp/autoCompleteComp",
layoutInfo: {
w: 6,
h: 5,
@@ -1022,9 +1026,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: SwitchCompIcon,
keywords: trans("uiComp.switchCompKeywords"),
- lazyLoad: true,
- compName: "SwitchComp",
- compPath: "comps/switchComp",
+ comp: SwitchComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1037,9 +1039,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: CheckboxCompIcon,
keywords: trans("uiComp.checkboxCompKeywords"),
- lazyLoad: true,
- compName: "CheckboxComp",
- compPath: "comps/selectInputComp/checkboxComp",
+ comp: CheckboxComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1052,9 +1052,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: RadioCompIcon,
keywords: trans("uiComp.radioCompKeywords"),
- lazyLoad: true,
- compName: "RadioComp",
- compPath: "comps/selectInputComp/radioComp",
+ comp: RadioComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1067,9 +1065,7 @@ export var uiCompMap: Registry = {
categories: ["forms", "scheduling"],
icon: DateCompIcon,
keywords: trans("uiComp.dateCompKeywords"),
- lazyLoad: true,
- compName: "DatePickerComp",
- compPath: "comps/dateComp/dateComp",
+ comp: DatePickerComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1082,9 +1078,7 @@ export var uiCompMap: Registry = {
categories: ["forms", "scheduling"],
icon: DateRangeCompIcon,
keywords: trans("uiComp.dateRangeCompKeywords"),
- lazyLoad: true,
- compName: "DateRangeComp",
- compPath: "comps/dateComp/dateComp",
+ comp: DateRangeComp,
layoutInfo: {
w: 12,
h: 6,
@@ -1097,9 +1091,7 @@ export var uiCompMap: Registry = {
categories: ["forms", "scheduling"],
icon: TimeCompIcon,
keywords: trans("uiComp.timeCompKeywords"),
- lazyLoad: true,
- compName: "TimePickerComp",
- compPath: "comps/dateComp/timeComp",
+ comp: TimePickerComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1112,9 +1104,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.timeRangeCompDesc"),
icon: TimeRangeCompIcon,
keywords: trans("uiComp.timeRangeCompKeywords"),
- lazyLoad: true,
- compName: "TimeRangeComp",
- compPath: "comps/dateComp/timeComp",
+ comp: TimeRangeComp,
layoutInfo: {
w: 12,
h: 6,
@@ -1128,9 +1118,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: SliderCompIcon,
keywords: trans("uiComp.sliderCompKeywords"),
- lazyLoad: true,
- compName: "SliderComp",
- compPath: "comps/numberInputComp/sliderComp",
+ comp: SliderComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1143,9 +1131,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: RangeSliderCompIcon,
keywords: trans("uiComp.rangeSliderCompKeywords"),
- lazyLoad: true,
- compName: "RangeSliderComp",
- compPath: "comps/numberInputComp/rangeSliderComp",
+ comp: RangeSliderComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1173,9 +1159,7 @@ export var uiCompMap: Registry = {
categories: ["forms", "collaboration"],
icon: IconButtonCompIcon,
keywords: trans("uiComp.meetingCompKeywords"),
- lazyLoad: true,
- compName: "ControlButton",
- compPath: "comps/meetingComp/controlButton",
+ comp: ControlButton,
withoutLoading: true,
layoutInfo: {
w: 3,
@@ -1189,9 +1173,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: DropdownCompIcon,
keywords: trans("uiComp.dropdownCompKeywords"),
- lazyLoad: true,
- compName: "DropdownComp",
- compPath: "comps/buttonComp/dropdownComp",
+ comp: DropdownComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1204,9 +1186,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: ToggleButtonCompIcon,
keywords: trans("uiComp.toggleButtonCompKeywords"),
- lazyLoad: true,
- compName: "ToggleButtonComp",
- compPath: "comps/buttonComp/toggleButtonComp",
+ comp: ToggleButtonComp,
layoutInfo: {
w: 3,
h: 6,
@@ -1219,9 +1199,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: SegmentedCompIcon,
keywords: trans("uiComp.segmentedControlCompKeywords"),
- lazyLoad: true,
- compName: "SegmentedControlComp",
- compPath: "comps/selectInputComp/segmentedControl",
+ comp: SegmentedControlComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1235,9 +1213,7 @@ export var uiCompMap: Registry = {
categories: ["forms"],
icon: RatingCompIcon,
keywords: trans("uiComp.ratingCompKeywords"),
- lazyLoad: true,
- compName: "RatingComp",
- compPath: "comps/ratingComp",
+ comp: RatingComp,
layoutInfo: {
w: 6,
h: 6,
@@ -1331,9 +1307,7 @@ export var uiCompMap: Registry = {
categories: ["dashboards", "projectmanagement"],
icon: ProgressCompIcon,
keywords: trans("uiComp.progressCompKeywords"),
- lazyLoad: true,
- compName: "ProgressComp",
- compPath: "comps/progressComp",
+ comp: ProgressComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1346,9 +1320,7 @@ export var uiCompMap: Registry = {
categories: ["dashboards", "projectmanagement"],
icon: ProcessCircleCompIcon,
keywords: trans("uiComp.progressCircleCompKeywords"),
- lazyLoad: true,
- compName: "ProgressCircleComp",
- compPath: "comps/progressCircleComp",
+ comp: ProgressCircleComp,
layoutInfo: {
w: 6,
h: 20,
@@ -1364,9 +1336,7 @@ export var uiCompMap: Registry = {
categories: ["documents"],
icon: UploadCompIcon,
keywords: trans("uiComp.fileUploadCompKeywords"),
- lazyLoad: true,
- compName: "FileComp",
- compPath: "comps/fileComp/fileComp",
+ comp: FileComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1379,9 +1349,7 @@ export var uiCompMap: Registry = {
categories: ["documents"],
icon: FileViewerCompIcon,
keywords: trans("uiComp.fileViewerCompKeywords"),
- lazyLoad: true,
- compName: "FileViewerComp",
- compPath: "comps/fileViewerComp",
+ comp: FileViewerComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1397,9 +1365,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: ImageCompIcon,
keywords: trans("uiComp.imageCompKeywords"),
- lazyLoad: true,
- compName: "ImageComp",
- compPath: "comps/imageComp",
+ comp: ImageComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1412,9 +1378,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: CarouselCompIcon,
keywords: trans("uiComp.drawerCompKeywords"),
- lazyLoad: true,
- compName: "CarouselComp",
- compPath: "comps/carouselComp",
+ comp: CarouselComp,
withoutLoading: true,
layoutInfo: {
w: 12,
@@ -1428,9 +1392,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: AudioCompIcon,
keywords: trans("uiComp.audioCompKeywords"),
- lazyLoad: true,
- compName: "AudioComp",
- compPath: "comps/mediaComp/audioComp",
+ comp: AudioComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1443,9 +1405,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: VideoCompIcon,
keywords: trans("uiComp.videoCompKeywords"),
- lazyLoad: true,
- compName: "VideoComp",
- compPath: "comps/mediaComp/videoComp",
+ comp: VideoComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1459,9 +1419,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia", "dashboards"],
icon: ShapesCompIcon,
keywords: trans("uiComp.shapeCompKeywords"),
- lazyLoad: true,
- compName: "ShapeComp",
- compPath: "comps/shapeComp/shapeComp",
+ comp: ShapeComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1475,9 +1433,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: LottieAnimationCompIcon,
keywords: trans("uiComp.jsonLottieCompKeywords"),
- lazyLoad: true,
- compName: "JsonLottieComp",
- compPath: "comps/jsonComp/jsonLottieComp",
+ comp: JsonLottieComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1490,9 +1446,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: IconCompIcon,
keywords: trans("uiComp.iconCompKeywords"),
- lazyLoad: true,
- compName: "IconComp",
- compPath: "comps/iconComp",
+ comp: IconComp,
layoutInfo: {
w: 2,
h: 10,
@@ -1520,9 +1474,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia"],
icon: ColorPickerCompIcon,
keywords: trans("uiComp.colorPickerCompKeywords"),
- lazyLoad: true,
- compName: "ColorPickerComp",
- compPath: "comps/mediaComp/colorPickerComp",
+ comp: ColorPickerComp,
},
// item Handling
@@ -1534,9 +1486,7 @@ export var uiCompMap: Registry = {
categories: ["itemHandling", "documents"],
icon: QRCodeCompIcon,
keywords: trans("uiComp.qrCodeCompKeywords"),
- lazyLoad: true,
- compName: "QRCodeComp",
- compPath: "comps/qrCodeComp",
+ comp: QRCodeComp,
layoutInfo: {
w: 6,
h: 32,
@@ -1549,9 +1499,7 @@ export var uiCompMap: Registry = {
categories: ["itemHandling"],
icon: ScannerCompIcon,
keywords: trans("uiComp.scannerCompKeywords"),
- lazyLoad: true,
- compName: "ScannerComp",
- compPath: "comps/buttonComp/scannerComp",
+ comp: ScannerComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1564,9 +1512,7 @@ export var uiCompMap: Registry = {
categories: ["itemHandling"],
icon: SignatureCompIcon,
keywords: trans("uiComp.signatureCompKeywords"),
- lazyLoad: true,
- compName: "SignatureComp",
- compPath: "comps/signatureComp",
+ comp: SignatureComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1592,9 +1538,7 @@ export var uiCompMap: Registry = {
categories: ["multimedia", "itemHandling"],
icon: TourCompIcon,
keywords: trans("uiComp.tourCompKeywords"),
- lazyLoad: true,
- compName: "TourComp",
- compPath: "comps/tourComp/tourComp",
+ comp: TourComp,
layoutInfo: {
w: 1,
h: 1,
@@ -1607,9 +1551,7 @@ export var uiCompMap: Registry = {
categories: ["forms", "itemHandling"],
icon: MultiSelectCompIcon,
keywords: trans("uiComp.multiSelectCompKeywords"),
- lazyLoad: true,
- compName: "MultiSelectComp",
- compPath: "comps/selectInputComp/multiSelectComp",
+ comp: MultiSelectComp,
layoutInfo: {
w: 6,
h: 5,
@@ -1622,9 +1564,7 @@ export var uiCompMap: Registry = {
categories: ["layout", "itemHandling", "documents"],
icon: TreeDisplayCompIcon,
keywords: trans("uiComp.treeCompKeywords"),
- lazyLoad: true,
- compName: "TreeComp",
- compPath: "comps/treeComp/treeComp",
+ comp: TreeComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1637,9 +1577,7 @@ export var uiCompMap: Registry = {
categories: ["layout", "itemHandling", "documents"],
icon: TreeSelectCompIcon,
keywords: trans("uiComp.treeSelectCompKeywords"),
- lazyLoad: true,
- compName: "TreeSelectComp",
- compPath: "comps/treeComp/treeSelectComp",
+ comp: TreeSelectComp,
layoutInfo: {
w: 12,
h: 5,
@@ -1652,9 +1590,7 @@ export var uiCompMap: Registry = {
description: trans("uiComp.transferDesc"),
categories: ["itemHandling", "documents"],
keywords: trans("uiComp.transferKeywords"),
- lazyLoad: true,
- compName: "transferComp",
- compPath: "comps/transferComp",
+ comp: transferComp,
layoutInfo: {
w: 12,
h: 50,
@@ -1689,9 +1625,7 @@ export var uiCompMap: Registry = {
icon: IFrameCompIcon,
categories: ["integration"],
keywords: trans("uiComp.iframeCompKeywords"),
- lazyLoad: true,
- compName: "IFrameComp",
- compPath: "comps/iframeComp",
+ comp: IFrameComp,
layoutInfo: {
w: 12,
h: 40,
@@ -1704,9 +1638,7 @@ export var uiCompMap: Registry = {
icon: CustomCompIcon,
categories: ["integration"],
keywords: trans("uiComp.customCompKeywords"),
- lazyLoad: true,
- compName: "CustomComp",
- compPath: "comps/customComp/customComp",
+ comp: CustomComp,
layoutInfo: {
w: 12,
h: 40,
From 2d2ce6a98b01f4b5195315d72b3c5b050804e5d9 Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Thu, 20 Feb 2025 00:25:19 +0500
Subject: [PATCH 049/124] optimise bundle size for lowcoder
---
client/packages/lowcoder/index.html | 1 +
client/packages/lowcoder/package.json | 1 +
client/packages/lowcoder/vite.config.mts | 111 ++++++++++++++++++++++-
client/yarn.lock | 1 +
4 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder/index.html b/client/packages/lowcoder/index.html
index 0f69f0293..8bd8757bc 100644
--- a/client/packages/lowcoder/index.html
+++ b/client/packages/lowcoder/index.html
@@ -30,6 +30,7 @@
flex-direction: column;
top: 0;
z-index: 10000;
+ transition: opacity 0.25s linear;
}
#loading svg {
animation: breath 1s linear infinite;
diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json
index 65146553a..44903f0a6 100644
--- a/client/packages/lowcoder/package.json
+++ b/client/packages/lowcoder/package.json
@@ -127,6 +127,7 @@
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-only-ascii": "^0.0.0",
"http-proxy-middleware": "^2.0.6",
+ "rollup-plugin-terser": "^7.0.2",
"rollup-plugin-visualizer": "^5.9.2",
"typescript": "^4.8.4",
"vite": "^4.5.5",
diff --git a/client/packages/lowcoder/vite.config.mts b/client/packages/lowcoder/vite.config.mts
index 9699e15db..371b944a7 100644
--- a/client/packages/lowcoder/vite.config.mts
+++ b/client/packages/lowcoder/vite.config.mts
@@ -1,5 +1,5 @@
import dotenv from "dotenv";
-import { defineConfig, ServerOptions, UserConfig } from "vite";
+import { defineConfig, PluginOption, ServerOptions, UserConfig } from "vite";
import react from "@vitejs/plugin-react";
import viteTsconfigPaths from "vite-tsconfig-paths";
import svgrPlugin from "vite-plugin-svgr";
@@ -12,6 +12,7 @@ import dynamicImport from 'vite-plugin-dynamic-import';
import { ensureLastSlash } from "./src/dev-utils/util";
import { buildVars } from "./src/dev-utils/buildVars";
import { globalDepPlugin } from "./src/dev-utils/globalDepPlguin";
+import { terser } from 'rollup-plugin-terser';
// import { nodePolyfills } from 'vite-plugin-node-polyfills'
dotenv.config();
@@ -66,6 +67,7 @@ export const viteConfig: UserConfig = {
},
base,
build: {
+ minify: "terser",
manifest: true,
target: "es2020",
cssTarget: "chrome87",
@@ -73,9 +75,116 @@ export const viteConfig: UserConfig = {
assetsDir: "static",
emptyOutDir: false,
rollupOptions: {
+ treeshake: {
+ moduleSideEffects: true,
+ propertyReadSideEffects: false,
+ tryCatchDeoptimization: false,
+ unknownGlobalSideEffects: false,
+ },
output: {
chunkFileNames: "[hash].js",
+ manualChunks: (id) => {
+ if (id.includes("node_modules")) {
+ // UI LIBRARIES
+ if (id.includes("@ant-design/icons")) return "ant-design-icons";
+ if (id.includes("node_modules/antd")) return "antd";
+ if (id.includes("styled-components")) return "styled-components";
+
+ // 🔹 BARCODE & QR CODE PROCESSING
+ if (id.includes("react-qr-barcode-scanner")) return "barcode";
+
+ // TEXT EDITORS & PARSERS
+ if (id.includes("codemirror")) return "codemirror";
+ if (id.includes("quill")) return "quill";
+ if (id.includes("react-json-view")) return "react-json-view";
+ if (id.includes("react-quill")) return "react-quill";
+ if (id.includes("remark-gfm")) return "remark-gfm";
+ if (id.includes("rehype-raw")) return "rehype-raw";
+ if (id.includes("rehype-sanitize")) return "rehype-sanitize";
+
+ // DRAG & DROP
+ if (id.includes("@dnd-kit")) return "dnd-kit";
+ if (id.includes("react-draggable")) return "react-draggable";
+ if (id.includes("react-grid-layout")) return "react-grid-layout";
+ if (id.includes("react-sortable-hoc")) return "react-sortable-hoc";
+
+ // ICONS & FONTS
+ if (id.includes("@fortawesome")) return "fontawesome";
+ if (id.includes("@remixicon")) return "remixicon";
+
+ // DATE/TIME HANDLING
+ if (id.includes("moment")) return "moment";
+ if (id.includes("date-fns")) return "date-fns";
+ if (id.includes("dayjs")) return "dayjs";
+
+ // UTILITIES & HELPERS
+ if (id.includes("clsx")) return "clsx";
+ if (id.includes("immer")) return "immer";
+ if (id.includes("lodash")) return "lodash";
+ if (id.includes("lodash-es")) return "lodash-es";
+ if (id.includes("uuid")) return "uuid";
+ if (id.includes("ua-parser-js")) return "ua-parser-js";
+ if (id.includes("html2canvas")) return "ua-parser-js";
+ if (id.includes("numbro")) return "numbro";
+
+ // FILE & DATA PROCESSING
+ if (id.includes("buffer")) return "buffer";
+ if (id.includes("file-saver")) return "file-saver";
+ if (id.includes("papaparse")) return "papaparse";
+ if (id.includes("parse5")) return "parse5";
+ if (id.includes("xlsx")) return "xlsx";
+ if (id.includes("alasql")) return "alasql";
+ if (id.includes("sql-formatter")) return "sql-formatter";
+
+ // NETWORK & HTTP
+ if (id.includes("axios")) return "axios";
+ if (id.includes("fetch")) return "fetch";
+ if (id.includes("http")) return "http-modules";
+ if (id.includes("https")) return "https-modules";
+
+ // WEB SOCKETS & STREAMING
+ if (id.includes("sockjs")) return "websockets";
+ if (id.includes("websocket")) return "websockets";
+
+ // STATE MANAGEMENT
+ if (id.includes("react-error-boundary")) return "react-error-boundary";
+ if (id.includes("redux-devtools-extension")) return "redux-devtools";
+
+ // POLYFILLS & BROWSER COMPATIBILITY
+ // if (id.includes("core-js")) return "core-js";
+ if (id.includes("regenerator-runtime")) return "regenerator-runtime";
+ if (id.includes("eslint4b-prebuilt-2")) return "eslint4b-prebuilt-2";
+
+ // MISCELLANEOUS
+ if (id.includes("cnchar")) return "cnchar";
+ if (id.includes("hotkeys-js")) return "hotkeys-js";
+ if (id.includes("loglevel")) return "loglevel";
+ if (id.includes("qrcode.react")) return "qrcode-react";
+ if (id.includes("react-joyride")) return "react-joyride";
+ if (id.includes("rc-trigger")) return "rc-trigger";
+ if (id.includes("really-relaxed-json")) return "really-relaxed-json";
+ if (id.includes("simplebar-react")) return "simplebar-react";
+ if (id.includes("react-documents")) return "react-documents";
+ if (id.includes("react-colorful")) return "react-colorful";
+ if (id.includes("react-best-gradient-color-picker")) return "react-best-gradient-color-picker";
+ if (id.includes("@supabase/supabase-js")) return "supabase";
+ return null;
+ }
+ return null;
+ },
},
+ plugins: [
+ terser({
+ compress: {
+ drop_console: true,
+ drop_debugger: true,
+ pure_funcs: ["console.info", "console.debug", "console.log"],
+ },
+ format: {
+ comments: /(@vite-ignore|webpackIgnore)/
+ },
+ }) as PluginOption,
+ ],
onwarn: (warning, warn) => {
if (warning.code === 'MODULE_LEVEL_DIRECTIVE') {
return
diff --git a/client/yarn.lock b/client/yarn.lock
index 1589f0bb7..1f364cbc2 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -14242,6 +14242,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
rehype-sanitize: ^5.0.1
remark-gfm: ^4.0.0
resize-observer-polyfill: ^1.5.1
+ rollup-plugin-terser: ^7.0.2
rollup-plugin-visualizer: ^5.9.2
simplebar-react: ^3.2.4
sql-formatter: ^8.2.0
From 75a84b083a44ef82fde22c887cad1b672955dd1e Mon Sep 17 00:00:00 2001
From: RAHEEL
Date: Thu, 20 Feb 2025 00:40:32 +0500
Subject: [PATCH 050/124] remove console logs
---
client/packages/lowcoder/src/layout/layoutOpUtils.tsx | 4 ----
1 file changed, 4 deletions(-)
diff --git a/client/packages/lowcoder/src/layout/layoutOpUtils.tsx b/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
index cde38066a..8d10aa459 100644
--- a/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
+++ b/client/packages/lowcoder/src/layout/layoutOpUtils.tsx
@@ -92,7 +92,6 @@ export namespace layoutOpUtils {
}
function reduce(layout: Layout, op: LayoutOp, stickyItemMap?: Record>): Layout {
- // console.log(op.type);
let newLayout = layout;
switch (op.type) {
case LayoutOpTypes.CHANGE_ITEM: {
@@ -147,7 +146,6 @@ export let getUILayout = (
ops: LayoutOps | undefined,
setHiddenCompHeightZero: boolean = false
): Layout => {
- // console.log('layout->before', changedHs, layout);
// log.log("getUILayout. layout: ", layout, " extraLayout: ", extraLayout, " changedHs: ", changedHs, " ops: ", ops);
const stickyItemMap = getStickyItemMap(layout);
const hiddenItemHeight = _.fromPairs(
@@ -165,9 +163,7 @@ export let getUILayout = (
realOps.forEach((op) => {
layout = reduce(layout, op, stickyItemMap);
});
- // console.log('layout->after', layout);
layout = cascade(layout);
- // console.log('layout->after->2', layout);
if (!setHiddenCompHeightZero) {
const recoverHiddenOps = _.toPairs(hiddenItemHeight).map(([i, h]) => changeItemOp(i, { h }));
recoverHiddenOps.forEach((op) => {
From e2b27f4dab9a77754eb22cf0cb07676a4d17c8a9 Mon Sep 17 00:00:00 2001
From: FalkWolsky
Date: Wed, 19 Feb 2025 21:01:31 +0100
Subject: [PATCH 051/124] Hubspot DataSource preparations and first backend
translation
---
.../src/main/resources/locale_de.properties | 105 +
.../node-service/src/plugins/hubspot/index.ts | 81 +
.../specsV4/Automation API Collections.json | 7327 ++
.../hubspot/specsV4/CMS API Collections.json | 63340 ++++++++++++++++
.../hubspot/specsV4/CRM API Collections.json | 59105 ++++++++++++++
.../Conversations API Collections.json | 4516 ++
.../specsV4/Engagements API Collections.json | 16946 +++++
.../specsV4/Events API Collections.json | 2062 +
.../specsV4/Marketing API Collections.json | 18195 +++++
.../specsV4/Settings API Collections.json | 4000 +
.../specsV4/Webhooks API Collections.json | 1252 +
server/node-service/src/plugins/index.ts | 2 +
.../src/static/plugin-icons/hubspot.svg | 1 +
13 files changed, 176932 insertions(+)
create mode 100644 server/api-service/lowcoder-sdk/src/main/resources/locale_de.properties
create mode 100644 server/node-service/src/plugins/hubspot/index.ts
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Automation API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/CMS API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/CRM API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Conversations API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Engagements API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Events API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Marketing API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Settings API Collections.json
create mode 100644 server/node-service/src/plugins/hubspot/specsV4/Webhooks API Collections.json
create mode 100644 server/node-service/src/static/plugin-icons/hubspot.svg
diff --git a/server/api-service/lowcoder-sdk/src/main/resources/locale_de.properties b/server/api-service/lowcoder-sdk/src/main/resources/locale_de.properties
new file mode 100644
index 000000000..ed265a080
--- /dev/null
+++ b/server/api-service/lowcoder-sdk/src/main/resources/locale_de.properties
@@ -0,0 +1,105 @@
+## Allgemein
+INTERNAL_SERVER_ERROR=Oops! Der Dienst ist ausgelastet, bitte versuchen Sie es später erneut.
+NOT_AUTHORIZED=Entschuldigung, es scheint, dass Sie keine Berechtigung haben.
+INVALID_PARAMETER=Ungültiger Parameter gefunden: {0}, bitte überprüfen und erneut versuchen.
+INFRA_REDIS_TIMEOUT=Verbindungszeitüberschreitung, bitte versuchen Sie es später erneut.
+INFRA_MONGODB_TIMEOUT=Verbindungszeitüberschreitung, bitte versuchen Sie es später erneut.
+INVALID_PERMISSION_OPERATION=Ungültige Berechtigung für Ressource: {0}
+REQUEST_THROTTLED=Entschuldigung, die Anfrage erfolgt zu häufig, bitte versuchen Sie es später erneut.
+SERVER_NOT_READY=Entschuldigung, der Server ist noch nicht bereit.
+INVALID_ORG_ID=Ungültige Workspace-ID, sie könnte gelöscht worden sein oder existiert nicht.
+SWITCH_CURRENT_ORG_ERROR=Entschuldigung, Wechsel des Workspaces fehlgeschlagen, bitte versuchen Sie es später erneut.
+LAST_ADMIN_CANNOT_LEAVE_ORG=Entschuldigung, der letzte Administrator des Workspaces kann nicht verlassen.
+INVALID_GROUP_ID=Ungültige Gruppen-ID, bitte überprüfen und erneut versuchen.
+CANNOT_REMOVE_MYSELF=Entschuldigung, Gruppenadministratoren können sich nicht selbst löschen.
+CANNOT_LEAVE_GROUP=Entschuldigung, eine Gruppe muss mindestens einen Administrator enthalten.
+CANNOT_DELETE_SYSTEM_GROUP=Systemgruppen können nicht gelöscht werden.
+NEED_DEV_TO_CREATE_RESOURCE=Ungültige Aktion, Workspace-Entwickler oder Administrator erforderlich.
+UNABLE_TO_FIND_VALID_ORG=Kein gültiger Workspace für den aktuellen Benutzer gefunden.
+USER_BANNED=Dieses Konto ist gesperrt.
+SENDING_EMAIL_FAILED=E-Mail konnte nicht gesendet werden. Bitte überprüfen Sie die SMTP-Einstellungen für den Workspace.
+TOKEN_EXPIRED=Das Token zum Zurücksetzen des Passworts ist abgelaufen.
+INVALID_TOKEN=Ungültiges Token für die Anfrage zum Zurücksetzen des Passworts erhalten.
+
+# Einladung
+INVALID_INVITATION_CODE=Einladungscode nicht gefunden.
+ALREADY_IN_ORGANIZATION=Sie sind bereits in diesem Workspace.
+INVITED_ORG_DELETED=Oops! Der eingeladene Workspace wurde bereits gelöscht.
+APPLICATION_NOT_FOUND=Anwendung {0} konnte nicht gefunden werden.
+ILLEGAL_APPLICATION_PERMISSION_ID=Entschuldigung, es scheint, dass Sie keine Berechtigung haben.
+FETCH_HISTORY_SNAPSHOT_FAILURE=Fehler beim Abrufen des Anwendungsverlaufs, bitte versuchen Sie es später erneut.
+FETCH_HISTORY_SNAPSHOT_COUNT_FAILURE=Fehler beim Abrufen der Anzahl der Anwendungsverlaufsschnappschüsse, bitte versuchen Sie es später erneut.
+INVALID_HISTORY_SNAPSHOT=Verlaufsschnappschuss {0} konnte nicht gefunden werden.
+
+## Datenquelle
+DATASOURCE_NOT_FOUND=Datenquelle {0} konnte nicht gefunden werden.
+INVALID_DATASOURCE_CONFIGURATION=Ungültige Datenquellenkonfiguration: {0}, bitte erneut überprüfen.
+DATASOURCE_DELETE_FAIL_DUE_TO_REMAINING_QUERIES=Löschen der Datenquelle fehlgeschlagen, da sie noch in Abfragen verwendet wird.
+PLUGIN_CREATE_CONNECTION_FAILED=Entschuldigung, Verbindung zur Datenquelle fehlgeschlagen, Fehlermeldung: {0}.
+DATASOURCE_PLUGIN_ID_NOT_GIVEN=Ungültige Daten, Typ der Datenquelle fehlt.
+INVALID_DATASOURCE_CONFIG_TYPE=Entschuldigung, der Typ der Datenquellenkonfiguration ist ungültig: {0}, bitte erneut überprüfen.
+DATASOURCE_CONNECTION_TYPE_ERROR=Ungültiger Datenquellenverbindungstyp: {0}, bitte kontaktieren Sie den Systemadministrator.
+DATASOURCE_TYPE_ERROR=Ungültiger Datenquellentyp: {0}, bitte kontaktieren Sie den Systemadministrator.
+DUPLICATE_DATABASE_NAME=Oops, dieser Datenquellenname wird bereits verwendet, bitte wählen Sie einen neuen Namen.
+DATASOURCE_CLOSE_FAILED=Fehler beim Schließen der Datenquellenverbindung: {0}.
+DATASOURCE_AND_APP_ORG_NOT_MATCH=Ungültige Anfrage, die Datenquelle gehört nicht zum Anwendungs-Workspace.
+USER_NOT_SIGNED_IN=Unbekannter Benutzer, bitte zuerst anmelden.
+
+# Authentifizierung
+FAIL_TO_GET_OIDC_INFO=Fehler beim Abrufen der OIDC-Informationen, Fehlermeldung: {0}.
+LOG_IN_SOURCE_NOT_SUPPORTED=Entschuldigung, dieser Anmeldekanal wird nicht unterstützt.
+USER_LOGIN_ID_EXIST=Die aktuelle E-Mail wird bereits von einem anderen Benutzer verwendet.
+INVALID_PASSWORD=Entschuldigung, die Passwörter stimmen nicht überein.
+PASSWORD_NOT_SET_YET=Dieser Benutzer hat noch kein Passwort festgelegt und kann es daher nicht zurücksetzen.
+INVALID_EMAIL_OR_PASSWORD=Ungültige E-Mail oder ungültiges Passwort.
+ALREADY_BIND=Entschuldigung, {0} wurde bereits von Benutzer {1} gebunden.
+NEED_BIND_THIRD_PARTY_CONNECTION=Entschuldigung, es muss ein Drittanbieter-Anmeldekanal mit dem aktuellen Workspace verbunden werden.
+PAYLOAD_TOO_LARGE=Entschuldigung, die maximale Dateigröße beträgt {0} KB, Ihre Datei überschreitet das Limit.
+
+# Ereignisse
+EVENT_TYPE_USER_LOGIN=Anmeldung
+EVENT_TYPE_USER_LOGOUT=Abmeldung
+EVENT_TYPE_VIEW=App anzeigen
+EVENT_TYPE_APPLICATION_CREATE=Neue App erstellen
+EVENT_TYPE_APPLICATION_DELETE=App löschen
+EVENT_TYPE_APPLICATION_UPDATE=App aktualisieren
+EVENT_TYPE_APPLICATION_MOVE=In Ordner verschieben
+EVENT_TYPE_APPLICATION_RECYCLED=In Papierkorb verschieben
+EVENT_TYPE_APPLICATION_RESTORE=App wiederherstellen
+EVENT_TYPE_FOLDER_CREATE=Neuen Ordner erstellen
+EVENT_TYPE_FOLDER_DELETE=Ordner löschen
+EVENT_TYPE_FOLDER_UPDATE=Ordner aktualisieren
+EVENT_TYPE_QUERY_EXECUTION=Abfrage ausführen
+EVENT_TYPE_GROUP_CREATE=Neue Gruppe erstellen
+EVENT_TYPE_GROUP_UPDATE=Gruppe aktualisieren
+EVENT_TYPE_GROUP_DELETE=Gruppe löschen
+EVENT_TYPE_GROUP_MEMBER_ADD=Gruppenmitglied hinzufügen
+EVENT_TYPE_GROUP_MEMBER_ROLE_UPDATE=Rolle des Gruppenmitglieds aktualisieren
+EVENT_TYPE_GROUP_MEMBER_LEAVE=Gruppe verlassen
+EVENT_TYPE_GROUP_MEMBER_REMOVE=Gruppenmitglied entfernen
+EVENT_TYPE_SERVER_START_UP=Serverstart
+
+# Limitierungen
+EXCEED_MAX_USER_ORG_COUNT=Entschuldigung, Sie haben die maximale Anzahl an Workspaces erreicht.
+EXCEED_MAX_ORG_MEMBER_COUNT=Entschuldigung, dieser Workspace hat die maximale Anzahl an Mitgliedern erreicht.
+EXCEED_MAX_GROUP_COUNT=Entschuldigung, dieser Workspace hat die maximale Anzahl an Gruppen erreicht.
+EXCEED_MAX_APP_COUNT=Entschuldigung, Sie haben die maximale Anzahl an Anwendungen im aktuellen Workspace erreicht.
+EXCEED_MAX_DEVELOPER_COUNT=Entschuldigung, Sie haben die maximale Anzahl an Entwicklern erreicht.
+
+# Plugin
+DUPLICATE_COLUMN=Doppelte Spalten gefunden: {0}, bitte verwenden Sie das Schlüsselwort 'as', um doppelte Spalten umzubenennen.
+INVALID_JSON_FROM_RESPONSE=Antwortdaten sind kein gültiges JSON, bitte überprüfen Sie Ihren Content-Type-Header.
+REACH_REDIRECT_LIMIT=Maximale Anzahl an HTTP-Weiterleitungen erreicht: {0}.
+GOOGLESHEETS_QUERY_PARAM_EMPTY=Google Sheets Abfrageparameter ist leer.
+GOOGLESHEETS_QUERY_PARAM_ERROR=Google Sheets Abfrageparameter ist ungültig: {0}.
+GOOGLESHEETS_REQUEST_ERROR=Google Sheets Anfrage fehlgeschlagen.
+GOOGLESHEETS_DATASOURCE_CONFIG_ERROR=Fehler beim Parsen der Google Sheets Datenquellenkonfiguration.
+GOOGLESHEETS_EMPTY_ROW=Keine Daten in dieser Zeile gefunden. Möchten Sie zuerst etwas einfügen?
+APPLICATION_EDIT_ERROR_LACK_OF_DATASOURCE_PERMISSIONS=Aktuelle Änderungen an dieser Anwendung werden nicht gespeichert, da Berechtigungen für einige Datenquellen fehlen.
+CERTIFICATE_EMPTY=Zertifikat ist leer.
+USER_NOT_EXIST=Benutzer existiert nicht.
+DUPLICATE_AUTH_CONFIG_ADDITION=Dieser Anbieter wurde bereits zur Organisation hinzugefügt.
+EMAIL_PROVIDER_DISABLED=E-Mail-Anbieter ist deaktiviert.
+SLUG_DUPLICATE_ENTRY=Slug existiert bereits.
+SLUG_INVALID=Slug-Format ist ungültig.
+FLOW_ERROR=Flow-Fehlermeldung: {0}.
diff --git a/server/node-service/src/plugins/hubspot/index.ts b/server/node-service/src/plugins/hubspot/index.ts
new file mode 100644
index 000000000..5f4e6a858
--- /dev/null
+++ b/server/node-service/src/plugins/hubspot/index.ts
@@ -0,0 +1,81 @@
+import { dirToSpecList, specsToOptions, version2spec } from "../../common/util";
+import _ from "lodash";
+import path from "path";
+import { OpenAPI } from "openapi-types";
+import { ConfigToType, DataSourcePlugin, QueryConfig } from "lowcoder-sdk/dataSource";
+import { runOpenApi } from "../openApi";
+import { parseMultiOpenApi, ParseOpenApiOptions } from "../openApi/parse";
+
+const specs = {
+ "v4": dirToSpecList(path.join(__dirname, "./specsV4")),
+};
+
+const dataSourceConfig = {
+ type: "dataSource",
+ params: [
+ {
+ type: "textInput",
+ key: "authToken",
+ label: "Private Access Token",
+ placeholder: "",
+ tooltip: "Read more here: https://developers.hubspot.com/docs/guides/apps/private-apps/overview"
+ },
+ {
+ label: "API Version",
+ key: "specVersion",
+ type: "select",
+ tooltip: "Choose the HubSpot API Version.",
+ placeholder: "v4",
+ options: specsToOptions(specs)
+ },
+ ],
+} as const;
+
+const parseOptions: ParseOpenApiOptions = {
+ actionLabel: (method: string, path: string, operation: OpenAPI.Operation) => {
+ return _.upperFirst(operation.operationId || "");
+ },
+};
+
+let queryConfig: any = {};
+
+type DataSourceConfigType = ConfigToType;
+
+const hubspotPlugin: DataSourcePlugin = {
+ id: "hubspot",
+ name: "HubSpot",
+ icon: "hubspot.svg",
+ category: "CRM",
+ dataSourceConfig,
+
+ queryConfig: async (data) => {
+ if (!queryConfig[data.specVersion as keyof typeof queryConfig]) {
+ const { actions, categories } = await parseMultiOpenApi(version2spec(specs, data.specVersion), parseOptions);
+ queryConfig[data.specVersion as keyof typeof queryConfig] = {
+ type: "query",
+ label: "Action",
+ categories: {
+ label: "Resources",
+ items: categories,
+ },
+ actions,
+ };
+ }
+ return queryConfig[data.specVersion as keyof typeof queryConfig];
+ },
+
+ run: function (actionData, dataSourceConfig): Promise {
+ const runApiDsConfig = {
+ url: "",
+ serverURL: "https://api.hubapi.com", // HubSpot API base URL
+ dynamicParamsConfig: dataSourceConfig,
+ specVersion: dataSourceConfig.specVersion,
+ headers: {
+ Authorization: `Bearer ${dataSourceConfig.authToken}`,
+ },
+ };
+ return runOpenApi(actionData, runApiDsConfig, version2spec(specs, dataSourceConfig.specVersion));
+ },
+};
+
+export default hubspotPlugin;
diff --git a/server/node-service/src/plugins/hubspot/specsV4/Automation API Collections.json b/server/node-service/src/plugins/hubspot/specsV4/Automation API Collections.json
new file mode 100644
index 000000000..e4cb5c364
--- /dev/null
+++ b/server/node-service/src/plugins/hubspot/specsV4/Automation API Collections.json
@@ -0,0 +1,7327 @@
+openapi: 3.0.0
+info:
+ title: Automation API Collections
+ description: "Use [HubSpot's workflows tool](https://knowledge.hubspot.com/workflows/create-workflows) to automate business processes and allow your team to be more efficient. You can create custom workflow actions to integrate your service with HubSpot's workflows.\n\nAfter setting up your custom action, when users install your application, they can add the custom action to their workflows.\n\nIn workflows, you can also use the _Custom code_ action to write and execute JavaScript or Python (_in beta_). With custom code actions, you can extend workflow functionality within and outside of HubSpot. To learn more about HubSpot's APIs, you can refer to either the [developer documentation](https://developers.hubspot.com/docs/api/developer-guides-resources) for the latest versions or the [legacy developer documentation](https://legacydocs.hubspot.com/) for our older APIs. To see examples of common custom code actions, view [HubSpot's Programmable Automation Use Cases](https://www.hubspot.com/programmable-automation-use-cases).\n\nCustom code actions support JavaScript using the [Node 16.x runtime framework](https://aws.amazon.com/blogs/compute/node-js-16-x-runtime-now-available-in-aws-lambda/). If you're using Python for your custom code action, the custom code action will use [Python 3.9 runtime framework](https://www.python.org/downloads/release/python-390/). When an action executes, the runtime compute is managed through a serverless function by HubSpot and [AWS Lambda](https://aws.amazon.com/lambda/features/#:~:text=AWS%20Lambda%20is%20a%20serverless,scale%2C%20performance%2C%20and%20security.).\n\nIf you encounter any general issues implementing your custom code action, you can reach out to [HubSpot support](https://knowledge.hubspot.com/account/get-help-with-hubspot). However, if you're facing any issues with your written custom code, it's recommended to search and post on the [HubSpot Developer's Forum ](https://community.hubspot.com/t5/HubSpot-Developers/ct-p/developers)\_ to get tips, advice, or help with troubleshooting your code.\n\n**Fork the collections using Run in Postman:**\n\n[ ](https://app.getpostman.com/run-collection/26126890-c13185bb-dede-45ae-a8d8-ae1a1d63385a?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26126890-c13185bb-dede-45ae-a8d8-ae1a1d63385a%26entityType%3Dcollection%26workspaceId%3Dd7645f33-539d-49c6-9e0c-efa6faae355d)"
+ version: 1.0.0
+servers:
+ - url: http://{{baseurl}}
+components:
+ securitySchemes:
+ oauth2Auth:
+ type: http
+ scheme: oauth2
+ apikeyAuth:
+ type: http
+ scheme: apikey
+tags:
+ - name: Custom Workflow Actions API
+ description: "Use\_[HubSpot's workflows tool](https://knowledge.hubspot.com/workflows/create-workflows)\_to automate business processes and allow your team to be more efficient. You can create custom workflow actions to integrate your service with HubSpot's workflows.\n\nAfter setting up your custom action, when users install your application, they can add the custom action to their workflows.\_\n\nWhen those workflows execute, HTTPS requests will send to the configured URL with the payload that you set up. Requests made for your custom action will use the v2 version of the\__X-HubSpot-Signature_. Learn more about\_[validating requests from HubSpot](https://developers.hubspot.com/docs/api/webhooks/validating-requests). \n\nYou can also skip forward to the following sections:\n\n- [Before you get started](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#before)\n \n- [Define your custom action](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#define)\n \n- [Functions](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#functions)\n \n- [Input fields](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#input)\n \n- [Fetch external data fields](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#fetch)\n \n- [Output fields](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#output)\n \n- [Labels](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#label)\n \n- [Execution](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#execution)\n \n- [Asynchronous custom action execution](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#asynchronous-execution)\n \n- [Add custom execution messages with execution rules](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#executionmessage)\n \n- [Test and publish your custom action](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#publish)\n \n\nThe final section in this article provides\_[several custom action examples](https://developers.hubspot.com/docs/api/automation/custom-workflow-actions#custom-action-examples).\n\n## Before you get started\n\n- You'll need a\_[HubSpot developer account](https://app.hubspot.com/signup/developers)\_with a\_[HubSpot app](https://developers.hubspot.com/docs/api/creating-an-app). Your app logo will be used as the icon for the custom action.\n \n- When making requests to the custom workflow action endpoints, you must include your\_[HubSpot developer account key](https://legacydocs.hubspot.com/docs/faq/developer-api-keys)\_in the request URL. For example:\n \n\n`https://api.hubspot.com/automation/v4/actions/{appId}?hapikey={developer_API_key}`\n\n## Define your custom action\n\nTo create a custom workflow action, you'll need to define the action using the following fields. This also specifies the request format for requests coming from HubSpot, as well as the handling of responses from your service.\n\n- **`actionUrl`**: the URL where an HTTPS request is sent when the action is executed. The request body will contain information about which user the action is executing on behalf of, and what values were entered for the input fields.\n \n- **`objectTypes`****:**\_which CRM objects this action can be used with.\n \n- **`published`****:**\_by default, custom actions are created in an unpublished state. Unpublished actions are only visible in the developer portal associated with your HubSpot application. To make a custom action visible to users, update the published flag on your action definition to true.\n \n- **`inputFields`**: the inputs that the action receives. These will be filled by the user.\n \n- **`inputFieldDependencies`****:**\_these rules allow fields to be grayed out until other fields meet specific conditions.\n \n- `outputFields`: the values that the action will output that can be used by later actions in the workflow. A custom action can have zero, one, or many outputs.\n \n- `objectRequestOptions`**:**\_ properties of the enrolled object included in the payload to the actionUrl.\n \n- `labels`: copy that describes to the user what the action's fields represent and what the action does. English labels are required, but labels can be specified in any of the following supported languages as well:\_French (`fr`), German (`de`), Japanese (`ja`), Spanish (`es`), Brazilian Portuguese (`pt-br`), and Dutch (`nl`).\n \n- **`executionRules`**: a list of definitions you can specify to surface errors from your service to the user creating the workflow.\n \n- **`functions`****:**\_code snippets that are run in order to transform the payload being sent to a url and/or transform the response from that url."
+ - name: Custom Workflow Actions API > Callbacks
+ - name: Custom Workflow Actions API > Definitions
+ - name: Custom Workflow Actions API > Functions
+ - name: Custom Workflow Actions API > Revisions
+ - name: Sequences API
+ - name: Sequences API > Public_Enrollments
+ - name: Sequences API > Public_Sequences
+ - name: Workflows V4 API
+ - name: Workflows V4 API > Workflows
+ - name: Workflows V4 API > Email campaigns
+ - name: Workflows V4 API > Workflow ID mappings
+paths:
+ /automation/v4/actions/callbacks/{callbackId}/complete:
+ post:
+ tags:
+ - Custom Workflow Actions API > Callbacks
+ summary: Completes a callback
+ requestBody:
+ content: {}
+ security:
+ - oauth2Auth: []
+ parameters:
+ - name: Content-Type
+ in: header
+ schema:
+ type: string
+ example: application/json
+ - name: Accept
+ in: header
+ schema:
+ type: string
+ example: '*/*'
+ - name: callbackId
+ in: path
+ schema:
+ type: string
+ required: true
+ description: '(Required) '
+ example:
+ responses:
+ '204':
+ description: No Content
+ content:
+ text/plain:
+ schema:
+ type: string
+ example: null
+ '500':
+ description: Internal Server Error
+ headers:
+ Content-Type:
+ schema:
+ type: string
+ example: '*/*'
+ content:
+ text/plain:
+ schema:
+ type: string
+ example: |-
+ {
+ "category": "",
+ "correlationId": "",
+ "message": "",
+ "subCategory": "",
+ "context": {
+ "officia_2": [
+ "",
+ ""
+ ],
+ "ullamco_2ae": [
+ "",
+ ""
+ ]
+ },
+ "links": {
+ "ad3b": "",
+ "adipisicing065": ""
+ },
+ "errors": [
+ {
+ "message": "",
+ "subCategory": "",
+ "code": "",
+ "in": "",
+ "context": {
+ "mollita5": [
+ "",
+ ""
+ ]
+ }
+ },
+ {
+ "message": "",
+ "subCategory": "",
+ "code": "",
+ "in": "",
+ "context": {
+ "consectetur98d": [
+ "",
+ ""
+ ],
+ "sit_18_": [
+ "",
+ ""
+ ]
+ }
+ }
+ ]
+ }
+ /automation/v4/actions/callbacks/complete:
+ post:
+ tags:
+ - Custom Workflow Actions API > Callbacks
+ summary: Complete a batch of callbacks
+ requestBody:
+ content: {}
+ security:
+ - oauth2Auth: []
+ parameters:
+ - name: Content-Type
+ in: header
+ schema:
+ type: string
+ example: application/json
+ - name: Accept
+ in: header
+ schema:
+ type: string
+ example: '*/*'
+ responses:
+ '204':
+ description: No Content
+ content:
+ text/plain:
+ schema:
+ type: string
+ example: null
+ '500':
+ description: Internal Server Error
+ headers:
+ Content-Type:
+ schema:
+ type: string
+ example: '*/*'
+ content:
+ text/plain:
+ schema:
+ type: string
+ example: |-
+ {
+ "category": "",
+ "correlationId": "",
+ "message": "",
+ "subCategory": "",
+ "context": {
+ "officia_2": [
+ "",
+ ""
+ ],
+ "ullamco_2ae": [
+ "",
+ ""
+ ]
+ },
+ "links": {
+ "ad3b": "",
+ "adipisicing065": ""
+ },
+ "errors": [
+ {
+ "message": "",
+ "subCategory": "",
+ "code": "",
+ "in": "",
+ "context": {
+ "mollita5": [
+ "",
+ ""
+ ]
+ }
+ },
+ {
+ "message": "",
+ "subCategory": "",
+ "code": "",
+ "in": "",
+ "context": {
+ "consectetur98d": [
+ "",
+ ""
+ ],
+ "sit_18_": [
+ "",
+ ""
+ ]
+ }
+ }
+ ]
+ }
+ /automation/v4/actions/{appId}:
+ get:
+ tags:
+ - Custom Workflow Actions API > Definitions
+ summary: Get paged extension definitions
+ security:
+ - apikeyAuth: []
+ parameters:
+ - name: Accept
+ in: header
+ schema:
+ type: string
+ example: application/json
+ - name: limit
+ in: query
+ schema:
+ type: string
+ description: The maximum number of results to display per page.
+ example:
+ - name: after
+ in: query
+ schema:
+ type: string
+ description: >-
+ The paging cursor token of the last successfully read resource will
+ be returned as the `paging.next.after` JSON property of a paged
+ response containing more results.
+ example:
+ - name: archived
+ in: query
+ schema:
+ type: boolean
+ description: Whether to return only results that have been archived.
+ example: 'false'
+ - name: appId
+ in: path
+ schema:
+ type: string
+ required: true
+ description: '(Required) '
+ example:
+ responses:
+ '200':
+ description: OK
+ headers:
+ Content-Type:
+ schema:
+ type: string
+ example: application/json
+ content:
+ application/json:
+ schema:
+ type: object
+ example:
+ results:
+ - actionUrl:
+ functions:
+ - functionType: PRE_FETCH_OPTIONS
+ id:
+ - functionType: PRE_ACTION_EXECUTION
+ id:
+ id:
+ inputFields:
+ - isRequired:
+ typeDefinition:
+ externalOptions:
+ name:
+ options:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ type: date
+ helpText:
+ referencedObjectType: SOCIAL_BROADCAST
+ description:
+ externalOptionsReferenceType:
+ label:
+ fieldType: calculation_read_time
+ optionsUrl:
+ automationFieldType:
+ supportedValueTypes:
+ - OBJECT_PROPERTY
+ - STATIC_VALUE
+ - isRequired:
+ typeDefinition:
+ externalOptions:
+ name:
+ options:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ type: phone_number
+ helpText:
+ referencedObjectType: TASK
+ description:
+ externalOptionsReferenceType:
+ label:
+ fieldType: calculation_score
+ optionsUrl:
+ automationFieldType:
+ supportedValueTypes:
+ - FIELD_DATA
+ - STATIC_VALUE
+ labels:
+ sed_b63:
+ actionName:
+ inputFieldDescriptions:
+ aute_:
+ appDisplayName:
+ outputFieldLabels:
+ minim_54:
+ dolore_9c:
+ inputFieldOptionLabels:
+ dolor__:
+ dolore3f:
+ enim__7:
+ do3a:
+ ipsum_7:
+ in6:
+ actionDescription:
+ executionRules:
+ fugiat_d:
+ sintdab:
+ inputFieldLabels:
+ sint_e05:
+ actionCardContent:
+ objectTypes:
+ -
+ -
+ published:
+ revisionId:
+ outputFields:
+ - typeDefinition:
+ externalOptions:
+ name:
+ options:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ type: object_coordinates
+ helpText:
+ referencedObjectType: SALESFORCE_SYNC_ERROR
+ description:
+ externalOptionsReferenceType:
+ label:
+ fieldType: textarea
+ optionsUrl:
+ - typeDefinition:
+ externalOptions:
+ name:
+ options:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ - description:
+ displayOrder:
+ doubleData:
+ hidden:
+ label:
+ readOnly:
+ value:
+ type: json
+ helpText:
+ referencedObjectType: CONTENT_AUDIT_PAGE
+ description:
+ externalOptionsReferenceType:
+ label:
+ fieldType: date
+ optionsUrl:
+ archivedAt:
+ inputFieldDependencies:
+ - controllingFieldName: