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)); + }} + /> +
+
+
+ {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" + })}