diff --git a/client/packages/lowcoder-comps/package.json b/client/packages/lowcoder-comps/package.json index fb022019e..1a599a47a 100644 --- a/client/packages/lowcoder-comps/package.json +++ b/client/packages/lowcoder-comps/package.json @@ -17,8 +17,8 @@ "@fullcalendar/resource-timeline": "^6.1.11", "@fullcalendar/timegrid": "^6.1.6", "@fullcalendar/timeline": "^6.1.6", - "@types/react": "^18.2.45", - "@types/react-dom": "^18.2.18", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", "agora-rtc-sdk-ng": "^4.20.2", "agora-rtm-sdk": "^1.5.1", "big.js": "^6.2.1", @@ -28,8 +28,8 @@ "lowcoder-cli": "workspace:^", "lowcoder-sdk": "workspace:^", "mermaid": "^10.6.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", "typescript": "4.8.4" }, "lowcoder": { diff --git a/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoMeetingStreamComp.tsx b/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoMeetingStreamComp.tsx index a6d49b854..6ac45e93a 100644 --- a/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoMeetingStreamComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoMeetingStreamComp.tsx @@ -22,7 +22,7 @@ import { trans } from "../../i18n/comps"; import { client } from "./meetingControllerComp"; import type { IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng"; import { useEffect, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; const VideoContainer = styled.video` height: 100%; @@ -132,62 +132,63 @@ let VideoCompBuilder = (function () { }, [props.userId.value]); // console.log("userId", userId); + useResizeDetector({ + targetRef: conRef, + }); return ( {(editorState: any) => ( - + + {userId ? ( + props.onEvent("videoClicked")} + ref={videoRef} + style={{ + display: `${showVideo ? "flex" : "none"}`, + aspectRatio: props.videoAspectRatio, + borderRadius: props.style.radius, + width: "auto", + }} + id={userId} + > + ) : ( + <>> + )} - {userId ? ( - props.onEvent("videoClicked")} - ref={videoRef} - style={{ - display: `${showVideo ? "flex" : "none"}`, - aspectRatio: props.videoAspectRatio, - borderRadius: props.style.radius, - width: "auto", - }} - id={userId} - > - ) : ( - <>> - )} - - - {userName ?? ""} - + src={props.profileImageUrl.value} + /> + {userName ?? ""} - + )} ); diff --git a/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoSharingStreamComp.tsx b/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoSharingStreamComp.tsx index dbedc1fd5..ae5424ad0 100644 --- a/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoSharingStreamComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/agoraMeetingComp/videoSharingStreamComp.tsx @@ -19,7 +19,7 @@ import { useEffect, useRef, useState } from "react"; import { client } from "./meetingControllerComp"; import type { IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng"; import { trans } from "../../i18n/comps"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { ButtonStyleControl } from "./videobuttonCompConstants"; const VideoContainer = styled.video` @@ -123,61 +123,63 @@ let SharingCompBuilder = (function () { } }, [props.userId.value]); + useResizeDetector({ + targetRef: conRef, + }); + return ( {(editorState: any) => ( - + + {userId ? ( + props.onEvent("videoClicked")} + ref={videoRef} + style={{ + display: `${showVideoSharing ? "flex" : "none"}`, + aspectRatio: props.videoAspectRatio, + borderRadius: props.style.radius, + width: "auto", + }} + id="share-screen" + > + ) : ( + <>> + )} - {userId ? ( - props.onEvent("videoClicked")} - ref={videoRef} - style={{ - display: `${showVideoSharing ? "flex" : "none"}`, - aspectRatio: props.videoAspectRatio, - borderRadius: props.style.radius, - width: "auto", - }} - id="share-screen" - > - ) : ( - <>> - )} - - - {userName ?? ""} - + src={props.profileImageUrl?.value} + /> + {userName ?? ""} - + )} ); diff --git a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx index d915dc534..df7fc0623 100644 --- a/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/barChartComp/barChartComp.tsx @@ -10,7 +10,7 @@ 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 { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import { childrenToProps, @@ -56,7 +56,8 @@ 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 echartsCompRef = useRef(null); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -156,30 +157,34 @@ BarChartTmpComp = withViewFn(BarChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + console.log('barChart - resize'); + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartComp.tsx b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartComp.tsx index 500d9d376..adb03eff4 100644 --- a/client/packages/lowcoder-comps/src/comps/basicChartComp/chartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/basicChartComp/chartComp.tsx @@ -10,7 +10,7 @@ import { chartChildrenMap, ChartSize, getDataKeys } from "./chartConstants"; import { chartPropertyView } from "./chartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "./reactEcharts"; import { childrenToProps, @@ -57,7 +57,8 @@ BasicChartTmpComp = withViewFn(BasicChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); - const echartsCompRef = useRef(); + const echartsCompRef = useRef(null); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -135,31 +136,34 @@ BasicChartTmpComp = withViewFn(BasicChartTmpComp, (comp) => { comp.children.mapInstance.dispatch(changeValueAction(null, false)) }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={themeConfig} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={themeConfig} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/boxplotChartComp/boxplotChartComp.tsx b/client/packages/lowcoder-comps/src/comps/boxplotChartComp/boxplotChartComp.tsx index 8cd1910b1..2cc9c2793 100644 --- a/client/packages/lowcoder-comps/src/comps/boxplotChartComp/boxplotChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/boxplotChartComp/boxplotChartComp.tsx @@ -10,7 +10,7 @@ import { boxplotChartChildrenMap, ChartSize, getDataKeys } from "./boxplotChartC import { boxplotChartPropertyView } from "./boxplotChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import * as echarts from "echarts"; import { @@ -56,6 +56,7 @@ BoxplotChartTmpComp = withViewFn(BoxplotChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -151,30 +152,33 @@ BoxplotChartTmpComp = withViewFn(BoxplotChartTmpComp, (comp) => { ); }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/candleStickChartComp/candleStickChartComp.tsx b/client/packages/lowcoder-comps/src/comps/candleStickChartComp/candleStickChartComp.tsx index c07bcb62d..8692ef8cf 100644 --- a/client/packages/lowcoder-comps/src/comps/candleStickChartComp/candleStickChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/candleStickChartComp/candleStickChartComp.tsx @@ -10,7 +10,7 @@ import { candleStickChartChildrenMap, ChartSize, getDataKeys } from "./candleSti import { candleStickChartPropertyView } from "./candleStickChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ CandleStickChartTmpComp = withViewFn(CandleStickChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -154,20 +155,23 @@ CandleStickChartTmpComp = withViewFn(CandleStickChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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%" }} @@ -178,7 +182,7 @@ CandleStickChartTmpComp = withViewFn(CandleStickChartTmpComp, (comp) => { theme={mode !== 'map' ? themeConfig : undefined} mode={mode} /> - + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx b/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx index 581a75e92..74ebfca4d 100644 --- a/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartComp/chartComp.tsx @@ -10,7 +10,7 @@ import { chartChildrenMap, ChartSize, getDataKeys } from "./chartConstants"; import { chartPropertyView } from "./chartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "./reactEcharts"; import { childrenToProps, @@ -61,6 +61,7 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const [mapScriptLoaded, setMapScriptLoaded] = useState(false); const firstResize = useRef(true); @@ -215,20 +216,23 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { onMapEvent('zoomLevelChange'); }, [mode, mapZoomlevel]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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; - } - }} - > + {(mode !== 'map' || (mode === 'map' && isMapScriptLoaded)) && ( (echartsCompRef.current = e)} @@ -241,7 +245,7 @@ ChartTmpComp = withViewFn(ChartTmpComp, (comp) => { mode={mode} /> )} - + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/chartsGeoMapComp/chartsGeoMapComp.tsx b/client/packages/lowcoder-comps/src/comps/chartsGeoMapComp/chartsGeoMapComp.tsx index 0f13a6d4e..60bf25cb7 100644 --- a/client/packages/lowcoder-comps/src/comps/chartsGeoMapComp/chartsGeoMapComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/chartsGeoMapComp/chartsGeoMapComp.tsx @@ -10,7 +10,7 @@ import { chartChildrenMap, ChartSize, getDataKeys } from "../basicChartComp/char import { chartPropertyView } from "../basicChartComp/chartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import { childrenToProps, @@ -66,6 +66,7 @@ MapTmpComp = withViewFn(MapTmpComp, (comp) => { const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const [mapScriptLoaded, setMapScriptLoaded] = useState(false); const firstResize = useRef(true); @@ -168,33 +169,36 @@ MapTmpComp = withViewFn(MapTmpComp, (comp) => { onMapEvent('zoomLevelChange'); }, [mapZoomlevel]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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; - } - }} - > + {isMapScriptLoaded && ( (echartsCompRef.current = e)} - style={{ height: "100%" }} - notMerge - lazyUpdate - opts={{ locale: getEchartsLocale() }} - option={option} - theme={undefined} - mode={mode} - /> + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={undefined} + mode={mode} + /> )} - + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/funnelChartComp/funnelChartComp.tsx b/client/packages/lowcoder-comps/src/comps/funnelChartComp/funnelChartComp.tsx index 091ff9d67..63ccfdc14 100644 --- a/client/packages/lowcoder-comps/src/comps/funnelChartComp/funnelChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/funnelChartComp/funnelChartComp.tsx @@ -10,7 +10,7 @@ import { funnelChartChildrenMap, ChartSize, getDataKeys } from "./funnelChartCon import { funnelChartPropertyView } from "./funnelChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ FunnelChartTmpComp = withViewFn(FunnelChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,30 +156,33 @@ FunnelChartTmpComp = withViewFn(FunnelChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/gaugeChartComp/gaugeChartComp.tsx b/client/packages/lowcoder-comps/src/comps/gaugeChartComp/gaugeChartComp.tsx index 57ed97efb..67f89c2f4 100644 --- a/client/packages/lowcoder-comps/src/comps/gaugeChartComp/gaugeChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/gaugeChartComp/gaugeChartComp.tsx @@ -10,7 +10,7 @@ import { gaugeChartChildrenMap, ChartSize, getDataKeys } from "./gaugeChartConst import { gaugeChartPropertyView } from "./gaugeChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -57,6 +57,7 @@ GaugeChartTmpComp = withViewFn(GaugeChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -156,30 +157,33 @@ GaugeChartTmpComp = withViewFn(GaugeChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/graphChartComp/graphChartComp.tsx b/client/packages/lowcoder-comps/src/comps/graphChartComp/graphChartComp.tsx index a87d9d1ee..56b4de6a2 100644 --- a/client/packages/lowcoder-comps/src/comps/graphChartComp/graphChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/graphChartComp/graphChartComp.tsx @@ -10,7 +10,7 @@ import { graphChartChildrenMap, ChartSize, getDataKeys } from "./graphChartConst import { graphChartPropertyView } from "./graphChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -57,6 +57,7 @@ GraphChartTmpComp = withViewFn(GraphChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -156,30 +157,33 @@ GraphChartTmpComp = withViewFn(GraphChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/heatmapChartComp/heatmapChartComp.tsx b/client/packages/lowcoder-comps/src/comps/heatmapChartComp/heatmapChartComp.tsx index a5bc421cd..21064ba13 100644 --- a/client/packages/lowcoder-comps/src/comps/heatmapChartComp/heatmapChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/heatmapChartComp/heatmapChartComp.tsx @@ -10,7 +10,7 @@ import { heatmapChartChildrenMap, ChartSize, getDataKeys } from "./heatmapChartC import { heatmapChartPropertyView } from "./heatmapChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ HeatmapChartTmpComp = withViewFn(HeatmapChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ HeatmapChartTmpComp = withViewFn(HeatmapChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/imageEditorComp/index.tsx b/client/packages/lowcoder-comps/src/comps/imageEditorComp/index.tsx index 70d2bf29b..311a96eaf 100644 --- a/client/packages/lowcoder-comps/src/comps/imageEditorComp/index.tsx +++ b/client/packages/lowcoder-comps/src/comps/imageEditorComp/index.tsx @@ -13,7 +13,7 @@ import { stringExposingStateControl, } from "lowcoder-sdk"; import { useRef } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import _ from "lodash"; import { RecordConstructorToView } from "lowcoder-core"; import { Container, customTheme, EmbeddedButton, saveEvent } from "./imageEditorConstants"; @@ -70,6 +70,12 @@ const ContainerImageEditor = (props: RecordConstructorToView props.dataURI.onChange(dataURL); props.data.onChange(dataURL.split(",")[1]); }; + + useResizeDetector({ + targetRef: conRef, + onResize, + }); + return ( > {props.buttonText.value} - - - - - + + + ); }; diff --git a/client/packages/lowcoder-comps/src/comps/line3dChartComp/line3dChartComp.tsx b/client/packages/lowcoder-comps/src/comps/line3dChartComp/line3dChartComp.tsx index 712e224b2..14ce13539 100644 --- a/client/packages/lowcoder-comps/src/comps/line3dChartComp/line3dChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/line3dChartComp/line3dChartComp.tsx @@ -10,7 +10,7 @@ import { line3dChartChildrenMap, ChartSize, getDataKeys } from "./line3dChartCon import { line3dChartPropertyView } from "./line3dChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import * as echarts from "echarts"; import { @@ -56,6 +56,7 @@ Line3DChartTmpComp = withViewFn(Line3DChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -151,30 +152,33 @@ Line3DChartTmpComp = withViewFn(Line3DChartTmpComp, (comp) => { ); }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx index 1f1dfa150..032607625 100644 --- a/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/lineChartComp/lineChartComp.tsx @@ -10,7 +10,7 @@ import { lineChartChildrenMap, ChartSize, getDataKeys } from "./lineChartConstan import { lineChartPropertyView } from "./lineChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ LineChartTmpComp = withViewFn(LineChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -150,30 +151,33 @@ LineChartTmpComp = withViewFn(LineChartTmpComp, (comp) => { ); }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/parallelChartComp/parallelChartComp.tsx b/client/packages/lowcoder-comps/src/comps/parallelChartComp/parallelChartComp.tsx index 84bcf3280..51eedee13 100644 --- a/client/packages/lowcoder-comps/src/comps/parallelChartComp/parallelChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/parallelChartComp/parallelChartComp.tsx @@ -10,7 +10,7 @@ import { parallelChartChildrenMap, ChartSize, getDataKeys } from "./parallelChar import { parallelChartPropertyView } from "./parallelChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import * as echarts from "echarts"; import { @@ -56,6 +56,7 @@ ParallelChartTmpComp = withViewFn(ParallelChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -151,30 +152,33 @@ ParallelChartTmpComp = withViewFn(ParallelChartTmpComp, (comp) => { ); }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx index d502a8090..aaa5f0198 100644 --- a/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/pieChartComp/pieChartComp.tsx @@ -10,7 +10,7 @@ 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 { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import * as echarts from "echarts"; import { @@ -56,6 +56,7 @@ PieChartTmpComp = withViewFn(PieChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -170,30 +171,33 @@ PieChartTmpComp = withViewFn(PieChartTmpComp, (comp) => { ); }, [mapJson, theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/radarChartComp/radarChartComp.tsx b/client/packages/lowcoder-comps/src/comps/radarChartComp/radarChartComp.tsx index edb3e6312..a1cabc884 100644 --- a/client/packages/lowcoder-comps/src/comps/radarChartComp/radarChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/radarChartComp/radarChartComp.tsx @@ -10,7 +10,7 @@ import { radarChartChildrenMap, ChartSize, getDataKeys } from "./radarChartConst import { radarChartPropertyView } from "./radarChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ RadarChartTmpComp = withViewFn(RadarChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ RadarChartTmpComp = withViewFn(RadarChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/sankeyChartComp/sankeyChartComp.tsx b/client/packages/lowcoder-comps/src/comps/sankeyChartComp/sankeyChartComp.tsx index 0fedd251c..eefb9208c 100644 --- a/client/packages/lowcoder-comps/src/comps/sankeyChartComp/sankeyChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/sankeyChartComp/sankeyChartComp.tsx @@ -10,7 +10,7 @@ import { sankeyChartChildrenMap, ChartSize, getDataKeys } from "./sankeyChartCon import { sankeyChartPropertyView } from "./sankeyChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ SankeyChartTmpComp = withViewFn(SankeyChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ SankeyChartTmpComp = withViewFn(SankeyChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/scatterChartComp/scatterChartComp.tsx b/client/packages/lowcoder-comps/src/comps/scatterChartComp/scatterChartComp.tsx index 003fadf1d..c7fd7da9c 100644 --- a/client/packages/lowcoder-comps/src/comps/scatterChartComp/scatterChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/scatterChartComp/scatterChartComp.tsx @@ -10,7 +10,7 @@ import { scatterChartChildrenMap, ChartSize, getDataKeys } from "./scatterChartC import { scatterChartPropertyView } from "./scatterChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../basicChartComp/reactEcharts"; import * as echarts from "echarts"; import { @@ -56,6 +56,7 @@ ScatterChartTmpComp = withViewFn(ScatterChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -151,30 +152,33 @@ ScatterChartTmpComp = withViewFn(ScatterChartTmpComp, (comp) => { ); }, [theme, childrenProps, chartSize, ...Object.values(echartsConfigChildren)]); + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/sunburstChartComp/sunburstChartComp.tsx b/client/packages/lowcoder-comps/src/comps/sunburstChartComp/sunburstChartComp.tsx index aaa6ff799..f77c18293 100644 --- a/client/packages/lowcoder-comps/src/comps/sunburstChartComp/sunburstChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/sunburstChartComp/sunburstChartComp.tsx @@ -10,7 +10,7 @@ import { sunburstChartChildrenMap, ChartSize, getDataKeys } from "./sunburstChar import { sunburstChartPropertyView } from "./sunburstChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ SunburstChartTmpComp = withViewFn(SunburstChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ SunburstChartTmpComp = withViewFn(SunburstChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/themeriverChartComp/themeriverChartComp.tsx b/client/packages/lowcoder-comps/src/comps/themeriverChartComp/themeriverChartComp.tsx index 7724fe72a..80c549eaf 100644 --- a/client/packages/lowcoder-comps/src/comps/themeriverChartComp/themeriverChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/themeriverChartComp/themeriverChartComp.tsx @@ -10,7 +10,7 @@ import { themeriverChartChildrenMap, ChartSize, getDataKeys } from "./themeriver import { themeriverChartPropertyView } from "./themeriverChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ ThemeriverChartTmpComp = withViewFn(ThemeriverChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ ThemeriverChartTmpComp = withViewFn(ThemeriverChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/treeChartComp/treechartComp.tsx b/client/packages/lowcoder-comps/src/comps/treeChartComp/treechartComp.tsx index 53fd9c8bb..0f25f78e2 100644 --- a/client/packages/lowcoder-comps/src/comps/treeChartComp/treechartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/treeChartComp/treechartComp.tsx @@ -10,7 +10,7 @@ import { treeChartChildrenMap, ChartSize, getDataKeys } from "./treeChartConstan import { treeChartPropertyView } from "./treeChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ TreeChartTmpComp = withViewFn(TreeChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,20 +156,23 @@ TreeChartTmpComp = withViewFn(TreeChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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%" }} @@ -179,7 +183,7 @@ TreeChartTmpComp = withViewFn(TreeChartTmpComp, (comp) => { theme={mode !== 'map' ? themeConfig : undefined} mode={mode} /> - + ); }); diff --git a/client/packages/lowcoder-comps/src/comps/treemapChartComp/treemapChartComp.tsx b/client/packages/lowcoder-comps/src/comps/treemapChartComp/treemapChartComp.tsx index c67ea8875..3740efb23 100644 --- a/client/packages/lowcoder-comps/src/comps/treemapChartComp/treemapChartComp.tsx +++ b/client/packages/lowcoder-comps/src/comps/treemapChartComp/treemapChartComp.tsx @@ -10,7 +10,7 @@ import { treemapChartChildrenMap, ChartSize, getDataKeys } from "./treemapChartC import { treeChartPropertyView } from "./treemapChartPropertyView"; import _ from "lodash"; import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import ReactECharts from "../chartComp/reactEcharts"; import { childrenToProps, @@ -56,6 +56,7 @@ TreemapChartTmpComp = withViewFn(TreemapChartTmpComp, (comp) => { const onUIEvent = comp.children.onUIEvent.getView(); const onEvent = comp.children.onEvent.getView(); const echartsCompRef = useRef(); + const containerRef = useRef(null); const [chartSize, setChartSize] = useState(); const firstResize = useRef(true); const theme = useContext(ThemeContext); @@ -155,31 +156,34 @@ TreemapChartTmpComp = withViewFn(TreemapChartTmpComp, (comp) => { if(comp.children.mapInstance.value) return; }, [option]) + useResizeDetector({ + targetRef: containerRef, + onResize: ({width, height}) => { + if (width && height) { + setChartSize({ w: width, h: height }); + } + if (!firstResize.current) { + // ignore the first resize, which will impact the loading animation + echartsCompRef.current?.getEchartsInstance().resize(); + } else { + firstResize.current = false; + } + } + }) + 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} - theme={mode !== 'map' ? themeConfig : undefined} - mode={mode} - /> - + ref={(e) => (echartsCompRef.current = e)} + style={{ height: "100%" }} + notMerge + lazyUpdate + opts={{ locale: getEchartsLocale() }} + option={option} + theme={mode !== 'map' ? themeConfig : undefined} + mode={mode} + /> + ); }); diff --git a/client/packages/lowcoder-core/lib/index.d.ts b/client/packages/lowcoder-core/lib/index.d.ts index a8d4702ec..f2cf59699 100644 --- a/client/packages/lowcoder-core/lib/index.d.ts +++ b/client/packages/lowcoder-core/lib/index.d.ts @@ -1,6 +1,6 @@ /// import * as react from 'react'; -import React, { ReactNode } from 'react'; +import { ReactNode } from 'react'; import * as react_jsx_runtime from 'react/jsx-runtime'; type EvalMethods = Record>; diff --git a/client/packages/lowcoder-core/lib/index.js b/client/packages/lowcoder-core/lib/index.js index 32166ca50..22bd0c7ab 100644 --- a/client/packages/lowcoder-core/lib/index.js +++ b/client/packages/lowcoder-core/lib/index.js @@ -1,118 +1,80 @@ import _ from 'lodash'; import { serialize, compile, middleware, prefixer, stringify } from 'stylis'; -/****************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise, SuppressedError, Symbol */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -var __assign = function() { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; - -function __rest(s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -} - -function __generator(thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -} - -function __spreadArray(to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -} - -typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { - var e = new Error(message); - return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; + +function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +} + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function isEqualArgs(args, cacheArgs, equals) { @@ -123,21 +85,20 @@ function isEqualArgs(args, cacheArgs, equals) { return true; } return (args.length === cacheArgs.length && - cacheArgs.every(function (arg, index) { var _a, _b; return (_b = (_a = equals === null || equals === void 0 ? void 0 : equals[index]) === null || _a === void 0 ? void 0 : _a.call(equals, arg, args[index])) !== null && _b !== void 0 ? _b : arg === args[index]; })); + cacheArgs.every((arg, index) => equals?.[index]?.(arg, args[index]) ?? arg === args[index])); } function getCacheResult(thisObj, fnName, args, equals) { - var _a; - var cache = (_a = thisObj === null || thisObj === void 0 ? void 0 : thisObj.__cache) === null || _a === void 0 ? void 0 : _a[fnName]; + const cache = thisObj?.__cache?.[fnName]; if (cache && isEqualArgs(args, cache.args, equals)) { return cache.result; } } function cache(fn, args, thisObj, fnName, equals) { - var result = getCacheResult(thisObj, fnName, args, equals); + const result = getCacheResult(thisObj, fnName, args, equals); if (result) { return result.value; } - var cache = { + const cache = { id: Symbol("id"), args: args, time: Date.now(), @@ -146,117 +107,107 @@ function cache(fn, args, thisObj, fnName, equals) { thisObj.__cache = {}; } thisObj.__cache[fnName] = cache; - var value = fn.apply(thisObj, args); - cache.result = { value: value }; + const value = fn.apply(thisObj, args); + cache.result = { value }; return value; } function memoized(equals) { return function (target, fnName, descriptor) { - var fn = descriptor.value; - descriptor.value = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } + const fn = descriptor.value; + descriptor.value = function (...args) { return cache(fn, args, this, fnName, equals); }; return descriptor; }; } -var COST_MS_PRINT_THR = 0; -var RecursivePerfUtil = /** @class */ (function () { - function RecursivePerfUtil() { - var _this = this; - this.root = Symbol("root"); - this.stack = []; - this.initRecord = function () { - return { obj: _this.root, name: "@root", childrenPerfInfo: [], costMs: 0, depth: 0, info: {} }; - }; - this.getRecordByStack = function (stack) { - var curRecord = _this.record; - (stack !== null && stack !== void 0 ? stack : _this.stack).forEach(function (idx) { - curRecord = curRecord.childrenPerfInfo[idx]; - }); - return curRecord; - }; - this.clear = function () { - _this.record = _this.initRecord(); - }; - this.print = function (stack, cost_ms_print_thr) { - if (cost_ms_print_thr === void 0) { cost_ms_print_thr = COST_MS_PRINT_THR; } - var record = _this.getRecordByStack(stack); - console.info("~~ PerfInfo. costMs: ".concat(record.costMs.toFixed(3), ", stack: ").concat(stack, ", [name]").concat(record.name, ", [info]"), record.info, ", obj: ", record.obj, ", depth: ".concat(record.depth, ", size: ").concat(_.size(record.childrenPerfInfo))); - record.childrenPerfInfo.forEach(function (subRecord, idx) { - if (subRecord.costMs >= cost_ms_print_thr) { - console.info(" costMs: ".concat(subRecord.costMs.toFixed(3), " [").concat(idx, "]").concat(subRecord.name, " [info]"), subRecord.info, ". obj: ", subRecord.obj, ""); - } - }); - }; +const COST_MS_PRINT_THR = 0; +class RecursivePerfUtil { + root = Symbol("root"); + record; + stack = []; + constructor() { this.record = this.initRecord(); } - RecursivePerfUtil.prototype.log = function (info, key, log) { - info[key] = log; + initRecord = () => { + return { obj: this.root, name: "@root", childrenPerfInfo: [], costMs: 0, depth: 0, info: {} }; + }; + getRecordByStack = (stack) => { + let curRecord = this.record; + (stack ?? this.stack).forEach((idx) => { + curRecord = curRecord.childrenPerfInfo[idx]; + }); + return curRecord; }; - RecursivePerfUtil.prototype.perf = function (obj, name, fn) { + log(info, key, log) { + info[key] = log; + } + perf(obj, name, fn) { { return fn(_.noop); } + } + clear = () => { + this.record = this.initRecord(); }; - return RecursivePerfUtil; -}()); -var evalPerfUtil = new RecursivePerfUtil(); + print = (stack, cost_ms_print_thr = COST_MS_PRINT_THR) => { + const record = this.getRecordByStack(stack); + console.info(`~~ PerfInfo. costMs: ${record.costMs.toFixed(3)}, stack: ${stack}, [name]${record.name}, [info]`, record.info, `, obj: `, record.obj, `, depth: ${record.depth}, size: ${_.size(record.childrenPerfInfo)}`); + record.childrenPerfInfo.forEach((subRecord, idx) => { + if (subRecord.costMs >= cost_ms_print_thr) { + console.info(` costMs: ${subRecord.costMs.toFixed(3)} [${idx}]${subRecord.name} [info]`, subRecord.info, `. obj: `, subRecord.obj, ``); + } + }); + }; +} +const evalPerfUtil = new RecursivePerfUtil(); // @ts-ignore globalThis.evalPerfUtil = evalPerfUtil; -var AbstractNode = /** @class */ (function () { - function AbstractNode() { - this.type = "abstract"; - this.evalCache = {}; - } - AbstractNode.prototype.evaluate = function (exposingNodes, methods) { - var _this = this; - return evalPerfUtil.perf(this, "eval", function () { - exposingNodes = exposingNodes !== null && exposingNodes !== void 0 ? exposingNodes : {}; - var dependingNodeMap = _this.filterNodes(exposingNodes); +class AbstractNode { + type = "abstract"; + evalCache = {}; + constructor() { } + evaluate(exposingNodes, methods) { + return evalPerfUtil.perf(this, "eval", () => { + exposingNodes = exposingNodes ?? {}; + const dependingNodeMap = this.filterNodes(exposingNodes); // use cache when equals to the last dependingNodeMap - if (dependingNodeMapEquals(_this.evalCache.dependingNodeMap, dependingNodeMap)) { - return _this.evalCache.value; + if (dependingNodeMapEquals(this.evalCache.dependingNodeMap, dependingNodeMap)) { + return this.evalCache.value; } // initialize cyclic field - _this.evalCache.cyclic = false; - var result = _this.justEval(exposingNodes, methods); + this.evalCache.cyclic = false; + const result = this.justEval(exposingNodes, methods); // write cache - _this.evalCache.dependingNodeMap = dependingNodeMap; - _this.evalCache.value = result; - if (!_this.evalCache.cyclic) { + this.evalCache.dependingNodeMap = dependingNodeMap; + this.evalCache.value = result; + if (!this.evalCache.cyclic) { // check children cyclic - _this.evalCache.cyclic = _this.getChildren().some(function (node) { return node.hasCycle(); }); + this.evalCache.cyclic = this.getChildren().some((node) => node.hasCycle()); } return result; }); - }; - AbstractNode.prototype.hasCycle = function () { - var _a; - return (_a = this.evalCache.cyclic) !== null && _a !== void 0 ? _a : false; - }; - AbstractNode.prototype.dependNames = function () { + } + hasCycle() { + return this.evalCache.cyclic ?? false; + } + dependNames() { return Object.keys(this.dependValues()); - }; - AbstractNode.prototype.isHitEvalCache = function (exposingNodes) { - exposingNodes = exposingNodes !== null && exposingNodes !== void 0 ? exposingNodes : {}; - var dependingNodeMap = this.filterNodes(exposingNodes); + } + isHitEvalCache(exposingNodes) { + exposingNodes = exposingNodes ?? {}; + const dependingNodeMap = this.filterNodes(exposingNodes); return dependingNodeMapEquals(this.evalCache.dependingNodeMap, dependingNodeMap); - }; - return AbstractNode; -}()); + } +} /** * transform WrapNode in dependingNodeMap to actual node. * since WrapNode is dynamically constructed in eval process, its reference always changes. */ function unWrapDependingNodeMap(depMap) { - var nextMap = new Map(); - depMap.forEach(function (p, n) { + const nextMap = new Map(); + depMap.forEach((p, n) => { if (n.type === "wrap") { nextMap.set(n.delegate, p); } @@ -267,7 +218,7 @@ function unWrapDependingNodeMap(depMap) { return nextMap; } function setEquals(s1, s2) { - return s2 !== undefined && s1.size === s2.size && Array.from(s2).every(function (v) { return s1.has(v); }); + return s2 !== undefined && s1.size === s2.size && Array.from(s2).every((v) => s1.has(v)); } /** * check whether 2 dependingNodeMaps are equal @@ -282,10 +233,10 @@ function dependingNodeMapEquals(dependingNodeMap1, dependingNodeMap2) { if (!dependingNodeMap1 || dependingNodeMap1.size !== dependingNodeMap2.size) { return false; } - var map1 = unWrapDependingNodeMap(dependingNodeMap1); - var map2 = unWrapDependingNodeMap(dependingNodeMap2); - var result = true; - map2.forEach(function (paths, node) { + const map1 = unWrapDependingNodeMap(dependingNodeMap1); + const map2 = unWrapDependingNodeMap(dependingNodeMap2); + let result = true; + map2.forEach((paths, node) => { result = result && setEquals(paths, map1.get(node)); }); return result; @@ -294,41 +245,39 @@ function dependingNodeMapEquals(dependingNodeMap1, dependingNodeMap2) { /** * return a new node, evaluating to a function result with the input node value as the function's input */ -var FunctionNode = /** @class */ (function (_super) { - __extends(FunctionNode, _super); - function FunctionNode(child, func) { - var _this = _super.call(this) || this; - _this.child = child; - _this.func = func; - _this.type = "function"; - return _this; - } - FunctionNode.prototype.filterNodes = function (exposingNodes) { - var _this = this; - return evalPerfUtil.perf(this, "filterNodes", function () { - return _this.child.filterNodes(exposingNodes); +class FunctionNode extends AbstractNode { + child; + func; + type = "function"; + constructor(child, func) { + super(); + this.child = child; + this.func = func; + } + filterNodes(exposingNodes) { + return evalPerfUtil.perf(this, "filterNodes", () => { + return this.child.filterNodes(exposingNodes); }); - }; - FunctionNode.prototype.justEval = function (exposingNodes, methods) { + } + justEval(exposingNodes, methods) { return this.func(this.child.evaluate(exposingNodes, methods)); - }; - FunctionNode.prototype.getChildren = function () { + } + getChildren() { return [this.child]; - }; - FunctionNode.prototype.dependValues = function () { + } + dependValues() { return this.child.dependValues(); - }; - FunctionNode.prototype.fetchInfo = function (exposingNodes, options) { + } + fetchInfo(exposingNodes, options) { return this.child.fetchInfo(exposingNodes, options); - }; - __decorate([ - memoized() - ], FunctionNode.prototype, "filterNodes", null); - __decorate([ - memoized() - ], FunctionNode.prototype, "fetchInfo", null); - return FunctionNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], FunctionNode.prototype, "filterNodes", null); +__decorate([ + memoized() +], FunctionNode.prototype, "fetchInfo", null); function withFunction(child, func) { return new FunctionNode(child, func); } @@ -337,118 +286,108 @@ function addDepend(target, node, paths) { if (!node) { return; } - var value = target.get(node); + let value = target.get(node); if (value === undefined) { value = new Set(); target.set(node, value); } - paths.forEach(function (p) { return value === null || value === void 0 ? void 0 : value.add(p); }); + paths.forEach((p) => value?.add(p)); } function addDepends(target, source) { - source === null || source === void 0 ? void 0 : source.forEach(function (paths, node) { return addDepend(target, node, paths); }); + source?.forEach((paths, node) => addDepend(target, node, paths)); return target; } /** * the evaluated value is the record constructed by the children nodes */ -var RecordNode = /** @class */ (function (_super) { - __extends(RecordNode, _super); - function RecordNode(children) { - var _this = _super.call(this) || this; - _this.children = children; - _this.type = "record"; - return _this; - } - RecordNode.prototype.filterNodes = function (exposingNodes) { - var _this = this; - return evalPerfUtil.perf(this, "filterNodes", function () { - var result = new Map(); - Object.values(_this.children).forEach(function (node) { +class RecordNode extends AbstractNode { + children; + type = "record"; + constructor(children) { + super(); + this.children = children; + } + filterNodes(exposingNodes) { + return evalPerfUtil.perf(this, `filterNodes`, () => { + const result = new Map(); + Object.values(this.children).forEach((node) => { addDepends(result, node.filterNodes(exposingNodes)); }); return result; }); - }; - RecordNode.prototype.justEval = function (exposingNodes, methods) { - var _this = this; - return _.mapValues(this.children, function (v, key) { - return evalPerfUtil.perf(_this, "eval-".concat(key), function () { return v.evaluate(exposingNodes, methods); }); - }); - }; - RecordNode.prototype.getChildren = function () { + } + justEval(exposingNodes, methods) { + return _.mapValues(this.children, (v, key) => evalPerfUtil.perf(this, `eval-${key}`, () => v.evaluate(exposingNodes, methods))); + } + getChildren() { return Object.values(this.children); - }; - RecordNode.prototype.dependValues = function () { - var nodes = Object.values(this.children); + } + dependValues() { + const nodes = Object.values(this.children); if (nodes.length === 1) { return nodes[0].dependValues(); } - var ret = {}; - nodes.forEach(function (node) { - Object.entries(node.dependValues()).forEach(function (_a) { - var key = _a[0], value = _a[1]; + let ret = {}; + nodes.forEach((node) => { + Object.entries(node.dependValues()).forEach(([key, value]) => { ret[key] = value; }); }); return ret; - }; - RecordNode.prototype.fetchInfo = function (exposingNodes, options) { - var isFetching = false; - var ready = true; - Object.entries(this.children).forEach(function (_a) { - _a[0]; var child = _a[1]; - var fi = child.fetchInfo(exposingNodes, options); + } + fetchInfo(exposingNodes, options) { + let isFetching = false; + let ready = true; + Object.entries(this.children).forEach(([name, child]) => { + const fi = child.fetchInfo(exposingNodes, options); isFetching = fi.isFetching || isFetching; ready = fi.ready && ready; }); - return { isFetching: isFetching, ready: ready }; - }; - __decorate([ - memoized() - ], RecordNode.prototype, "filterNodes", null); - __decorate([ - memoized() - ], RecordNode.prototype, "fetchInfo", null); - return RecordNode; -}(AbstractNode)); + return { isFetching, ready }; + } +} +__decorate([ + memoized() +], RecordNode.prototype, "filterNodes", null); +__decorate([ + memoized() +], RecordNode.prototype, "fetchInfo", null); function fromRecord(record) { return new RecordNode(record); } -var CachedNode = /** @class */ (function (_super) { - __extends(CachedNode, _super); - function CachedNode(child) { - var _this = _super.call(this) || this; - _this.type = "cached"; - _this.child = withEvalCache(child); - return _this; +class CachedNode extends AbstractNode { + type = "cached"; + child; + constructor(child) { + super(); + this.child = withEvalCache(child); } - CachedNode.prototype.filterNodes = function (exposingNodes) { + filterNodes(exposingNodes) { return this.child.filterNodes(exposingNodes); - }; - CachedNode.prototype.justEval = function (exposingNodes, methods) { - var isCached = this.child.isHitEvalCache(exposingNodes); // isCached must be set before evaluate() call - var value = this.child.evaluate(exposingNodes, methods); - return { value: value, isCached: isCached }; - }; - CachedNode.prototype.getChildren = function () { + } + justEval(exposingNodes, methods) { + const isCached = this.child.isHitEvalCache(exposingNodes); // isCached must be set before evaluate() call + const value = this.child.evaluate(exposingNodes, methods); + return { value, isCached }; + } + getChildren() { return [this.child]; - }; - CachedNode.prototype.dependValues = function () { + } + dependValues() { return this.child.dependValues(); - }; - CachedNode.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return this.child.fetchInfo(exposingNodes); - }; - __decorate([ - memoized() - ], CachedNode.prototype, "filterNodes", null); - return CachedNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], CachedNode.prototype, "filterNodes", null); function withEvalCache(node) { - var newNode = withFunction(node, function (x) { return x; }); - newNode.evalCache = __assign({}, node.evalCache); + const newNode = withFunction(node, (x) => x); + newNode.evalCache = { ...node.evalCache }; return newNode; } /** @@ -465,9 +404,9 @@ function withEvalCache(node) { * @returns the new node */ function evalNodeOrMinor(mainNode, minorNode) { - var nodeRecord = { main: new CachedNode(mainNode), minor: minorNode }; - return new FunctionNode(new RecordNode(nodeRecord), function (record) { - var mainCachedValue = record.main; + const nodeRecord = { main: new CachedNode(mainNode), minor: minorNode }; + return new FunctionNode(new RecordNode(nodeRecord), (record) => { + const mainCachedValue = record.main; if (!mainCachedValue.isCached) { return mainCachedValue.value; } @@ -504,26 +443,27 @@ function toReadableString(value) { }); } -var ValueAndMsg = /** @class */ (function () { - function ValueAndMsg(value, msg, extra, midValue) { +class ValueAndMsg { + value; + msg; + extra; + midValue; // a middle value after eval and before transform + constructor(value, msg, extra, midValue) { this.value = value; this.msg = msg; this.extra = extra; this.midValue = midValue; } - ValueAndMsg.prototype.hasError = function () { + hasError() { return this.msg !== undefined; - }; - ValueAndMsg.prototype.getMsg = function (displayValueFn) { - var _a; - if (displayValueFn === void 0) { displayValueFn = toReadableString; } - return (_a = (this.hasError() ? this.msg : displayValueFn(this.value))) !== null && _a !== void 0 ? _a : ""; - }; - return ValueAndMsg; -}()); + } + getMsg(displayValueFn = toReadableString) { + return (this.hasError() ? this.msg : displayValueFn(this.value)) ?? ""; + } +} function dependsErrorMessage(node) { - return "DependencyError: \"".concat(node.unevaledValue, "\" caused a cyclic dependency."); + return `DependencyError: "${node.unevaledValue}" caused a cyclic dependency.`; } function getErrorMessage(err) { // todo try to use 'err instanceof EvalTypeError' instead @@ -535,27 +475,26 @@ function getErrorMessage(err) { : "UnknownError: unknown exception during eval"; } function mergeNodesWithSameName(map) { - var nameDepMap = {}; - map.forEach(function (paths, node) { - paths.forEach(function (p) { - var path = p.split("."); - var dep = genDepends(path, node); - var name = path[0]; - var newDep = mergeNode(nameDepMap[name], dep); + const nameDepMap = {}; + map.forEach((paths, node) => { + paths.forEach((p) => { + const path = p.split("."); + const dep = genDepends(path, node); + const name = path[0]; + const newDep = mergeNode(nameDepMap[name], dep); nameDepMap[name] = newDep; }); }); return nameDepMap; } function genDepends(path, node) { - var _a; if (path.length <= 0) { throw new Error("path length should not be 0"); } if (path.length === 1) { return node; } - return genDepends(path.slice(0, -1), fromRecord((_a = {}, _a[path[path.length - 1]] = node, _a))); + return genDepends(path.slice(0, -1), fromRecord({ [path[path.length - 1]]: node })); } // node2 mostly has one path function mergeNode(node1, node2) { @@ -565,13 +504,13 @@ function mergeNode(node1, node2) { if (!nodeIsRecord(node1) || !nodeIsRecord(node2)) { throw new Error("unevaledNode should be type of RecordNode"); } - var record1 = node1.children; - var record2 = node2.children; - var record = __assign({}, record1); - Object.keys(record2).forEach(function (name) { - var subNode1 = record1[name]; - var subNode2 = record2[name]; - var subNode = subNode1 ? mergeNode(subNode1, subNode2) : subNode2; + const record1 = node1.children; + const record2 = node2.children; + const record = { ...record1 }; + Object.keys(record2).forEach((name) => { + const subNode1 = record1[name]; + const subNode2 = record2[name]; + let subNode = subNode1 ? mergeNode(subNode1, subNode2) : subNode2; record[name] = subNode; }); return fromRecord(record); @@ -580,28 +519,28 @@ function nodeIsRecord(node) { return node.type === "record"; } -var DYNAMIC_SEGMENT_REGEX = /{{([\s\S]*?)}}/; +const DYNAMIC_SEGMENT_REGEX = /{{([\s\S]*?)}}/; function isDynamicSegment(segment) { return DYNAMIC_SEGMENT_REGEX.test(segment); } function getDynamicStringSegments(input) { - var segments = []; - var position = 0; - var start = input.indexOf("{{"); + const segments = []; + let position = 0; + let start = input.indexOf("{{"); while (start >= 0) { - var i = start + 2; + let i = start + 2; while (i < input.length && input[i] === "{") i++; - var end = input.indexOf("}}", i); + let end = input.indexOf("}}", i); if (end < 0) { break; } - var nextStart = input.indexOf("{{", end + 2); - var maxIndex = nextStart >= 0 ? nextStart : input.length; - var maxStartOffset = i - start - 2; - var sum = i - start; - var minValue = Number.MAX_VALUE; - var minOffset = Number.MAX_VALUE; + const nextStart = input.indexOf("{{", end + 2); + const maxIndex = nextStart >= 0 ? nextStart : input.length; + const maxStartOffset = i - start - 2; + let sum = i - start; + let minValue = Number.MAX_VALUE; + let minOffset = Number.MAX_VALUE; for (; i < maxIndex; i++) { switch (input[i]) { case "{": @@ -610,8 +549,8 @@ function getDynamicStringSegments(input) { case "}": sum--; if (input[i - 1] === "}") { - var offset = Math.min(Math.max(sum, 0), maxStartOffset); - var value = Math.abs(sum - offset); + const offset = Math.min(Math.max(sum, 0), maxStartOffset); + const value = Math.abs(sum - offset); if (value < minValue || (value === minValue && offset < minOffset)) { minValue = value; minOffset = offset; @@ -626,13 +565,12 @@ function getDynamicStringSegments(input) { start = nextStart; } segments.push(input.slice(position)); - return segments.filter(function (t) { return t; }); + return segments.filter((t) => t); } function filterDepends(unevaledValue, exposingNodes, maxDepth) { - var ret = new Map(); - for (var _i = 0, _a = getDynamicStringSegments(unevaledValue); _i < _a.length; _i++) { - var segment = _a[_i]; + const ret = new Map(); + for (const segment of getDynamicStringSegments(unevaledValue)) { if (isDynamicSegment(segment)) { addDepends(ret, parseDepends(segment.slice(2, -2), exposingNodes, maxDepth)); } @@ -643,8 +581,8 @@ function hasCycle(segment, exposingNodes) { if (!isDynamicSegment(segment)) { return false; } - var ret = false; - parseDepends(segment.slice(2, -2), exposingNodes).forEach(function (paths, node) { + let ret = false; + parseDepends(segment.slice(2, -2), exposingNodes).forEach((paths, node) => { ret = ret || node.hasCycle(); }); return ret; @@ -657,7 +595,7 @@ function changeDependName(unevaledValue, oldName, name, isFunction) { return rename(unevaledValue, oldName, name); } return getDynamicStringSegments(unevaledValue) - .map(function (segment) { + .map((segment) => { if (!isDynamicSegment(segment)) { return segment; } @@ -666,26 +604,24 @@ function changeDependName(unevaledValue, oldName, name, isFunction) { .join(""); } function rename(segment, oldName, name) { - var accessors = [".", "["]; - var regStrList = ["[a-zA-Z_$][a-zA-Z_$0-9.[\\]]*", "\\[[a-zA-Z_][a-zA-Z_0-9.]*"]; - var ret = segment; - for (var _i = 0, regStrList_1 = regStrList; _i < regStrList_1.length; _i++) { - var regStr = regStrList_1[_i]; - var reg = new RegExp(regStr, "g"); - ret = ret.replace(reg, function (s) { + const accessors = [".", "["]; + const regStrList = ["[a-zA-Z_$][a-zA-Z_$0-9.[\\]]*", "\\[[a-zA-Z_][a-zA-Z_0-9.]*"]; + let ret = segment; + for (const regStr of regStrList) { + const reg = new RegExp(regStr, "g"); + ret = ret.replace(reg, (s) => { if (s === oldName) { return name; } - var origin = oldName; - var target = name; - var matched = false; - if (s.startsWith("[".concat(origin))) { - origin = "[".concat(origin); - target = "[".concat(name); + let origin = oldName; + let target = name; + let matched = false; + if (s.startsWith(`[${origin}`)) { + origin = `[${origin}`; + target = `[${name}`; matched = true; } - for (var _i = 0, accessors_1 = accessors; _i < accessors_1.length; _i++) { - var accessor = accessors_1[_i]; + for (const accessor of accessors) { if (s.startsWith(origin + accessor)) { matched = true; target = target + accessor + s.substring(origin.length + accessor.length); @@ -701,28 +637,28 @@ function rename(segment, oldName, name) { return ret; } function getIdentifiers(jsSnippet) { - var ret = []; - var commonReg = /[a-zA-Z_$][a-zA-Z_$0-9.[\]]*/g; - var commonIds = jsSnippet.match(commonReg); + const ret = []; + const commonReg = /[a-zA-Z_$][a-zA-Z_$0-9.[\]]*/g; + const commonIds = jsSnippet.match(commonReg); if (commonIds) { - ret.push.apply(ret, commonIds); + ret.push(...commonIds); } - var indexIds = []; - (jsSnippet.match(/\[[a-zA-Z_][a-zA-Z_0-9\[\].]*\]/g) || []).forEach(function (i) { - indexIds.push.apply(indexIds, getIdentifiers(i.slice(1, -1))); + const indexIds = []; + (jsSnippet.match(/\[[a-zA-Z_][a-zA-Z_0-9\[\].]*\]/g) || []).forEach((i) => { + indexIds.push(...getIdentifiers(i.slice(1, -1))); }); - ret.push.apply(ret, indexIds); + ret.push(...indexIds); if (ret.length === 0) { return [jsSnippet]; } return ret; } function parseDepends(jsSnippet, exposingNodes, maxDepth) { - var depends = new Map(); - var identifiers = getIdentifiers(jsSnippet); - identifiers.forEach(function (identifier) { - var subpaths = _.toPath(identifier); - var depend = getDependNode(maxDepth ? subpaths.slice(0, maxDepth) : subpaths, exposingNodes); + const depends = new Map(); + const identifiers = getIdentifiers(jsSnippet); + identifiers.forEach((identifier) => { + const subpaths = _.toPath(identifier); + const depend = getDependNode(maxDepth ? subpaths.slice(0, maxDepth) : subpaths, exposingNodes); if (depend) { addDepend(depends, depend[0], [depend[1]]); } @@ -733,12 +669,11 @@ function getDependNode(subPaths, exposingNodes) { if (subPaths.length <= 0) { return undefined; } - var nodes = exposingNodes; - var node = undefined; - var path = []; - for (var _i = 0, subPaths_1 = subPaths; _i < subPaths_1.length; _i++) { - var subPath = subPaths_1[_i]; - var subNode = nodes[subPath]; + let nodes = exposingNodes; + let node = undefined; + const path = []; + for (const subPath of subPaths) { + const subNode = nodes[subPath]; if (!nodes.hasOwnProperty(subPath) || !subNode) { break; } @@ -1120,7 +1055,7 @@ var loglevel = { var log = loglevelExports; // global variables black list, forbidden to use in for jsQuery/jsAction -var functionBlacklist = new Set([ +const functionBlacklist = new Set([ "top", "parent", "document", @@ -1132,17 +1067,18 @@ var functionBlacklist = new Set([ "Navigator", "MutationObserver", ]); -var expressionBlacklist = new Set(__spreadArray(__spreadArray([], Array.from(functionBlacklist.values()), true), [ +const expressionBlacklist = new Set([ + ...Array.from(functionBlacklist.values()), "setTimeout", "setInterval", "setImmediate", -], false)); -var globalVarNames = new Set(["window", "globalThis", "self", "global"]); +]); +const globalVarNames = new Set(["window", "globalThis", "self", "global"]); function createBlackHole() { return new Proxy(function () { return createBlackHole(); }, { - get: function (t, p, r) { + get(t, p, r) { if (p === "toString") { return function () { return ""; @@ -1153,32 +1089,31 @@ function createBlackHole() { return ""; }; } - log.log("[Sandbox] access ".concat(String(p), " on black hole, return mock object")); + log.log(`[Sandbox] access ${String(p)} on black hole, return mock object`); return createBlackHole(); }, }); } -function createMockWindow(base, blacklist, onSet, disableLimit) { - if (blacklist === void 0) { blacklist = expressionBlacklist; } - var win = new Proxy(Object.assign({}, base), { - has: function () { +function createMockWindow(base, blacklist = expressionBlacklist, onSet, disableLimit) { + const win = new Proxy(Object.assign({}, base), { + has() { return true; }, - set: function (target, p, newValue) { + set(target, p, newValue) { if (typeof p === "string") { - onSet === null || onSet === void 0 ? void 0 : onSet(p); + onSet?.(p); } return Reflect.set(target, p, newValue); }, - get: function (target, p) { + get(target, p) { if (p in target) { return Reflect.get(target, p); } if (globalVarNames.has(p)) { return win; } - if (typeof p === "string" && (blacklist === null || blacklist === void 0 ? void 0 : blacklist.has(p)) && !disableLimit) { - log.log("[Sandbox] access ".concat(String(p), " on mock window, return mock object")); + if (typeof p === "string" && blacklist?.has(p) && !disableLimit) { + log.log(`[Sandbox] access ${String(p)} on mock window, return mock object`); return createBlackHole(); } return getPropertyFromNativeWindow(p); @@ -1186,8 +1121,8 @@ function createMockWindow(base, blacklist, onSet, disableLimit) { }); return win; } -var mockWindow; -var currentDisableLimit = false; +let mockWindow; +let currentDisableLimit = false; function clearMockWindow() { mockWindow = createMockWindow(); } @@ -1195,7 +1130,7 @@ function isDomElement(obj) { return obj instanceof Element || obj instanceof HTMLCollection; } function getPropertyFromNativeWindow(prop) { - var ret = Reflect.get(window, prop); + const ret = Reflect.get(window, prop); if (typeof ret === "function" && !ret.prototype) { return ret.bind(window); } @@ -1206,22 +1141,22 @@ function getPropertyFromNativeWindow(prop) { return ret; } function proxySandbox(context, methods, options) { - var _a = options || {}, _b = _a.disableLimit, disableLimit = _b === void 0 ? false : _b, _c = _a.scope, scope = _c === void 0 ? "expression" : _c, onSetGlobalVars = _a.onSetGlobalVars; - var isProtectedVar = function (key) { + const { disableLimit = false, scope = "expression", onSetGlobalVars } = options || {}; + const isProtectedVar = (key) => { return key in context || key in (methods || {}) || globalVarNames.has(key); }; - var cache = {}; - var blacklist = scope === "function" ? functionBlacklist : expressionBlacklist; + const cache = {}; + const blacklist = scope === "function" ? functionBlacklist : expressionBlacklist; if (scope === "function" || !mockWindow || disableLimit !== currentDisableLimit) { mockWindow = createMockWindow(mockWindow, blacklist, onSetGlobalVars, disableLimit); } currentDisableLimit = disableLimit; return new Proxy(mockWindow, { - has: function (target, p) { + has(target, p) { // proxy all variables return true; }, - get: function (target, p, receiver) { + get(target, p, receiver) { if (p === Symbol.unscopables) { return undefined; } @@ -1235,7 +1170,7 @@ function proxySandbox(context, methods, options) { if (p in cache) { return Reflect.get(cache, p); } - var value = Reflect.get(context, p, receiver); + let value = Reflect.get(context, p, receiver); if (typeof value === "object" && value !== null) { if (methods && p in methods) { value = Object.assign({}, value, Reflect.get(methods, p)); @@ -1251,38 +1186,43 @@ function proxySandbox(context, methods, options) { } return Reflect.get(target, p, receiver); }, - set: function (target, p, value, receiver) { + set(target, p, value, receiver) { if (isProtectedVar(p)) { throw new Error(p.toString() + " can't be modified"); } return Reflect.set(target, p, value, receiver); }, - defineProperty: function (target, p, attributes) { + defineProperty(target, p, attributes) { if (isProtectedVar(p)) { throw new Error("can't define property:" + p.toString()); } return Reflect.defineProperty(target, p, attributes); }, - deleteProperty: function (target, p) { + deleteProperty(target, p) { if (isProtectedVar(p)) { throw new Error("can't delete property:" + p.toString()); } return Reflect.deleteProperty(target, p); }, - setPrototypeOf: function (target, v) { + setPrototypeOf(target, v) { throw new Error("can't invoke setPrototypeOf"); }, }); } function evalScript(script, context, methods) { - return evalFunc("return (".concat(script, "\n);"), context, methods); + return evalFunc(`return (${script}\n);`, context, methods); } function evalFunc(functionBody, context, methods, options, isAsync) { - var code = "with(this){\n return (".concat(isAsync ? "async " : "", "function() {\n 'use strict';\n ").concat(functionBody, ";\n }).call(this);\n }"); + const code = `with(this){ + return (${isAsync ? "async " : ""}function() { + 'use strict'; + ${functionBody}; + }).call(this); + }`; // eslint-disable-next-line no-new-func - var vm = new Function(code); - var sandbox = proxySandbox(context, methods, options); - var result = vm.call(sandbox); + const vm = new Function(code); + const sandbox = proxySandbox(context, methods, options); + const result = vm.call(sandbox); return result; } @@ -1468,7 +1408,7 @@ function call(content, context, segment) { return new ValueAndMsg("", undefined, { segments: [{ value: segment, success: true }] }); } try { - var value = evalScript(content, context); + const value = evalScript(content, context); return new ValueAndMsg(value, undefined, { segments: [{ value: segment, success: true }] }); } catch (err) { @@ -1480,65 +1420,60 @@ function call(content, context, segment) { function evalDefault(unevaledValue, context) { return new DefaultParser(unevaledValue, context).parse(); } -var DefaultParser = /** @class */ (function () { - function DefaultParser(unevaledValue, context) { +class DefaultParser { + context; + segments; + valueAndMsgs = []; + constructor(unevaledValue, context) { this.context = context; - this.valueAndMsgs = []; this.segments = getDynamicStringSegments(unevaledValue.trim()); } - DefaultParser.prototype.parse = function () { - var _a; + parse() { try { - var object = this.parseObject(); + const object = this.parseObject(); if (this.valueAndMsgs.length === 0) { return new ValueAndMsg(object); } - return new ValueAndMsg(object, (_a = _.find(this.valueAndMsgs, "msg")) === null || _a === void 0 ? void 0 : _a.msg, { - segments: this.valueAndMsgs.flatMap(function (v) { var _a, _b; return (_b = (_a = v === null || v === void 0 ? void 0 : v.extra) === null || _a === void 0 ? void 0 : _a.segments) !== null && _b !== void 0 ? _b : []; }), + return new ValueAndMsg(object, _.find(this.valueAndMsgs, "msg")?.msg, { + segments: this.valueAndMsgs.flatMap((v) => v?.extra?.segments ?? []), }); } catch (err) { // return null, the later transform will determine the default value return new ValueAndMsg("", getErrorMessage(err)); } - }; - DefaultParser.prototype.parseObject = function () { - var _this = this; - var values = this.segments.map(function (segment) { - return isDynamicSegment(segment) ? _this.evalDynamicSegment(segment) : segment; - }); + } + parseObject() { + const values = this.segments.map((segment) => isDynamicSegment(segment) ? this.evalDynamicSegment(segment) : segment); return values.length === 1 ? values[0] : values.join(""); - }; - DefaultParser.prototype.evalDynamicSegment = function (segment) { - var valueAndMsg = call(segment.slice(2, -2).trim(), this.context, segment); + } + evalDynamicSegment(segment) { + const valueAndMsg = call(segment.slice(2, -2).trim(), this.context, segment); this.valueAndMsgs.push(valueAndMsg); return valueAndMsg.value; - }; - return DefaultParser; -}()); + } +} function evalJson(unevaledValue, context) { return new RelaxedJsonParser(unevaledValue, context).parse(); } // this will also be used in node-service -var RelaxedJsonParser = /** @class */ (function (_super) { - __extends(RelaxedJsonParser, _super); - function RelaxedJsonParser(unevaledValue, context) { - var _this = _super.call(this, unevaledValue, context) || this; - _this.evalIndexedObject = _this.evalIndexedObject.bind(_this); - return _this; +class RelaxedJsonParser extends DefaultParser { + constructor(unevaledValue, context) { + super(unevaledValue, context); + this.evalIndexedObject = this.evalIndexedObject.bind(this); } - RelaxedJsonParser.prototype.parseObject = function () { + parseObject() { try { return this.parseRelaxedJson(); } catch (e) { - return _super.prototype.parseObject.call(this); + return super.parseObject(); } - }; - RelaxedJsonParser.prototype.parseRelaxedJson = function () { + } + parseRelaxedJson() { // replace the original {{...}} as relaxed-json adaptive \{\{ + ${index} + \}\} - var indexedRelaxedJsonString = this.segments - .map(function (s, i) { return (isDynamicSegment(s) ? "\\{\\{" + i + "\\}\\}" : s); }) + const indexedRelaxedJsonString = this.segments + .map((s, i) => (isDynamicSegment(s) ? "\\{\\{" + i + "\\}\\}" : s)) .join(""); if (indexedRelaxedJsonString.length === 0) { // return empty, let the later transform determines the default value @@ -1546,13 +1481,13 @@ var RelaxedJsonParser = /** @class */ (function (_super) { } // transform to standard JSON strings with RELAXED JSON // here is a trick: if "\{\{ \}\}" is in quotes, keep it unchanged; otherwise transform to "{{ }}" - var indexedJsonString = relaxedJSONToJSON(indexedRelaxedJsonString, true); + const indexedJsonString = relaxedJSONToJSON(indexedRelaxedJsonString, true); // here use eval instead of JSON.parse, in order to support escaping like JavaScript. JSON.parse will cause error when escaping non-spicial char // since eval support escaping, replace "\{\{ + ${index} + \}\}" as "\\{\\{ + ${index} + \\}\\}" - var indexedJsonObject = evalScript(indexedJsonString.replace(/\\{\\{\d+\\}\\}/g, function (s) { return "\\\\{\\\\{" + s.slice(4, -4) + "\\\\}\\\\}"; }), {}); + const indexedJsonObject = evalScript(indexedJsonString.replace(/\\{\\{\d+\\}\\}/g, (s) => "\\\\{\\\\{" + s.slice(4, -4) + "\\\\}\\\\}"), {}); return this.evalIndexedObject(indexedJsonObject); - }; - RelaxedJsonParser.prototype.evalIndexedObject = function (obj) { + } + evalIndexedObject(obj) { if (typeof obj === "string") { return this.evalIndexedStringToObject(obj); } @@ -1562,91 +1497,72 @@ var RelaxedJsonParser = /** @class */ (function (_super) { if (Array.isArray(obj)) { return obj.map(this.evalIndexedObject); } - var ret = {}; - for (var _i = 0, _a = Object.entries(obj); _i < _a.length; _i++) { - var _b = _a[_i], key = _b[0], value = _b[1]; + const ret = {}; + for (const [key, value] of Object.entries(obj)) { ret[this.evalIndexedStringToString(key)] = this.evalIndexedObject(value); } return ret; - }; - RelaxedJsonParser.prototype.evalIndexedStringToObject = function (indexedString) { + } + evalIndexedStringToObject(indexedString) { // if the whole string is "{{ + ${index} + }}", it indicates that the original "{{...}}" is not in quotes, as a standalone JSON value. if (indexedString.match(/^{{\d+}}$/)) { return this.evalIndexedSnippet(indexedString); } return this.evalIndexedStringToString(indexedString); - }; - RelaxedJsonParser.prototype.evalIndexedStringToString = function (indexedString) { - var _this = this; + } + evalIndexedStringToString(indexedString) { // replace all {{ + ${index} + }} and \{\{ + ${index} \}\} - return indexedString.replace(/({{\d+}})|(\\{\\{\d+\\}\\})/g, function (s) { return _this.evalIndexedSnippet(s) + ""; }); - }; + return indexedString.replace(/({{\d+}})|(\\{\\{\d+\\}\\})/g, (s) => this.evalIndexedSnippet(s) + ""); + } // eval {{ + ${index} + }} or \{\{ + ${index} + \}\} - RelaxedJsonParser.prototype.evalIndexedSnippet = function (snippet) { - var index = parseInt(snippet.startsWith("{{") ? snippet.slice(2, -2) : snippet.slice(4, -4)); + evalIndexedSnippet(snippet) { + const index = parseInt(snippet.startsWith("{{") ? snippet.slice(2, -2) : snippet.slice(4, -4)); if (index >= 0 && index < this.segments.length) { - var segment = this.segments[index]; + const segment = this.segments[index]; if (isDynamicSegment(segment)) { return this.evalDynamicSegment(segment); } } return snippet; - }; - return RelaxedJsonParser; -}(DefaultParser)); + } +} function evalFunction(unevaledValue, context, methods, isAsync) { try { - return new ValueAndMsg(function (args, runInHost, scope) { - if (runInHost === void 0) { runInHost = false; } - if (scope === void 0) { scope = "function"; } - return evalFunc(unevaledValue.startsWith("return") - ? unevaledValue + "\n" - : "return ".concat(isAsync ? "async " : "", "function(){'use strict'; ").concat(unevaledValue, "\n}()"), args ? __assign(__assign({}, context), args) : context, methods, { disableLimit: runInHost, scope: scope }, isAsync); - }); + return new ValueAndMsg((args, runInHost = false, scope = "function") => evalFunc(unevaledValue.startsWith("return") + ? unevaledValue + "\n" + : `return ${isAsync ? "async " : ""}function(){'use strict'; ${unevaledValue}\n}()`, args ? { ...context, ...args } : context, methods, { disableLimit: runInHost, scope }, isAsync)); } catch (err) { - return new ValueAndMsg(function () { }, getErrorMessage(err)); - } -} -function evalFunctionResult(unevaledValue, context, methods) { - return __awaiter(this, void 0, void 0, function () { - var valueAndMsg, _a, err_1; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - valueAndMsg = evalFunction(unevaledValue, context, methods, true); - if (valueAndMsg.hasError()) { - return [2 /*return*/, new ValueAndMsg("", valueAndMsg.msg)]; - } - _b.label = 1; - case 1: - _b.trys.push([1, 3, , 4]); - _a = ValueAndMsg.bind; - return [4 /*yield*/, valueAndMsg.value()]; - case 2: return [2 /*return*/, new (_a.apply(ValueAndMsg, [void 0, _b.sent()]))()]; - case 3: - err_1 = _b.sent(); - return [2 /*return*/, new ValueAndMsg("", getErrorMessage(err_1))]; - case 4: return [2 /*return*/]; - } - }); - }); + return new ValueAndMsg(() => { }, getErrorMessage(err)); + } +} +async function evalFunctionResult(unevaledValue, context, methods) { + const valueAndMsg = evalFunction(unevaledValue, context, methods, true); + if (valueAndMsg.hasError()) { + return new ValueAndMsg("", valueAndMsg.msg); + } + try { + return new ValueAndMsg(await valueAndMsg.value()); + } + catch (err) { + return new ValueAndMsg("", getErrorMessage(err)); + } } function string2Fn(unevaledValue, type, methods) { if (type) { switch (type) { case "JSON": - return function (context) { return evalJson(unevaledValue, context); }; + return (context) => evalJson(unevaledValue, context); case "Function": - return function (context) { return evalFunction(unevaledValue, context, methods); }; + return (context) => evalFunction(unevaledValue, context, methods); } } - return function (context) { return evalDefault(unevaledValue, context); }; + return (context) => evalDefault(unevaledValue, context); } -var IS_FETCHING_FIELD = "isFetching"; -var LATEST_END_TIME_FIELD = "latestEndTime"; -var TRIGGER_TYPE_FIELD = "triggerType"; +const IS_FETCHING_FIELD = "isFetching"; +const LATEST_END_TIME_FIELD = "latestEndTime"; +const TRIGGER_TYPE_FIELD = "triggerType"; /** * user input node * @@ -1656,66 +1572,62 @@ var TRIGGER_TYPE_FIELD = "triggerType"; * * FIXME(libin): distinguish Json CodeNode,since wrapContext may cause problems. */ -var CodeNode = /** @class */ (function (_super) { - __extends(CodeNode, _super); - function CodeNode(unevaledValue, options) { - var _this = this; - var _a; - _this = _super.call(this) || this; - _this.unevaledValue = unevaledValue; - _this.options = options; - _this.type = "input"; - _this.directDepends = new Map(); - _this.codeType = options === null || options === void 0 ? void 0 : options.codeType; - _this.evalWithMethods = (_a = options === null || options === void 0 ? void 0 : options.evalWithMethods) !== null && _a !== void 0 ? _a : true; - return _this; +class CodeNode extends AbstractNode { + unevaledValue; + options; + type = "input"; + codeType; + evalWithMethods; + directDepends = new Map(); + constructor(unevaledValue, options) { + super(); + this.unevaledValue = unevaledValue; + this.options = options; + this.codeType = options?.codeType; + this.evalWithMethods = options?.evalWithMethods ?? true; } // FIXME: optimize later - CodeNode.prototype.convertedValue = function () { + convertedValue() { if (this.codeType === "Function") { - return "{{function(){".concat(this.unevaledValue, "}}}"); + return `{{function(){${this.unevaledValue}}}}`; } return this.unevaledValue; - }; - CodeNode.prototype.filterNodes = function (exposingNodes) { + } + filterNodes(exposingNodes) { if (!!this.evalCache.inFilterNodes) { return new Map(); } this.evalCache.inFilterNodes = true; try { - var filteredDepends = this.filterDirectDepends(exposingNodes); + const filteredDepends = this.filterDirectDepends(exposingNodes); // log.log("unevaledValue: ", this.unevaledValue, "\nfilteredDepends:", filteredDepends); - var result_1 = addDepends(new Map(), filteredDepends); - filteredDepends.forEach(function (paths, node) { - addDepends(result_1, node.filterNodes(exposingNodes)); + const result = addDepends(new Map(), filteredDepends); + filteredDepends.forEach((paths, node) => { + addDepends(result, node.filterNodes(exposingNodes)); }); // Add isFetching & latestEndTime node for FetchCheck - var topDepends = filterDepends(this.convertedValue(), exposingNodes, 1); - topDepends.forEach(function (paths, depend) { + const topDepends = filterDepends(this.convertedValue(), exposingNodes, 1); + topDepends.forEach((paths, depend) => { if (nodeIsRecord(depend)) { - var _loop_1 = function (field) { - var node = depend.children[field]; + for (const field of [IS_FETCHING_FIELD, LATEST_END_TIME_FIELD]) { + const node = depend.children[field]; if (node) { - addDepend(result_1, node, Array.from(paths).map(function (p) { return p + "." + field; })); + addDepend(result, node, Array.from(paths).map((p) => p + "." + field)); } - }; - for (var _i = 0, _a = [IS_FETCHING_FIELD, LATEST_END_TIME_FIELD]; _i < _a.length; _i++) { - var field = _a[_i]; - _loop_1(field); } } }); - return result_1; + return result; } finally { this.evalCache.inFilterNodes = false; } - }; + } // only includes direct depends, exlucdes depends of dependencies - CodeNode.prototype.filterDirectDepends = function (exposingNodes) { + filterDirectDepends(exposingNodes) { return filterDepends(this.convertedValue(), exposingNodes); - }; - CodeNode.prototype.justEval = function (exposingNodes, methods) { + } + justEval(exposingNodes, methods) { // log.log("justEval: ", this, "\nexposingNodes: ", exposingNodes); if (!!this.evalCache.inEval) { // found cyclic eval @@ -1724,12 +1636,12 @@ var CodeNode = /** @class */ (function (_super) { } this.evalCache.inEval = true; try { - var dependingNodeMap = this.filterDirectDepends(exposingNodes); + const dependingNodeMap = this.filterDirectDepends(exposingNodes); this.directDepends = dependingNodeMap; - var dependingNodes = mergeNodesWithSameName(dependingNodeMap); - var fn = string2Fn(this.unevaledValue, this.codeType, this.evalWithMethods ? methods : {}); - var evalNode = withFunction(fromRecord(dependingNodes), fn); - var valueAndMsg = evalNode.evaluate(exposingNodes); + const dependingNodes = mergeNodesWithSameName(dependingNodeMap); + const fn = string2Fn(this.unevaledValue, this.codeType, this.evalWithMethods ? methods : {}); + const evalNode = withFunction(fromRecord(dependingNodes), fn); + let valueAndMsg = evalNode.evaluate(exposingNodes); // log.log("unevaledValue: ", this.unevaledValue, "\ndependingNodes: ", dependingNodes, "\nvalueAndMsg: ", valueAndMsg); if (this.evalCache.cyclic) { valueAndMsg = new ValueAndMsg(valueAndMsg.value, (valueAndMsg.msg ? valueAndMsg.msg + "\n" : "") + dependsErrorMessage(this), fixCyclic(valueAndMsg.extra, exposingNodes)); @@ -1739,25 +1651,25 @@ var CodeNode = /** @class */ (function (_super) { finally { this.evalCache.inEval = false; } - }; - CodeNode.prototype.getChildren = function () { + } + getChildren() { if (this.directDepends) { return Array.from(this.directDepends.keys()); } return []; - }; - CodeNode.prototype.dependValues = function () { - var ret = {}; - this.directDepends.forEach(function (paths, node) { + } + dependValues() { + let ret = {}; + this.directDepends.forEach((paths, node) => { if (node instanceof AbstractNode) { - paths.forEach(function (path) { + paths.forEach((path) => { ret[path] = node.evalCache.value; }); } }); return ret; - }; - CodeNode.prototype.fetchInfo = function (exposingNodes, options) { + } + fetchInfo(exposingNodes, options) { if (!!this.evalCache.inIsFetching) { return { isFetching: false, @@ -1766,67 +1678,65 @@ var CodeNode = /** @class */ (function (_super) { } this.evalCache.inIsFetching = true; try { - var topDepends = filterDepends(this.convertedValue(), exposingNodes, 1); - var isFetching_1 = false; - var ready_1 = true; - topDepends.forEach(function (paths, depend) { - var pathsArr = Array.from(paths); - var value = depend.evaluate(exposingNodes); - if ((options === null || options === void 0 ? void 0 : options.ignoreManualDepReadyStatus) && + const topDepends = filterDepends(this.convertedValue(), exposingNodes, 1); + let isFetching = false; + let ready = true; + topDepends.forEach((paths, depend) => { + const pathsArr = Array.from(paths); + const value = depend.evaluate(exposingNodes); + if (options?.ignoreManualDepReadyStatus && _.has(value, TRIGGER_TYPE_FIELD) && value.triggerType === "manual") { return; } // if query is dependent on itself, mark as ready - if ((pathsArr === null || pathsArr === void 0 ? void 0 : pathsArr[0]) === (options === null || options === void 0 ? void 0 : options.queryName)) + if (pathsArr?.[0] === options?.queryName) return; // wait for lazy loaded comps to load before executing query on page load if (value && !Object.keys(value).length && paths.size) { - isFetching_1 = true; - ready_1 = false; + isFetching = true; + ready = false; } if (_.has(value, IS_FETCHING_FIELD)) { - isFetching_1 = isFetching_1 || value.isFetching === true; + isFetching = isFetching || value.isFetching === true; } if (_.has(value, LATEST_END_TIME_FIELD)) { - ready_1 = ready_1 && value.latestEndTime > 0; + ready = ready && value.latestEndTime > 0; } }); - var dependingNodeMap = this.filterNodes(exposingNodes); - dependingNodeMap.forEach(function (paths, depend) { - var fi = depend.fetchInfo(exposingNodes, options); - isFetching_1 = isFetching_1 || fi.isFetching; - ready_1 = ready_1 && fi.ready; + const dependingNodeMap = this.filterNodes(exposingNodes); + dependingNodeMap.forEach((paths, depend) => { + const fi = depend.fetchInfo(exposingNodes, options); + isFetching = isFetching || fi.isFetching; + ready = ready && fi.ready; }); return { - isFetching: isFetching_1, - ready: ready_1, + isFetching, + ready: ready, }; } finally { this.evalCache.inIsFetching = false; } - }; - __decorate([ - memoized() - ], CodeNode.prototype, "filterNodes", null); - __decorate([ - memoized() - ], CodeNode.prototype, "filterDirectDepends", null); - __decorate([ - memoized() - ], CodeNode.prototype, "fetchInfo", null); - return CodeNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], CodeNode.prototype, "filterNodes", null); +__decorate([ + memoized() +], CodeNode.prototype, "filterDirectDepends", null); +__decorate([ + memoized() +], CodeNode.prototype, "fetchInfo", null); /** * generate node for unevaledValue */ function fromUnevaledValue(unevaledValue) { - return new FunctionNode(new CodeNode(unevaledValue), function (valueAndMsg) { return valueAndMsg.value; }); + return new FunctionNode(new CodeNode(unevaledValue), (valueAndMsg) => valueAndMsg.value); } function fixCyclic(extra, exposingNodes) { - var _a; - (_a = extra === null || extra === void 0 ? void 0 : extra.segments) === null || _a === void 0 ? void 0 : _a.forEach(function (segment) { + extra?.segments?.forEach((segment) => { if (segment.success) { segment.success = !hasCycle(segment.value, exposingNodes); } @@ -1837,38 +1747,37 @@ function fixCyclic(extra, exposingNodes) { /** * evaluate to get FetchInfo or fetching status */ -var FetchCheckNode = /** @class */ (function (_super) { - __extends(FetchCheckNode, _super); - function FetchCheckNode(child, options) { - var _this = _super.call(this) || this; - _this.child = child; - _this.options = options; - _this.type = "fetchCheck"; - return _this; - } - FetchCheckNode.prototype.filterNodes = function (exposingNodes) { +class FetchCheckNode extends AbstractNode { + child; + options; + type = "fetchCheck"; + constructor(child, options) { + super(); + this.child = child; + this.options = options; + } + filterNodes(exposingNodes) { return this.child.filterNodes(exposingNodes); - }; - FetchCheckNode.prototype.justEval = function (exposingNodes) { + } + justEval(exposingNodes) { return this.fetchInfo(exposingNodes); - }; - FetchCheckNode.prototype.getChildren = function () { + } + getChildren() { return [this.child]; - }; - FetchCheckNode.prototype.dependValues = function () { + } + dependValues() { return this.child.dependValues(); - }; - FetchCheckNode.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return this.child.fetchInfo(exposingNodes, this.options); - }; - __decorate([ - memoized() - ], FetchCheckNode.prototype, "filterNodes", null); - __decorate([ - memoized() - ], FetchCheckNode.prototype, "fetchInfo", null); - return FetchCheckNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], FetchCheckNode.prototype, "filterNodes", null); +__decorate([ + memoized() +], FetchCheckNode.prototype, "fetchInfo", null); function isFetching(node) { return new FetchCheckNode(node); } @@ -3104,48 +3013,46 @@ var LRU = LRUCache; /** * directly provide data */ -var SimpleNode = /** @class */ (function (_super) { - __extends(SimpleNode, _super); - function SimpleNode(value) { - var _this = _super.call(this) || this; - _this.value = value; - _this.type = "simple"; - return _this; +class SimpleNode extends AbstractNode { + value; + type = "simple"; + constructor(value) { + super(); + this.value = value; } - SimpleNode.prototype.filterNodes = function (exposingNodes) { - return evalPerfUtil.perf(this, "filterNodes", function () { + filterNodes(exposingNodes) { + return evalPerfUtil.perf(this, "filterNodes", () => { return new Map(); }); - }; - SimpleNode.prototype.justEval = function (exposingNodes) { + } + justEval(exposingNodes) { return this.value; - }; - SimpleNode.prototype.getChildren = function () { + } + getChildren() { return []; - }; - SimpleNode.prototype.dependValues = function () { + } + dependValues() { return {}; - }; - SimpleNode.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return { isFetching: false, ready: true, }; - }; - __decorate([ - memoized() - ], SimpleNode.prototype, "filterNodes", null); - return SimpleNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], SimpleNode.prototype, "filterNodes", null); /** * provide simple value, don't need to eval */ function fromValue(value) { return new SimpleNode(value); } -var lru = new LRU({ max: 16384 }); +const lru = new LRU({ max: 16384 }); function fromValueWithCache(value) { - var res = lru.get(value); + let res = lru.get(value); if (res === undefined) { res = fromValue(value); lru.set(value, res); @@ -3154,102 +3061,101 @@ function fromValueWithCache(value) { } // encapsulate module node, use specified exposing nodes and input nodes -var WrapNode = /** @class */ (function (_super) { - __extends(WrapNode, _super); - function WrapNode(delegate, moduleExposingNodes, moduleExposingMethods, inputNodes) { - var _this = _super.call(this) || this; - _this.delegate = delegate; - _this.moduleExposingNodes = moduleExposingNodes; - _this.moduleExposingMethods = moduleExposingMethods; - _this.inputNodes = inputNodes; - _this.type = "wrap"; - return _this; - } - WrapNode.prototype.wrap = function (exposingNodes, exposingMethods) { +class WrapNode extends AbstractNode { + delegate; + moduleExposingNodes; + moduleExposingMethods; + inputNodes; + type = "wrap"; + constructor(delegate, moduleExposingNodes, moduleExposingMethods, inputNodes) { + super(); + this.delegate = delegate; + this.moduleExposingNodes = moduleExposingNodes; + this.moduleExposingMethods = moduleExposingMethods; + this.inputNodes = inputNodes; + } + wrap(exposingNodes, exposingMethods) { if (!this.inputNodes) { return this.moduleExposingNodes; } - var inputNodeEntries = Object.entries(this.inputNodes); + const inputNodeEntries = Object.entries(this.inputNodes); if (inputNodeEntries.length === 0) { return this.moduleExposingNodes; } - var inputNodes = {}; - inputNodeEntries.forEach(function (_a) { - var name = _a[0], node = _a[1]; - var targetNode = typeof node === "string" ? exposingNodes[node] : node; + const inputNodes = {}; + inputNodeEntries.forEach(([name, node]) => { + let targetNode = typeof node === "string" ? exposingNodes[node] : node; if (!targetNode) { return; } inputNodes[name] = new WrapNode(targetNode, exposingNodes, exposingMethods); }); - return __assign(__assign({}, this.moduleExposingNodes), inputNodes); - }; - WrapNode.prototype.filterNodes = function (exposingNodes) { + return { + ...this.moduleExposingNodes, + ...inputNodes, + }; + } + filterNodes(exposingNodes) { return this.delegate.filterNodes(this.wrap(exposingNodes, {})); - }; - WrapNode.prototype.justEval = function (exposingNodes, methods) { + } + justEval(exposingNodes, methods) { return this.delegate.evaluate(this.wrap(exposingNodes, methods), this.moduleExposingMethods); - }; - WrapNode.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return this.delegate.fetchInfo(this.wrap(exposingNodes, {})); - }; - WrapNode.prototype.getChildren = function () { + } + getChildren() { return [this.delegate]; - }; - WrapNode.prototype.dependValues = function () { + } + dependValues() { return {}; - }; - __decorate([ - memoized() - ], WrapNode.prototype, "filterNodes", null); - __decorate([ - memoized() - ], WrapNode.prototype, "fetchInfo", null); - return WrapNode; -}(AbstractNode)); - -var WrapContextNode = /** @class */ (function (_super) { - __extends(WrapContextNode, _super); - function WrapContextNode(child) { - var _this = _super.call(this) || this; - _this.child = child; - _this.type = "wrapContext"; - return _this; } - WrapContextNode.prototype.filterNodes = function (exposingNodes) { +} +__decorate([ + memoized() +], WrapNode.prototype, "filterNodes", null); +__decorate([ + memoized() +], WrapNode.prototype, "fetchInfo", null); + +class WrapContextNode extends AbstractNode { + child; + type = "wrapContext"; + constructor(child) { + super(); + this.child = child; + } + filterNodes(exposingNodes) { return this.child.filterNodes(exposingNodes); - }; - WrapContextNode.prototype.justEval = function (exposingNodes, methods) { - var _this = this; - return function (params) { - var nodes; + } + justEval(exposingNodes, methods) { + return (params) => { + let nodes; if (params) { - nodes = __assign({}, exposingNodes); - Object.entries(params).forEach(function (_a) { - var key = _a[0], value = _a[1]; + nodes = { ...exposingNodes }; + Object.entries(params).forEach(([key, value]) => { nodes[key] = fromValueWithCache(value); }); } else { nodes = exposingNodes; } - return _this.child.evaluate(nodes, methods); + return this.child.evaluate(nodes, methods); }; - }; - WrapContextNode.prototype.getChildren = function () { + } + getChildren() { return [this.child]; - }; - WrapContextNode.prototype.dependValues = function () { + } + dependValues() { return this.child.dependValues(); - }; - WrapContextNode.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return this.child.fetchInfo(exposingNodes); - }; - __decorate([ - memoized() - ], WrapContextNode.prototype, "filterNodes", null); - return WrapContextNode; -}(AbstractNode)); + } +} +__decorate([ + memoized() +], WrapContextNode.prototype, "filterNodes", null); function wrapContext(node) { return new WrapContextNode(node); } @@ -3257,59 +3163,57 @@ function wrapContext(node) { /** * build a new node by setting new dependent nodes in child node */ -var WrapContextNodeV2 = /** @class */ (function (_super) { - __extends(WrapContextNodeV2, _super); - function WrapContextNodeV2(child, paramNodes) { - var _this = _super.call(this) || this; - _this.child = child; - _this.paramNodes = paramNodes; - _this.type = "wrapContextV2"; - return _this; - } - WrapContextNodeV2.prototype.filterNodes = function (exposingNodes) { +class WrapContextNodeV2 extends AbstractNode { + child; + paramNodes; + type = "wrapContextV2"; + constructor(child, paramNodes) { + super(); + this.child = child; + this.paramNodes = paramNodes; + } + filterNodes(exposingNodes) { return this.child.filterNodes(exposingNodes); - }; - WrapContextNodeV2.prototype.justEval = function (exposingNodes, methods) { + } + justEval(exposingNodes, methods) { return this.child.evaluate(this.wrap(exposingNodes), methods); - }; - WrapContextNodeV2.prototype.getChildren = function () { + } + getChildren() { return [this.child]; - }; - WrapContextNodeV2.prototype.dependValues = function () { + } + dependValues() { return this.child.dependValues(); - }; - WrapContextNodeV2.prototype.fetchInfo = function (exposingNodes) { + } + fetchInfo(exposingNodes) { return this.child.fetchInfo(this.wrap(exposingNodes)); - }; - WrapContextNodeV2.prototype.wrap = function (exposingNodes) { - return __assign(__assign({}, exposingNodes), this.paramNodes); - }; - __decorate([ - memoized() - ], WrapContextNodeV2.prototype, "filterNodes", null); - __decorate([ - memoized() - ], WrapContextNodeV2.prototype, "wrap", null); - return WrapContextNodeV2; -}(AbstractNode)); + } + wrap(exposingNodes) { + return { ...exposingNodes, ...this.paramNodes }; + } +} +__decorate([ + memoized() +], WrapContextNodeV2.prototype, "filterNodes", null); +__decorate([ + memoized() +], WrapContextNodeV2.prototype, "wrap", null); function transformWrapper(transformFn, defaultValue) { function transformWithMsg(valueAndMsg) { - var _a; - var result; + let result; try { - var value = transformFn(valueAndMsg.value); + const value = transformFn(valueAndMsg.value); result = new ValueAndMsg(value, valueAndMsg.msg, valueAndMsg.extra, valueAndMsg.value); } catch (err) { - var value = void 0; + let value; try { - value = defaultValue !== null && defaultValue !== void 0 ? defaultValue : transformFn(""); + value = defaultValue ?? transformFn(""); } catch (err2) { value = undefined; } - var errorMsg = (_a = valueAndMsg.msg) !== null && _a !== void 0 ? _a : getErrorMessage(err); + const errorMsg = valueAndMsg.msg ?? getErrorMessage(err); result = new ValueAndMsg(value, errorMsg, valueAndMsg.extra, valueAndMsg.value); } // log.trace( @@ -3326,34 +3230,33 @@ function transformWrapper(transformFn, defaultValue) { } function styleNamespace(id) { - return "style-for-".concat(id); + return `style-for-${id}`; } function evalStyle(id, css, globalStyle) { - var _a; - var styleId = styleNamespace(id); - var prefixId = globalStyle ? id : ".".concat(id); - var compiledCSS = ""; - css.forEach(function (i) { + const styleId = styleNamespace(id); + const prefixId = globalStyle ? id : `.${id}`; + let compiledCSS = ""; + css.forEach((i) => { if (!i.trim()) { return; } - compiledCSS += serialize(compile("".concat(prefixId, "{").concat(i, "}")), middleware([prefixer, stringify])); + compiledCSS += serialize(compile(`${prefixId}{${i}}`), middleware([prefixer, stringify])); }); - var styleNode = document.querySelector("#".concat(styleId)); + let styleNode = document.querySelector(`#${styleId}`); if (!styleNode) { styleNode = document.createElement("style"); styleNode.setAttribute("type", "text/css"); styleNode.setAttribute("id", styleId); styleNode.setAttribute("data-style-src", "eval"); - (_a = document.querySelector("head")) === null || _a === void 0 ? void 0 : _a.appendChild(styleNode); + document.querySelector("head")?.appendChild(styleNode); } styleNode.textContent = compiledCSS; } function clearStyleEval(id) { - var styleId = id && styleNamespace(id); - var styleNode = document.querySelectorAll("style[data-style-src=eval]"); + const styleId = id && styleNamespace(id); + const styleNode = document.querySelectorAll(`style[data-style-src=eval]`); if (styleNode) { - styleNode.forEach(function (i) { + styleNode.forEach((i) => { if (!styleId || styleId === i.id) { i.remove(); } @@ -3399,11 +3302,11 @@ function customAction(value, editDSL) { type: CompActionTypes.CUSTOM, path: [], value: value, - editDSL: editDSL, + editDSL, }; } function updateActionContextAction(context) { - var value = { + const value = { type: CompActionTypes.UPDATE_ACTION_CONTEXT, path: [], editDSL: false, @@ -3432,14 +3335,19 @@ function isCustomAction(action, type) { * RootComp will change the path correctly when queryName is passed. */ function executeQueryAction(props) { - return __assign({ type: CompActionTypes.EXECUTE_QUERY, path: [], editDSL: false }, props); + return { + type: CompActionTypes.EXECUTE_QUERY, + path: [], + editDSL: false, + ...props, + }; } function triggerModuleEventAction(name) { return { type: CompActionTypes.TRIGGER_MODULE_EVENT, path: [], editDSL: false, - name: name, + name, }; } /** @@ -3449,7 +3357,7 @@ function changeValueAction(value, editDSL) { return { type: CompActionTypes.CHANGE_VALUE, path: [], - editDSL: editDSL, + editDSL, value: value, }; } @@ -3457,7 +3365,7 @@ function isBroadcastAction(action, type) { return action.type === CompActionTypes.BROADCAST && _.get(action.action, "type") === type; } function renameAction(oldName, name) { - var value = { + const value = { type: CompActionTypes.RENAME, path: [], editDSL: true, @@ -3481,12 +3389,12 @@ function routeByNameAction(name, action) { }; } function multiChangeAction(changes) { - var editDSL = Object.values(changes).some(function (action) { return !!action.editDSL; }); - console.assert(Object.values(changes).every(function (action) { return !_.isNil(action.editDSL) && action.editDSL === editDSL; }), "multiChangeAction should wrap actions with the same editDSL value in property. editDSL: ".concat(editDSL, "\nchanges:"), changes); + const editDSL = Object.values(changes).some((action) => !!action.editDSL); + console.assert(Object.values(changes).every((action) => !_.isNil(action.editDSL) && action.editDSL === editDSL), `multiChangeAction should wrap actions with the same editDSL value in property. editDSL: ${editDSL}\nchanges:`, changes); return { type: CompActionTypes.MULTI_CHANGE, path: [], - editDSL: editDSL, + editDSL, changes: changes, }; } @@ -3513,14 +3421,16 @@ function onlyEvalAction() { }; } function wrapChildAction(childName, action) { - return __assign(__assign({}, action), { path: __spreadArray([childName], action.path, true) }); + return { + ...action, + path: [childName, ...action.path], + }; } function isChildAction(action) { - var _a, _b; - return ((_b = (_a = action === null || action === void 0 ? void 0 : action.path) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0; + return (action?.path?.length ?? 0) > 0; } function unwrapChildAction(action) { - return [action.path[0], __assign(__assign({}, action), { path: action.path.slice(1) })]; + return [action.path[0], { ...action, path: action.path.slice(1) }]; } function changeChildAction(childName, value, editDSL) { return wrapChildAction(childName, changeValueAction(value, editDSL)); @@ -3534,16 +3444,16 @@ function updateNodesV2Action(value) { }; } function wrapActionExtraInfo(action, extraInfos) { - return __assign(__assign({}, action), { extraInfo: __assign(__assign({}, action.extraInfo), extraInfos) }); + return { ...action, extraInfo: { ...action.extraInfo, ...extraInfos } }; } function deferAction(action) { - return __assign(__assign({}, action), { priority: "defer" }); + return { ...action, priority: "defer" }; } function changeEditDSLAction(action, editDSL) { - return __assign(__assign({}, action), { editDSL: editDSL }); + return { ...action, editDSL }; } -var CACHE_PREFIX = "__cache__"; +const CACHE_PREFIX = "__cache__"; /** * a decorator for caching function's result ignoring params. * @@ -3553,14 +3463,10 @@ var CACHE_PREFIX = "__cache__"; * */ function memo(target, propertyKey, descriptor) { - var originalMethod = descriptor.value; - var cachePropertyKey = CACHE_PREFIX + propertyKey; - descriptor.value = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - var thisObj = this; + const originalMethod = descriptor.value; + const cachePropertyKey = CACHE_PREFIX + propertyKey; + descriptor.value = function (...args) { + const thisObj = this; if (!thisObj[cachePropertyKey]) { // put the result into array, for representing `undefined` thisObj[cachePropertyKey] = [originalMethod.apply(this, args)]; @@ -3577,13 +3483,13 @@ function shallowEqual(obj1, obj2) { return true; } return (Object.keys(obj1).length === Object.keys(obj2).length && - Object.keys(obj1).every(function (key) { return obj2.hasOwnProperty(key) && obj1[key] === obj2[key]; })); + Object.keys(obj1).every((key) => obj2.hasOwnProperty(key) && obj1[key] === obj2[key])); } function containFields(obj, fields) { if (fields === undefined) { return true; } - var notEqualIndex = Object.keys(fields).findIndex(function (key) { + const notEqualIndex = Object.keys(fields).findIndex((key) => { return obj[key] !== fields[key]; }); return notEqualIndex === -1; @@ -3593,11 +3499,11 @@ function containFields(obj, fields) { * pros: this function can support private fields. */ function setFieldsNoTypeCheck(obj, fields, params) { - var res = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj); - Object.keys(res).forEach(function (key) { + const res = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj); + Object.keys(res).forEach((key) => { if (key.startsWith(CACHE_PREFIX)) { - var propertyKey = key.slice(CACHE_PREFIX.length); - if (!(params === null || params === void 0 ? void 0 : params.keepCacheKeys) || !(params === null || params === void 0 ? void 0 : params.keepCacheKeys.includes(propertyKey))) { + const propertyKey = key.slice(CACHE_PREFIX.length); + if (!params?.keepCacheKeys || !params?.keepCacheKeys.includes(propertyKey)) { delete res[key]; } } @@ -3605,35 +3511,34 @@ function setFieldsNoTypeCheck(obj, fields, params) { return Object.assign(res, fields); } -var AbstractComp = /** @class */ (function () { - function AbstractComp(params) { - var _a; - this.dispatch = (_a = params.dispatch) !== null && _a !== void 0 ? _a : (function (_action) { }); +class AbstractComp { + dispatch; + constructor(params) { + this.dispatch = params.dispatch ?? ((_action) => { }); } - AbstractComp.prototype.changeDispatch = function (dispatch) { + changeDispatch(dispatch) { return setFieldsNoTypeCheck(this, { dispatch: dispatch }, { keepCacheKeys: ["node"] }); - }; + } /** * trigger changeValueAction, type safe */ - AbstractComp.prototype.dispatchChangeValueAction = function (value) { + dispatchChangeValueAction(value) { this.dispatch(this.changeValueAction(value)); - }; - AbstractComp.prototype.changeValueAction = function (value) { + } + changeValueAction(value) { return changeValueAction(value, true); - }; + } /** * don't override the function, override nodeWithout function instead * FIXME: node reference mustn't be changed if this object is changed */ - AbstractComp.prototype.node = function () { + node() { return this.nodeWithoutCache(); - }; - __decorate([ - memo - ], AbstractComp.prototype, "node", null); - return AbstractComp; -}()); + } +} +__decorate([ + memo +], AbstractComp.prototype, "node", null); /** * wrap a dispatch as a child dispatch @@ -3643,7 +3548,7 @@ var AbstractComp = /** @class */ (function () { * @returns a wrapped dispatch with the child dispatch */ function wrapDispatch(dispatch, childName) { - return function (action) { + return (action) => { if (dispatch) { dispatch(wrapChildAction(childName, action)); } @@ -3655,69 +3560,68 @@ function wrapDispatch(dispatch, childName) { * @remarks * functions can be cached if needed. **/ -var MultiBaseComp = /** @class */ (function (_super) { - __extends(MultiBaseComp, _super); - function MultiBaseComp(params) { - var _this = _super.call(this, params) || this; - _this.IGNORABLE_DEFAULT_VALUE = {}; - _this.children = _this.parseChildrenFromValue(params); - return _this; - } - MultiBaseComp.prototype.reduce = function (action) { - var comp = this.reduceOrUndefined(action); +class MultiBaseComp extends AbstractComp { + children; + constructor(params) { + super(params); + this.children = this.parseChildrenFromValue(params); + } + reduce(action) { + const comp = this.reduceOrUndefined(action); if (!comp) { console.warn("not supported action, should not happen, action:", action, "\ncurrent comp:", this); return this; } return comp; - }; + } // if the base class can't handle this action, just return undefined - MultiBaseComp.prototype.reduceOrUndefined = function (action) { - var _a, _b; - var _c; + reduceOrUndefined(action) { // log.debug("reduceOrUndefined. action: ", action, " this: ", this); // must handle DELETE in the parent level if (action.type === CompActionTypes.DELETE_COMP && action.path.length === 1) { return this.setChildren(_.omit(this.children, action.path[0])); } if (action.type === CompActionTypes.REPLACE_COMP && action.path.length === 1) { - var NextComp = action.compFactory; + const NextComp = action.compFactory; if (!NextComp) { return this; } - var compName = action.path[0]; - var currentComp = this.children[compName]; - var value = currentComp.toJsonValue(); - var nextComp = new NextComp({ - value: value, + const compName = action.path[0]; + const currentComp = this.children[compName]; + const value = currentComp.toJsonValue(); + const nextComp = new NextComp({ + value, dispatch: wrapDispatch(this.dispatch, compName), }); - return this.setChildren(__assign(__assign({}, this.children), (_a = {}, _a[compName] = nextComp, _a))); + return this.setChildren({ + ...this.children, + [compName]: nextComp, + }); } if (isChildAction(action)) { - var _d = unwrapChildAction(action), childName = _d[0], childAction = _d[1]; - var child = this.children[childName]; + const [childName, childAction] = unwrapChildAction(action); + const child = this.children[childName]; if (!child) { log.error("found bad action path ", childName); return this; } - var newChild = child.reduce(childAction); + const newChild = child.reduce(childAction); return this.setChild(childName, newChild); } // key, value switch (action.type) { case CompActionTypes.MULTI_CHANGE: { - var changes_1 = action.changes; + const { changes } = action; // handle DELETE in the parent level - var mcChildren = _.omitBy(this.children, function (comp, childName) { - var innerAction = changes_1[childName]; + let mcChildren = _.omitBy(this.children, (comp, childName) => { + const innerAction = changes[childName]; return (innerAction && innerAction.type === CompActionTypes.DELETE_COMP && innerAction.path.length === 0); }); // CHANGE - mcChildren = _.mapValues(mcChildren, function (comp, childName) { - var innerAction = changes_1[childName]; + mcChildren = _.mapValues(mcChildren, (comp, childName) => { + const innerAction = changes[childName]; if (innerAction) { return comp.reduce(innerAction); } @@ -3726,27 +3630,31 @@ var MultiBaseComp = /** @class */ (function (_super) { return this.setChildren(mcChildren); } case CompActionTypes.UPDATE_NODES_V2: { - var value_1 = action.value; - if (value_1 === undefined) { + const { value } = action; + if (value === undefined) { return this; } - var cacheKey = CACHE_PREFIX + "REDUCE_UPDATE_NODE"; + const cacheKey = CACHE_PREFIX + "REDUCE_UPDATE_NODE"; // if constructed by the value, just return - if (this[cacheKey] === value_1) { + if (this[cacheKey] === value) { // console.info("inside: UPDATE_NODE_V2 cache hit. action: ", action, "\nvalue: ", value, "\nthis: ", this); return this; } - var children = _.mapValues(this.children, function (comp, childName) { - if (value_1.hasOwnProperty(childName)) { - return comp.reduce(updateNodesV2Action(value_1[childName])); + const children = _.mapValues(this.children, (comp, childName) => { + if (value.hasOwnProperty(childName)) { + return comp.reduce(updateNodesV2Action(value[childName])); } return comp; }); - var extraFields = (_c = this.extraNode()) === null || _c === void 0 ? void 0 : _c.updateNodeFields(value_1); + const extraFields = this.extraNode()?.updateNodeFields(value); if (shallowEqual(children, this.children) && containFields(this, extraFields)) { return this; } - return setFieldsNoTypeCheck(this, __assign((_b = { children: children }, _b[cacheKey] = value_1, _b), extraFields), { keepCacheKeys: ["node"] }); + return setFieldsNoTypeCheck(this, { + children: children, + [cacheKey]: value, + ...extraFields, + }, { keepCacheKeys: ["node"] }); } case CompActionTypes.CHANGE_VALUE: { return this.setChildren(this.parseChildrenFromValue({ @@ -3755,7 +3663,7 @@ var MultiBaseComp = /** @class */ (function (_super) { })); } case CompActionTypes.BROADCAST: { - return this.setChildren(_.mapValues(this.children, function (comp) { + return this.setChildren(_.mapValues(this.children, (comp) => { return comp.reduce(action); })); } @@ -3763,88 +3671,96 @@ var MultiBaseComp = /** @class */ (function (_super) { return this; } } - }; - MultiBaseComp.prototype.setChild = function (childName, newChild) { - var _a; + } + setChild(childName, newChild) { if (this.children[childName] === newChild) { return this; } - return this.setChildren(__assign(__assign({}, this.children), (_a = {}, _a[childName] = newChild, _a))); - }; - MultiBaseComp.prototype.setChildren = function (children, params) { + return this.setChildren({ + ...this.children, + [childName]: newChild, + }); + } + setChildren(children, params) { if (shallowEqual(children, this.children)) { return this; } return setFieldsNoTypeCheck(this, { children: children }, params); - }; + } /** * extended interface. * * @return node for additional node, updateNodeFields for handling UPDATE_NODE event * FIXME: make type safe */ - MultiBaseComp.prototype.extraNode = function () { + extraNode() { return undefined; - }; - MultiBaseComp.prototype.childrenNode = function () { - var _this = this; - var result = {}; - Object.keys(this.children).forEach(function (key) { - var node = _this.children[key].node(); + } + childrenNode() { + const result = {}; + Object.keys(this.children).forEach((key) => { + const node = this.children[key].node(); if (node !== undefined) { result[key] = node; } }); return result; - }; - MultiBaseComp.prototype.nodeWithoutCache = function () { - var _a; - return fromRecord(__assign(__assign({}, this.childrenNode()), (_a = this.extraNode()) === null || _a === void 0 ? void 0 : _a.node)); - }; - MultiBaseComp.prototype.changeDispatch = function (dispatch) { - var newChildren = _.mapValues(this.children, function (comp, childName) { + } + nodeWithoutCache() { + return fromRecord({ + ...this.childrenNode(), + ...this.extraNode()?.node, + }); + } + changeDispatch(dispatch) { + const newChildren = _.mapValues(this.children, (comp, childName) => { return comp.changeDispatch(wrapDispatch(dispatch, childName)); }); - return _super.prototype.changeDispatch.call(this, dispatch).setChildren(newChildren, { keepCacheKeys: ["node"] }); - }; - MultiBaseComp.prototype.ignoreChildDefaultValue = function () { + return super.changeDispatch(dispatch).setChildren(newChildren, { keepCacheKeys: ["node"] }); + } + ignoreChildDefaultValue() { return false; - }; - MultiBaseComp.prototype.toJsonValue = function () { - var _this = this; - var result = {}; - var ignore = this.ignoreChildDefaultValue(); - Object.keys(this.children).forEach(function (key) { - var comp = _this.children[key]; + } + IGNORABLE_DEFAULT_VALUE = {}; + toJsonValue() { + const result = {}; + const ignore = this.ignoreChildDefaultValue(); + Object.keys(this.children).forEach((key) => { + const comp = this.children[key]; // FIXME: this implementation is a little tricky, better choose a encapsulated implementation if (comp.hasOwnProperty("NO_PERSISTENCE")) { return; } - var value = comp.toJsonValue(); + const value = comp.toJsonValue(); if (ignore && _.isEqual(value, comp["IGNORABLE_DEFAULT_VALUE"])) { return; } result[key] = value; }); return result; - }; + } // FIXME: autoHeight should be encapsulated in UIComp/UICompBuilder - MultiBaseComp.prototype.autoHeight = function () { + autoHeight() { return true; - }; - MultiBaseComp.prototype.changeChildAction = function (childName, value) { + } + changeChildAction(childName, value) { return wrapChildAction(childName, this.children[childName].changeValueAction(value)); - }; - return MultiBaseComp; -}(AbstractComp)); + } +} function mergeExtra(e1, e2) { if (e1 === undefined) { return e2; } return { - node: __assign(__assign({}, e1.node), e2.node), - updateNodeFields: function (value) { - return __assign(__assign({}, e1.updateNodeFields(value)), e2.updateNodeFields(value)); + node: { + ...e1.node, + ...e2.node, + }, + updateNodeFields: (value) => { + return { + ...e1.updateNodeFields(value), + ...e2.updateNodeFields(value), + }; }, }; } @@ -3852,22 +3768,19 @@ function mergeExtra(e1, e2) { /** * maintainer a JSONValue, nothing else */ -var SimpleAbstractComp = /** @class */ (function (_super) { - __extends(SimpleAbstractComp, _super); - function SimpleAbstractComp(params) { - var _this = this; - var _a; - _this = _super.call(this, params) || this; - _this.value = (_a = _this.oldValueToNew(params.value)) !== null && _a !== void 0 ? _a : _this.getDefaultValue(); - return _this; +class SimpleAbstractComp extends AbstractComp { + value; + constructor(params) { + super(params); + this.value = this.oldValueToNew(params.value) ?? this.getDefaultValue(); } /** * may override this to implement compatibility */ - SimpleAbstractComp.prototype.oldValueToNew = function (value) { + oldValueToNew(value) { return value; - }; - SimpleAbstractComp.prototype.reduce = function (action) { + } + reduce(action) { if (action.type === CompActionTypes.CHANGE_VALUE) { if (this.value === action.value) { return this; @@ -3875,29 +3788,23 @@ var SimpleAbstractComp = /** @class */ (function (_super) { return setFieldsNoTypeCheck(this, { value: action.value }); } return this; - }; - SimpleAbstractComp.prototype.nodeWithoutCache = function () { + } + nodeWithoutCache() { return fromValue(this.value); - }; - SimpleAbstractComp.prototype.exposingNode = function () { + } + exposingNode() { return this.node(); - }; + } // may be used in defaultValue - SimpleAbstractComp.prototype.toJsonValue = function () { + toJsonValue() { return this.value; - }; - return SimpleAbstractComp; -}(AbstractComp)); -var SimpleComp = /** @class */ (function (_super) { - __extends(SimpleComp, _super); - function SimpleComp() { - return _super !== null && _super.apply(this, arguments) || this; - } - SimpleComp.prototype.getView = function () { + } +} +class SimpleComp extends SimpleAbstractComp { + getView() { return this.value; - }; - return SimpleComp; -}(SimpleAbstractComp)); + } +} var jsxRuntimeExports = {}; var jsxRuntime = { @@ -3905,7 +3812,50 @@ var jsxRuntime = { set exports(v){ jsxRuntimeExports = v; }, }; -var reactJsxRuntime_production_min = {}; +var reactJsxRuntime_production = {}; + +/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +var hasRequiredReactJsxRuntime_production; + +function requireReactJsxRuntime_production () { + if (hasRequiredReactJsxRuntime_production) return reactJsxRuntime_production; + hasRequiredReactJsxRuntime_production = 1; + var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), + REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"); + function jsxProd(type, config, maybeKey) { + var key = null; + void 0 !== maybeKey && (key = "" + maybeKey); + void 0 !== config.key && (key = "" + config.key); + if ("key" in config) { + maybeKey = {}; + for (var propName in config) + "key" !== propName && (maybeKey[propName] = config[propName]); + } else maybeKey = config; + config = maybeKey.ref; + return { + $$typeof: REACT_ELEMENT_TYPE, + type: type, + key: key, + ref: void 0 !== config ? config : null, + props: maybeKey + }; + } + reactJsxRuntime_production.Fragment = REACT_FRAGMENT_TYPE; + reactJsxRuntime_production.jsx = jsxProd; + reactJsxRuntime_production.jsxs = jsxProd; + return reactJsxRuntime_production; +} + +var reactJsxRuntime_development = {}; var reactExports = {}; var react = { @@ -3913,41 +3863,559 @@ var react = { set exports(v){ reactExports = v; }, }; -var react_production_min = {}; +var react_production = {}; /** * @license React - * react.production.min.js + * react.production.js * - * Copyright (c) Facebook, Inc. and its affiliates. + * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -var hasRequiredReact_production_min; - -function requireReact_production_min () { - if (hasRequiredReact_production_min) return react_production_min; - hasRequiredReact_production_min = 1; -var l=Symbol.for("react.element"),n=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),q=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),t=Symbol.for("react.provider"),u=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),z=Symbol.iterator;function A(a){if(null===a||"object"!==typeof a)return null;a=z&&a[z]||a["@@iterator"];return "function"===typeof a?a:null} - var B={isMounted:function(){return !1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}E.prototype.isReactComponent={}; - E.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,a,b,"setState");};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate");};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B;}var H=G.prototype=new F; - H.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0}; - function M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=""+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - printWarning('warn', format, args); + "production" !== process.env.NODE_ENV && + (function () { + function defineDeprecationWarning(methodName, info) { + Object.defineProperty(Component.prototype, methodName, { + get: function () { + console.warn( + "%s(...) is deprecated in plain JavaScript React classes. %s", + info[0], + info[1] + ); + } + }); } - } - } - function error(format) { - { - { - for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { - args[_key2 - 1] = arguments[_key2]; - } - - printWarning('error', format, args); + function getIteratorFn(maybeIterable) { + if (null === maybeIterable || "object" !== typeof maybeIterable) + return null; + maybeIterable = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable["@@iterator"]; + return "function" === typeof maybeIterable ? maybeIterable : null; } - } - } - - function printWarning(level, format, args) { - // When changing this logic, you might want to also - // update consoleWithStackDev.www.js as well. - { - var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; - var stack = ReactDebugCurrentFrame.getStackAddendum(); - - if (stack !== '') { - format += '%s'; - args = args.concat([stack]); - } // eslint-disable-next-line react-internal/safe-string-coercion - - - var argsWithFormat = args.map(function (item) { - return String(item); - }); // Careful: RN currently depends on this prefix - - argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it - // breaks IE9: https://github.com/facebook/react/issues/13610 - // eslint-disable-next-line react-internal/no-production-logging - - Function.prototype.apply.call(console[level], console, argsWithFormat); - } - } - - var didWarnStateUpdateForUnmountedComponent = {}; - - function warnNoop(publicInstance, callerName) { - { - var _constructor = publicInstance.constructor; - var componentName = _constructor && (_constructor.displayName || _constructor.name) || 'ReactClass'; - var warningKey = componentName + "." + callerName; - - if (didWarnStateUpdateForUnmountedComponent[warningKey]) { - return; + function warnNoop(publicInstance, callerName) { + publicInstance = + ((publicInstance = publicInstance.constructor) && + (publicInstance.displayName || publicInstance.name)) || + "ReactClass"; + var warningKey = publicInstance + "." + callerName; + didWarnStateUpdateForUnmountedComponent[warningKey] || + (console.error( + "Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.", + callerName, + publicInstance + ), + (didWarnStateUpdateForUnmountedComponent[warningKey] = !0)); } - - error("Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName); - - didWarnStateUpdateForUnmountedComponent[warningKey] = true; - } - } - /** - * This is the abstract API for an update queue. - */ - - - var ReactNoopUpdateQueue = { - /** - * Checks whether or not this composite component is mounted. - * @param {ReactClass} publicInstance The instance we want to test. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function (publicInstance) { - return false; - }, - - /** - * Forces an update. This should only be invoked when it is known with - * certainty that we are **not** in a DOM transaction. - * - * You may want to call this when you know that some deeper aspect of the - * component's state has changed but `setState` was not called. - * - * This will not invoke `shouldComponentUpdate`, but it will invoke - * `componentWillUpdate` and `componentDidUpdate`. - * - * @param {ReactClass} publicInstance The instance that should rerender. - * @param {?function} callback Called after component is updated. - * @param {?string} callerName name of the calling function in the public API. - * @internal - */ - enqueueForceUpdate: function (publicInstance, callback, callerName) { - warnNoop(publicInstance, 'forceUpdate'); - }, - - /** - * Replaces all of the state. Always use this or `setState` to mutate state. - * You should treat `this.state` as immutable. - * - * There is no guarantee that `this.state` will be immediately updated, so - * accessing `this.state` after calling this method may return the old value. - * - * @param {ReactClass} publicInstance The instance that should rerender. - * @param {object} completeState Next state. - * @param {?function} callback Called after component is updated. - * @param {?string} callerName name of the calling function in the public API. - * @internal - */ - enqueueReplaceState: function (publicInstance, completeState, callback, callerName) { - warnNoop(publicInstance, 'replaceState'); - }, - - /** - * Sets a subset of the state. This only exists because _pendingState is - * internal. This provides a merging strategy that is not available to deep - * properties which is confusing. TODO: Expose pendingState or don't use it - * during the merge. - * - * @param {ReactClass} publicInstance The instance that should rerender. - * @param {object} partialState Next partial state to be merged with state. - * @param {?function} callback Called after component is updated. - * @param {?string} Name of the calling function in the public API. - * @internal - */ - enqueueSetState: function (publicInstance, partialState, callback, callerName) { - warnNoop(publicInstance, 'setState'); - } - }; - - var assign = Object.assign; - - var emptyObject = {}; - - { - Object.freeze(emptyObject); - } - /** - * Base class helpers for the updating state of a component. - */ - - - function Component(props, context, updater) { - this.props = props; - this.context = context; // If a component has string refs, we will assign a different object later. - - this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the - // renderer. - - this.updater = updater || ReactNoopUpdateQueue; - } - - Component.prototype.isReactComponent = {}; - /** - * Sets a subset of the state. Always use this to mutate - * state. You should treat `this.state` as immutable. - * - * There is no guarantee that `this.state` will be immediately updated, so - * accessing `this.state` after calling this method may return the old value. - * - * There is no guarantee that calls to `setState` will run synchronously, - * as they may eventually be batched together. You can provide an optional - * callback that will be executed when the call to setState is actually - * completed. - * - * When a function is provided to setState, it will be called at some point in - * the future (not synchronously). It will be called with the up to date - * component arguments (state, props, context). These values can be different - * from this.* because your function may be called after receiveProps but before - * shouldComponentUpdate, and this new state, props, and context will not yet be - * assigned to this. - * - * @param {object|function} partialState Next partial state or function to - * produce next partial state to be merged with current state. - * @param {?function} callback Called after state is updated. - * @final - * @protected - */ - - Component.prototype.setState = function (partialState, callback) { - if (typeof partialState !== 'object' && typeof partialState !== 'function' && partialState != null) { - throw new Error('setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.'); - } - - this.updater.enqueueSetState(this, partialState, callback, 'setState'); - }; - /** - * Forces an update. This should only be invoked when it is known with - * certainty that we are **not** in a DOM transaction. - * - * You may want to call this when you know that some deeper aspect of the - * component's state has changed but `setState` was not called. - * - * This will not invoke `shouldComponentUpdate`, but it will invoke - * `componentWillUpdate` and `componentDidUpdate`. - * - * @param {?function} callback Called after update is complete. - * @final - * @protected - */ - - - Component.prototype.forceUpdate = function (callback) { - this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); - }; - /** - * Deprecated APIs. These APIs used to exist on classic React classes but since - * we would like to deprecate them, we're not going to move them over to this - * modern base class. Instead, we define a getter that warns if it's accessed. - */ - - - { - var deprecatedAPIs = { - isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'], - replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).'] - }; - - var defineDeprecationWarning = function (methodName, info) { - Object.defineProperty(Component.prototype, methodName, { - get: function () { - warn('%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]); - - return undefined; - } - }); - }; - - for (var fnName in deprecatedAPIs) { - if (deprecatedAPIs.hasOwnProperty(fnName)) { - defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); + function Component(props, context, updater) { + this.props = props; + this.context = context; + this.refs = emptyObject; + this.updater = updater || ReactNoopUpdateQueue; } - } - } - - function ComponentDummy() {} - - ComponentDummy.prototype = Component.prototype; - /** - * Convenience component with default shallow equality check for sCU. - */ - - function PureComponent(props, context, updater) { - this.props = props; - this.context = context; // If a component has string refs, we will assign a different object later. - - this.refs = emptyObject; - this.updater = updater || ReactNoopUpdateQueue; - } - - var pureComponentPrototype = PureComponent.prototype = new ComponentDummy(); - pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. - - assign(pureComponentPrototype, Component.prototype); - pureComponentPrototype.isPureReactComponent = true; - - // an immutable object with a single mutable value - function createRef() { - var refObject = { - current: null - }; - - { - Object.seal(refObject); - } - - return refObject; - } - - var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare - - function isArray(a) { - return isArrayImpl(a); - } - - /* - * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol - * and Temporal.* types. See https://github.com/facebook/react/pull/22064. - * - * The functions in this module will throw an easier-to-understand, - * easier-to-debug exception with a clear errors message message explaining the - * problem. (Instead of a confusing exception thrown inside the implementation - * of the `value` object). - */ - // $FlowFixMe only called in DEV, so void return is not possible. - function typeName(value) { - { - // toStringTag is needed for namespaced types like Temporal.Instant - var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; - var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; - return type; - } - } // $FlowFixMe only called in DEV, so void return is not possible. - - - function willCoercionThrow(value) { - { - try { - testStringCoercion(value); - return false; - } catch (e) { - return true; + function ComponentDummy() {} + function PureComponent(props, context, updater) { + this.props = props; + this.context = context; + this.refs = emptyObject; + this.updater = updater || ReactNoopUpdateQueue; } - } - } - - function testStringCoercion(value) { - // If you ended up here by following an exception call stack, here's what's - // happened: you supplied an object or symbol value to React (as a prop, key, - // DOM attribute, CSS property, string ref, etc.) and when React tried to - // coerce it to a string using `'' + value`, an exception was thrown. - // - // The most common types that will cause this exception are `Symbol` instances - // and Temporal objects like `Temporal.Instant`. But any object that has a - // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this - // exception. (Library authors do this to prevent users from using built-in - // numeric operators like `+` or comparison operators like `>=` because custom - // methods are needed to perform accurate arithmetic or comparison.) - // - // To fix the problem, coerce this object or symbol value to a string before - // passing it to React. The most reliable way is usually `String(value)`. - // - // To find which value is throwing, check the browser or debugger console. - // Before this exception was thrown, there should be `console.error` output - // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the - // problem and how that type was used: key, atrribute, input value prop, etc. - // In most cases, this console output also shows the component and its - // ancestor components where the exception happened. - // - // eslint-disable-next-line react-internal/safe-string-coercion - return '' + value; - } - function checkKeyStringCoercion(value) { - { - if (willCoercionThrow(value)) { - error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); - - return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + function testStringCoercion(value) { + return "" + value; } - } - } - - function getWrappedName(outerType, innerType, wrapperName) { - var displayName = outerType.displayName; - - if (displayName) { - return displayName; - } - - var functionName = innerType.displayName || innerType.name || ''; - return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName; - } // Keep in sync with react-reconciler/getComponentNameFromFiber - - - function getContextName(type) { - return type.displayName || 'Context'; - } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead. - - - function getComponentNameFromType(type) { - if (type == null) { - // Host root, text node or just invalid type. - return null; - } - - { - if (typeof type.tag === 'number') { - error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.'); + function checkKeyStringCoercion(value) { + try { + testStringCoercion(value); + var JSCompiler_inline_result = !1; + } catch (e) { + JSCompiler_inline_result = !0; + } + if (JSCompiler_inline_result) { + JSCompiler_inline_result = console; + var JSCompiler_temp_const = JSCompiler_inline_result.error; + var JSCompiler_inline_result$jscomp$0 = + ("function" === typeof Symbol && + Symbol.toStringTag && + value[Symbol.toStringTag]) || + value.constructor.name || + "Object"; + JSCompiler_temp_const.call( + JSCompiler_inline_result, + "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", + JSCompiler_inline_result$jscomp$0 + ); + return testStringCoercion(value); + } } - } - - if (typeof type === 'function') { - return type.displayName || type.name || null; - } - - if (typeof type === 'string') { - return type; - } - - switch (type) { - case REACT_FRAGMENT_TYPE: - return 'Fragment'; - - case REACT_PORTAL_TYPE: - return 'Portal'; - - case REACT_PROFILER_TYPE: - return 'Profiler'; - - case REACT_STRICT_MODE_TYPE: - return 'StrictMode'; - - case REACT_SUSPENSE_TYPE: - return 'Suspense'; - - case REACT_SUSPENSE_LIST_TYPE: - return 'SuspenseList'; - - } - - if (typeof type === 'object') { - switch (type.$$typeof) { - case REACT_CONTEXT_TYPE: - var context = type; - return getContextName(context) + '.Consumer'; - - case REACT_PROVIDER_TYPE: - var provider = type; - return getContextName(provider._context) + '.Provider'; - - case REACT_FORWARD_REF_TYPE: - return getWrappedName(type, type.render, 'ForwardRef'); - - case REACT_MEMO_TYPE: - var outerName = type.displayName || null; - - if (outerName !== null) { - return outerName; - } - - return getComponentNameFromType(type.type) || 'Memo'; - - case REACT_LAZY_TYPE: - { - var lazyComponent = type; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - return getComponentNameFromType(init(payload)); - } catch (x) { - return null; - } + function getComponentNameFromType(type) { + if (null == type) return null; + if ("function" === typeof type) + return type.$$typeof === REACT_CLIENT_REFERENCE + ? null + : type.displayName || type.name || null; + if ("string" === typeof type) return type; + switch (type) { + case REACT_FRAGMENT_TYPE: + return "Fragment"; + case REACT_PROFILER_TYPE: + return "Profiler"; + case REACT_STRICT_MODE_TYPE: + return "StrictMode"; + case REACT_SUSPENSE_TYPE: + return "Suspense"; + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + case REACT_ACTIVITY_TYPE: + return "Activity"; + } + if ("object" === typeof type) + switch ( + ("number" === typeof type.tag && + console.error( + "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue." + ), + type.$$typeof) + ) { + case REACT_PORTAL_TYPE: + return "Portal"; + case REACT_CONTEXT_TYPE: + return (type.displayName || "Context") + ".Provider"; + case REACT_CONSUMER_TYPE: + return (type._context.displayName || "Context") + ".Consumer"; + case REACT_FORWARD_REF_TYPE: + var innerType = type.render; + type = type.displayName; + type || + ((type = innerType.displayName || innerType.name || ""), + (type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef")); + return type; + case REACT_MEMO_TYPE: + return ( + (innerType = type.displayName || null), + null !== innerType + ? innerType + : getComponentNameFromType(type.type) || "Memo" + ); + case REACT_LAZY_TYPE: + innerType = type._payload; + type = type._init; + try { + return getComponentNameFromType(type(innerType)); + } catch (x) {} } - - // eslint-disable-next-line no-fallthrough + return null; } - } - - return null; - } - - var hasOwnProperty = Object.prototype.hasOwnProperty; - - var RESERVED_PROPS = { - key: true, - ref: true, - __self: true, - __source: true - }; - var specialPropKeyWarningShown, specialPropRefWarningShown, didWarnAboutStringRefs; - - { - didWarnAboutStringRefs = {}; - } - - function hasValidRef(config) { - { - if (hasOwnProperty.call(config, 'ref')) { - var getter = Object.getOwnPropertyDescriptor(config, 'ref').get; - - if (getter && getter.isReactWarning) { - return false; + function getTaskName(type) { + if (type === REACT_FRAGMENT_TYPE) return "<>"; + if ( + "object" === typeof type && + null !== type && + type.$$typeof === REACT_LAZY_TYPE + ) + return "<...>"; + try { + var name = getComponentNameFromType(type); + return name ? "<" + name + ">" : "<...>"; + } catch (x) { + return "<...>"; } } - } - - return config.ref !== undefined; - } - - function hasValidKey(config) { - { - if (hasOwnProperty.call(config, 'key')) { - var getter = Object.getOwnPropertyDescriptor(config, 'key').get; - - if (getter && getter.isReactWarning) { - return false; - } + function getOwner() { + var dispatcher = ReactSharedInternals.A; + return null === dispatcher ? null : dispatcher.getOwner(); } - } - - return config.key !== undefined; - } - - function defineKeyPropWarningGetter(props, displayName) { - var warnAboutAccessingKey = function () { - { - if (!specialPropKeyWarningShown) { - specialPropKeyWarningShown = true; - - error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); + function UnknownOwner() { + return Error("react-stack-top-frame"); + } + function hasValidKey(config) { + if (hasOwnProperty.call(config, "key")) { + var getter = Object.getOwnPropertyDescriptor(config, "key").get; + if (getter && getter.isReactWarning) return !1; } + return void 0 !== config.key; } - }; - - warnAboutAccessingKey.isReactWarning = true; - Object.defineProperty(props, 'key', { - get: warnAboutAccessingKey, - configurable: true - }); - } - - function defineRefPropWarningGetter(props, displayName) { - var warnAboutAccessingRef = function () { - { - if (!specialPropRefWarningShown) { - specialPropRefWarningShown = true; - - error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); + function defineKeyPropWarningGetter(props, displayName) { + function warnAboutAccessingKey() { + specialPropKeyWarningShown || + ((specialPropKeyWarningShown = !0), + console.error( + "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", + displayName + )); } + warnAboutAccessingKey.isReactWarning = !0; + Object.defineProperty(props, "key", { + get: warnAboutAccessingKey, + configurable: !0 + }); } - }; - - warnAboutAccessingRef.isReactWarning = true; - Object.defineProperty(props, 'ref', { - get: warnAboutAccessingRef, - configurable: true - }); - } - - function warnIfStringRefCannotBeAutoConverted(config) { - { - if (typeof config.ref === 'string' && ReactCurrentOwner.current && config.__self && ReactCurrentOwner.current.stateNode !== config.__self) { - var componentName = getComponentNameFromType(ReactCurrentOwner.current.type); - - if (!didWarnAboutStringRefs[componentName]) { - error('Component "%s" contains the string ref "%s". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref', componentName, config.ref); - - didWarnAboutStringRefs[componentName] = true; - } + function elementRefGetterWithDeprecationWarning() { + var componentName = getComponentNameFromType(this.type); + didWarnAboutElementRef[componentName] || + ((didWarnAboutElementRef[componentName] = !0), + console.error( + "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release." + )); + componentName = this.props.ref; + return void 0 !== componentName ? componentName : null; } - } - } - /** - * Factory method to create a new React element. This no longer adheres to - * the class pattern, so do not use new to call it. Also, instanceof check - * will not work. Instead test $$typeof field against Symbol.for('react.element') to check - * if something is a React Element. - * - * @param {*} type - * @param {*} props - * @param {*} key - * @param {string|object} ref - * @param {*} owner - * @param {*} self A *temporary* helper to detect places where `this` is - * different from the `owner` when React.createElement is called, so that we - * can warn. We want to get rid of owner and replace string `ref`s with arrow - * functions, and as long as `this` and owner are the same, there will be no - * change in behavior. - * @param {*} source An annotation object (added by a transpiler or otherwise) - * indicating filename, line number, and/or other information. - * @internal - */ - - - var ReactElement = function (type, key, ref, self, source, owner, props) { - var element = { - // This tag allows us to uniquely identify this as a React Element - $$typeof: REACT_ELEMENT_TYPE, - // Built-in properties that belong on the element - type: type, - key: key, - ref: ref, - props: props, - // Record the component responsible for creating this element. - _owner: owner - }; - - { - // The validation flag is currently mutative. We put it on - // an external backing store so that we can freeze the whole object. - // This can be replaced with a WeakMap once they are implemented in - // commonly used development environments. - element._store = {}; // To make comparing ReactElements easier for testing purposes, we make - // the validation flag non-enumerable (where possible, which should - // include every environment we run tests in), so the test framework - // ignores it. - - Object.defineProperty(element._store, 'validated', { - configurable: false, - enumerable: false, - writable: true, - value: false - }); // self and source are DEV only properties. - - Object.defineProperty(element, '_self', { - configurable: false, - enumerable: false, - writable: false, - value: self - }); // Two elements created in two different places should be considered - // equal for testing purposes and therefore we hide it from enumeration. - - Object.defineProperty(element, '_source', { - configurable: false, - enumerable: false, - writable: false, - value: source - }); - - if (Object.freeze) { - Object.freeze(element.props); - Object.freeze(element); - } - } - - return element; - }; - /** - * Create and return a new ReactElement of the given type. - * See https://reactjs.org/docs/react-api.html#createelement - */ - - function createElement(type, config, children) { - var propName; // Reserved names are extracted - - var props = {}; - var key = null; - var ref = null; - var self = null; - var source = null; - - if (config != null) { - if (hasValidRef(config)) { - ref = config.ref; - - { - warnIfStringRefCannotBeAutoConverted(config); - } + function ReactElement( + type, + key, + self, + source, + owner, + props, + debugStack, + debugTask + ) { + self = props.ref; + type = { + $$typeof: REACT_ELEMENT_TYPE, + type: type, + key: key, + props: props, + _owner: owner + }; + null !== (void 0 !== self ? self : null) + ? Object.defineProperty(type, "ref", { + enumerable: !1, + get: elementRefGetterWithDeprecationWarning + }) + : Object.defineProperty(type, "ref", { enumerable: !1, value: null }); + type._store = {}; + Object.defineProperty(type._store, "validated", { + configurable: !1, + enumerable: !1, + writable: !0, + value: 0 + }); + Object.defineProperty(type, "_debugInfo", { + configurable: !1, + enumerable: !1, + writable: !0, + value: null + }); + Object.defineProperty(type, "_debugStack", { + configurable: !1, + enumerable: !1, + writable: !0, + value: debugStack + }); + Object.defineProperty(type, "_debugTask", { + configurable: !1, + enumerable: !1, + writable: !0, + value: debugTask + }); + Object.freeze && (Object.freeze(type.props), Object.freeze(type)); + return type; } - - if (hasValidKey(config)) { - { - checkKeyStringCoercion(config.key); - } - - key = '' + config.key; + function cloneAndReplaceKey(oldElement, newKey) { + newKey = ReactElement( + oldElement.type, + newKey, + void 0, + void 0, + oldElement._owner, + oldElement.props, + oldElement._debugStack, + oldElement._debugTask + ); + oldElement._store && + (newKey._store.validated = oldElement._store.validated); + return newKey; } - - self = config.__self === undefined ? null : config.__self; - source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object - - for (propName in config) { - if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { - props[propName] = config[propName]; - } + function isValidElement(object) { + return ( + "object" === typeof object && + null !== object && + object.$$typeof === REACT_ELEMENT_TYPE + ); } - } // Children can be more than one argument, and those are transferred onto - // the newly allocated props object. - - - var childrenLength = arguments.length - 2; - - if (childrenLength === 1) { - props.children = children; - } else if (childrenLength > 1) { - var childArray = Array(childrenLength); - - for (var i = 0; i < childrenLength; i++) { - childArray[i] = arguments[i + 2]; + function escape(key) { + var escaperLookup = { "=": "=0", ":": "=2" }; + return ( + "$" + + key.replace(/[=:]/g, function (match) { + return escaperLookup[match]; + }) + ); } - - { - if (Object.freeze) { - Object.freeze(childArray); - } + function getElementKey(element, index) { + return "object" === typeof element && + null !== element && + null != element.key + ? (checkKeyStringCoercion(element.key), escape("" + element.key)) + : index.toString(36); } - - props.children = childArray; - } // Resolve default props - - - if (type && type.defaultProps) { - var defaultProps = type.defaultProps; - - for (propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; + function noop$1() {} + function resolveThenable(thenable) { + switch (thenable.status) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + default: + switch ( + ("string" === typeof thenable.status + ? thenable.then(noop$1, noop$1) + : ((thenable.status = "pending"), + thenable.then( + function (fulfilledValue) { + "pending" === thenable.status && + ((thenable.status = "fulfilled"), + (thenable.value = fulfilledValue)); + }, + function (error) { + "pending" === thenable.status && + ((thenable.status = "rejected"), + (thenable.reason = error)); + } + )), + thenable.status) + ) { + case "fulfilled": + return thenable.value; + case "rejected": + throw thenable.reason; + } } + throw thenable; } - } - - { - if (key || ref) { - var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; - - if (key) { - defineKeyPropWarningGetter(props, displayName); + function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) { + var type = typeof children; + if ("undefined" === type || "boolean" === type) children = null; + var invokeCallback = !1; + if (null === children) invokeCallback = !0; + else + switch (type) { + case "bigint": + case "string": + case "number": + invokeCallback = !0; + break; + case "object": + switch (children.$$typeof) { + case REACT_ELEMENT_TYPE: + case REACT_PORTAL_TYPE: + invokeCallback = !0; + break; + case REACT_LAZY_TYPE: + return ( + (invokeCallback = children._init), + mapIntoArray( + invokeCallback(children._payload), + array, + escapedPrefix, + nameSoFar, + callback + ) + ); + } + } + if (invokeCallback) { + invokeCallback = children; + callback = callback(invokeCallback); + var childKey = + "" === nameSoFar ? "." + getElementKey(invokeCallback, 0) : nameSoFar; + isArrayImpl(callback) + ? ((escapedPrefix = ""), + null != childKey && + (escapedPrefix = + childKey.replace(userProvidedKeyEscapeRegex, "$&/") + "/"), + mapIntoArray(callback, array, escapedPrefix, "", function (c) { + return c; + })) + : null != callback && + (isValidElement(callback) && + (null != callback.key && + ((invokeCallback && invokeCallback.key === callback.key) || + checkKeyStringCoercion(callback.key)), + (escapedPrefix = cloneAndReplaceKey( + callback, + escapedPrefix + + (null == callback.key || + (invokeCallback && invokeCallback.key === callback.key) + ? "" + : ("" + callback.key).replace( + userProvidedKeyEscapeRegex, + "$&/" + ) + "/") + + childKey + )), + "" !== nameSoFar && + null != invokeCallback && + isValidElement(invokeCallback) && + null == invokeCallback.key && + invokeCallback._store && + !invokeCallback._store.validated && + (escapedPrefix._store.validated = 2), + (callback = escapedPrefix)), + array.push(callback)); + return 1; } - - if (ref) { - defineRefPropWarningGetter(props, displayName); + invokeCallback = 0; + childKey = "" === nameSoFar ? "." : nameSoFar + ":"; + if (isArrayImpl(children)) + for (var i = 0; i < children.length; i++) + (nameSoFar = children[i]), + (type = childKey + getElementKey(nameSoFar, i)), + (invokeCallback += mapIntoArray( + nameSoFar, + array, + escapedPrefix, + type, + callback + )); + else if (((i = getIteratorFn(children)), "function" === typeof i)) + for ( + i === children.entries && + (didWarnAboutMaps || + console.warn( + "Using Maps as children is not supported. Use an array of keyed ReactElements instead." + ), + (didWarnAboutMaps = !0)), + children = i.call(children), + i = 0; + !(nameSoFar = children.next()).done; + + ) + (nameSoFar = nameSoFar.value), + (type = childKey + getElementKey(nameSoFar, i++)), + (invokeCallback += mapIntoArray( + nameSoFar, + array, + escapedPrefix, + type, + callback + )); + else if ("object" === type) { + if ("function" === typeof children.then) + return mapIntoArray( + resolveThenable(children), + array, + escapedPrefix, + nameSoFar, + callback + ); + array = String(children); + throw Error( + "Objects are not valid as a React child (found: " + + ("[object Object]" === array + ? "object with keys {" + Object.keys(children).join(", ") + "}" + : array) + + "). If you meant to render a collection of children, use an array instead." + ); } + return invokeCallback; } - } - - return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); - } - function cloneAndReplaceKey(oldElement, newKey) { - var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props); - return newElement; - } - /** - * Clone and return a new ReactElement using element as the starting point. - * See https://reactjs.org/docs/react-api.html#cloneelement - */ - - function cloneElement(element, config, children) { - if (element === null || element === undefined) { - throw new Error("React.cloneElement(...): The argument must be a React element, but you passed " + element + "."); - } - - var propName; // Original props are copied - - var props = assign({}, element.props); // Reserved names are extracted - - var key = element.key; - var ref = element.ref; // Self is preserved since the owner is preserved. - - var self = element._self; // Source is preserved since cloneElement is unlikely to be targeted by a - // transpiler, and the original source is probably a better indicator of the - // true owner. - - var source = element._source; // Owner will be preserved, unless ref is overridden - - var owner = element._owner; - - if (config != null) { - if (hasValidRef(config)) { - // Silently steal the ref from the parent. - ref = config.ref; - owner = ReactCurrentOwner.current; + function mapChildren(children, func, context) { + if (null == children) return children; + var result = [], + count = 0; + mapIntoArray(children, result, "", "", function (child) { + return func.call(context, child, count++); + }); + return result; } - - if (hasValidKey(config)) { - { - checkKeyStringCoercion(config.key); + function lazyInitializer(payload) { + if (-1 === payload._status) { + var ctor = payload._result; + ctor = ctor(); + ctor.then( + function (moduleObject) { + if (0 === payload._status || -1 === payload._status) + (payload._status = 1), (payload._result = moduleObject); + }, + function (error) { + if (0 === payload._status || -1 === payload._status) + (payload._status = 2), (payload._result = error); + } + ); + -1 === payload._status && + ((payload._status = 0), (payload._result = ctor)); } - - key = '' + config.key; - } // Remaining properties override existing props - - - var defaultProps; - - if (element.type && element.type.defaultProps) { - defaultProps = element.type.defaultProps; + if (1 === payload._status) + return ( + (ctor = payload._result), + void 0 === ctor && + console.error( + "lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))\n\nDid you accidentally put curly braces around the import?", + ctor + ), + "default" in ctor || + console.error( + "lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))", + ctor + ), + ctor.default + ); + throw payload._result; } - - for (propName in config) { - if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { - if (config[propName] === undefined && defaultProps !== undefined) { - // Resolve default props - props[propName] = defaultProps[propName]; - } else { - props[propName] = config[propName]; - } - } + function resolveDispatcher() { + var dispatcher = ReactSharedInternals.H; + null === dispatcher && + console.error( + "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem." + ); + return dispatcher; } - } // Children can be more than one argument, and those are transferred onto - // the newly allocated props object. - - - var childrenLength = arguments.length - 2; - - if (childrenLength === 1) { - props.children = children; - } else if (childrenLength > 1) { - var childArray = Array(childrenLength); - - for (var i = 0; i < childrenLength; i++) { - childArray[i] = arguments[i + 2]; + function noop() {} + function enqueueTask(task) { + if (null === enqueueTaskImpl) + try { + var requireString = ("require" + Math.random()).slice(0, 7); + enqueueTaskImpl = (module && module[requireString]).call( + module, + "timers" + ).setImmediate; + } catch (_err) { + enqueueTaskImpl = function (callback) { + !1 === didWarnAboutMessageChannel && + ((didWarnAboutMessageChannel = !0), + "undefined" === typeof MessageChannel && + console.error( + "This browser does not have a MessageChannel implementation, so enqueuing tasks via await act(async () => ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning." + )); + var channel = new MessageChannel(); + channel.port1.onmessage = callback; + channel.port2.postMessage(void 0); + }; + } + return enqueueTaskImpl(task); } - - props.children = childArray; - } - - return ReactElement(element.type, key, ref, self, source, owner, props); - } - /** - * Verifies the object is a ReactElement. - * See https://reactjs.org/docs/react-api.html#isvalidelement - * @param {?object} object - * @return {boolean} True if `object` is a ReactElement. - * @final - */ - - function isValidElement(object) { - return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; - } - - var SEPARATOR = '.'; - var SUBSEPARATOR = ':'; - /** - * Escape and wrap key so it is safe to use as a reactid - * - * @param {string} key to be escaped. - * @return {string} the escaped key. - */ - - function escape(key) { - var escapeRegex = /[=:]/g; - var escaperLookup = { - '=': '=0', - ':': '=2' - }; - var escapedString = key.replace(escapeRegex, function (match) { - return escaperLookup[match]; - }); - return '$' + escapedString; - } - /** - * TODO: Test that a single child and an array with one item have the same key - * pattern. - */ - - - var didWarnAboutMaps = false; - var userProvidedKeyEscapeRegex = /\/+/g; - - function escapeUserProvidedKey(text) { - return text.replace(userProvidedKeyEscapeRegex, '$&/'); - } - /** - * Generate a key string that identifies a element within a set. - * - * @param {*} element A element that could contain a manual key. - * @param {number} index Index that is used if a manual key is not provided. - * @return {string} - */ - - - function getElementKey(element, index) { - // Do some typechecking here since we call this blindly. We want to ensure - // that we don't block potential future ES APIs. - if (typeof element === 'object' && element !== null && element.key != null) { - // Explicit key - { - checkKeyStringCoercion(element.key); + function aggregateErrors(errors) { + return 1 < errors.length && "function" === typeof AggregateError + ? new AggregateError(errors) + : errors[0]; } - - return escape('' + element.key); - } // Implicit key determined by the index in the set - - - return index.toString(36); - } - - function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) { - var type = typeof children; - - if (type === 'undefined' || type === 'boolean') { - // All of the above are perceived as null. - children = null; - } - - var invokeCallback = false; - - if (children === null) { - invokeCallback = true; - } else { - switch (type) { - case 'string': - case 'number': - invokeCallback = true; - break; - - case 'object': - switch (children.$$typeof) { - case REACT_ELEMENT_TYPE: - case REACT_PORTAL_TYPE: - invokeCallback = true; - } - + function popActScope(prevActQueue, prevActScopeDepth) { + prevActScopeDepth !== actScopeDepth - 1 && + console.error( + "You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. " + ); + actScopeDepth = prevActScopeDepth; } - } - - if (invokeCallback) { - var _child = children; - var mappedChild = callback(_child); // If it's the only child, treat the name as if it was wrapped in an array - // so that it's consistent if the number of children grows: - - var childKey = nameSoFar === '' ? SEPARATOR + getElementKey(_child, 0) : nameSoFar; - - if (isArray(mappedChild)) { - var escapedChildKey = ''; - - if (childKey != null) { - escapedChildKey = escapeUserProvidedKey(childKey) + '/'; - } - - mapIntoArray(mappedChild, array, escapedChildKey, '', function (c) { - return c; - }); - } else if (mappedChild != null) { - if (isValidElement(mappedChild)) { - { - // The `if` statement here prevents auto-disabling of the safe - // coercion ESLint rule, so we must manually disable it below. - // $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key - if (mappedChild.key && (!_child || _child.key !== mappedChild.key)) { - checkKeyStringCoercion(mappedChild.key); + function recursivelyFlushAsyncActWork(returnValue, resolve, reject) { + var queue = ReactSharedInternals.actQueue; + if (null !== queue) + if (0 !== queue.length) + try { + flushActQueue(queue); + enqueueTask(function () { + return recursivelyFlushAsyncActWork(returnValue, resolve, reject); + }); + return; + } catch (error) { + ReactSharedInternals.thrownErrors.push(error); } - } - - mappedChild = cloneAndReplaceKey(mappedChild, // Keep both the (mapped) and old keys if they differ, just as - // traverseAllChildren used to do for objects as children - escapedPrefix + ( // $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key - mappedChild.key && (!_child || _child.key !== mappedChild.key) ? // $FlowFixMe Flow incorrectly thinks existing element's key can be a number - // eslint-disable-next-line react-internal/safe-string-coercion - escapeUserProvidedKey('' + mappedChild.key) + '/' : '') + childKey); - } - - array.push(mappedChild); - } - - return 1; - } - - var child; - var nextName; - var subtreeCount = 0; // Count of children found in the current subtree. - - var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; - - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - child = children[i]; - nextName = nextNamePrefix + getElementKey(child, i); - subtreeCount += mapIntoArray(child, array, escapedPrefix, nextName, callback); + else ReactSharedInternals.actQueue = null; + 0 < ReactSharedInternals.thrownErrors.length + ? ((queue = aggregateErrors(ReactSharedInternals.thrownErrors)), + (ReactSharedInternals.thrownErrors.length = 0), + reject(queue)) + : resolve(returnValue); } - } else { - var iteratorFn = getIteratorFn(children); - - if (typeof iteratorFn === 'function') { - var iterableChildren = children; - - { - // Warn about using Maps as children - if (iteratorFn === iterableChildren.entries) { - if (!didWarnAboutMaps) { - warn('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.'); + function flushActQueue(queue) { + if (!isFlushing) { + isFlushing = !0; + var i = 0; + try { + for (; i < queue.length; i++) { + var callback = queue[i]; + do { + ReactSharedInternals.didUsePromise = !1; + var continuation = callback(!1); + if (null !== continuation) { + if (ReactSharedInternals.didUsePromise) { + queue[i] = callback; + queue.splice(0, i); + return; + } + callback = continuation; + } else break; + } while (1); } - - didWarnAboutMaps = true; + queue.length = 0; + } catch (error) { + queue.splice(0, i + 1), ReactSharedInternals.thrownErrors.push(error); + } finally { + isFlushing = !1; } } - - var iterator = iteratorFn.call(iterableChildren); - var step; - var ii = 0; - - while (!(step = iterator.next()).done) { - child = step.value; - nextName = nextNamePrefix + getElementKey(child, ii++); - subtreeCount += mapIntoArray(child, array, escapedPrefix, nextName, callback); - } - } else if (type === 'object') { - // eslint-disable-next-line react-internal/safe-string-coercion - var childrenString = String(children); - throw new Error("Objects are not valid as a React child (found: " + (childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString) + "). " + 'If you meant to render a collection of children, use an array ' + 'instead.'); } - } - - return subtreeCount; - } - - /** - * Maps children that are typically specified as `props.children`. - * - * See https://reactjs.org/docs/react-api.html#reactchildrenmap - * - * The provided mapFunction(child, index) will be called for each - * leaf child. - * - * @param {?*} children Children tree container. - * @param {function(*, int)} func The map function. - * @param {*} context Context for mapFunction. - * @return {object} Object containing the ordered map of results. - */ - function mapChildren(children, func, context) { - if (children == null) { - return children; - } - - var result = []; - var count = 0; - mapIntoArray(children, result, '', '', function (child) { - return func.call(context, child, count++); - }); - return result; - } - /** - * Count the number of children that are typically specified as - * `props.children`. - * - * See https://reactjs.org/docs/react-api.html#reactchildrencount - * - * @param {?*} children Children tree container. - * @return {number} The number of children. - */ - - - function countChildren(children) { - var n = 0; - mapChildren(children, function () { - n++; // Don't return anything - }); - return n; - } - - /** - * Iterates through children that are typically specified as `props.children`. - * - * See https://reactjs.org/docs/react-api.html#reactchildrenforeach - * - * The provided forEachFunc(child, index) will be called for each - * leaf child. - * - * @param {?*} children Children tree container. - * @param {function(*, int)} forEachFunc - * @param {*} forEachContext Context for forEachContext. - */ - function forEachChildren(children, forEachFunc, forEachContext) { - mapChildren(children, function () { - forEachFunc.apply(this, arguments); // Don't return anything. - }, forEachContext); - } - /** - * Flatten a children object (typically specified as `props.children`) and - * return an array with appropriately re-keyed children. - * - * See https://reactjs.org/docs/react-api.html#reactchildrentoarray - */ - - - function toArray(children) { - return mapChildren(children, function (child) { - return child; - }) || []; - } - /** - * Returns the first child in a collection of children and verifies that there - * is only one child in the collection. - * - * See https://reactjs.org/docs/react-api.html#reactchildrenonly - * - * The current implementation of this function assumes that a single child gets - * passed without a wrapper, but the purpose of this helper function is to - * abstract away the particular structure of children. - * - * @param {?object} children Child collection structure. - * @return {ReactElement} The first and only `ReactElement` contained in the - * structure. - */ - - - function onlyChild(children) { - if (!isValidElement(children)) { - throw new Error('React.Children.only expected to receive a single React element child.'); - } - - return children; - } - - function createContext(defaultValue) { - // TODO: Second argument used to be an optional `calculateChangedBits` - // function. Warn to reserve for future use? - var context = { - $$typeof: REACT_CONTEXT_TYPE, - // As a workaround to support multiple concurrent renderers, we categorize - // some renderers as primary and others as secondary. We only expect - // there to be two concurrent renderers at most: React Native (primary) and - // Fabric (secondary); React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - _currentValue: defaultValue, - _currentValue2: defaultValue, - // Used to track how many concurrent renderers this context currently - // supports within in a single renderer. Such as parallel server rendering. - _threadCount: 0, - // These are circular - Provider: null, - Consumer: null, - // Add these to use same hidden class in VM as ServerContext - _defaultValue: null, - _globalName: null - }; - context.Provider = { - $$typeof: REACT_PROVIDER_TYPE, - _context: context - }; - var hasWarnedAboutUsingNestedContextConsumers = false; - var hasWarnedAboutUsingConsumerProvider = false; - var hasWarnedAboutDisplayNameOnConsumer = false; - - { - // A separate object, but proxies back to the original context object for - // backwards compatibility. It has a different $$typeof, so we can properly - // warn for the incorrect usage of Context as a Consumer. - var Consumer = { - $$typeof: REACT_CONTEXT_TYPE, - _context: context - }; // $FlowFixMe: Flow complains about not setting a value, which is intentional here - - Object.defineProperties(Consumer, { - Provider: { - get: function () { - if (!hasWarnedAboutUsingConsumerProvider) { - hasWarnedAboutUsingConsumerProvider = true; - - error('Rendering is not supported and will be removed in ' + 'a future major release. Did you mean to render instead?'); - } - - return context.Provider; - }, - set: function (_Provider) { - context.Provider = _Provider; - } - }, - _currentValue: { - get: function () { - return context._currentValue; - }, - set: function (_currentValue) { - context._currentValue = _currentValue; - } - }, - _currentValue2: { - get: function () { - return context._currentValue2; + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && + "function" === + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && + __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error()); + var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), + REACT_PORTAL_TYPE = Symbol.for("react.portal"), + REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), + REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), + REACT_PROFILER_TYPE = Symbol.for("react.profiler"); + var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), + REACT_CONTEXT_TYPE = Symbol.for("react.context"), + REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), + REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), + REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), + REACT_MEMO_TYPE = Symbol.for("react.memo"), + REACT_LAZY_TYPE = Symbol.for("react.lazy"), + REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), + MAYBE_ITERATOR_SYMBOL = Symbol.iterator, + didWarnStateUpdateForUnmountedComponent = {}, + ReactNoopUpdateQueue = { + isMounted: function () { + return !1; }, - set: function (_currentValue2) { - context._currentValue2 = _currentValue2; - } - }, - _threadCount: { - get: function () { - return context._threadCount; - }, - set: function (_threadCount) { - context._threadCount = _threadCount; - } - }, - Consumer: { - get: function () { - if (!hasWarnedAboutUsingNestedContextConsumers) { - hasWarnedAboutUsingNestedContextConsumers = true; - - error('Rendering is not supported and will be removed in ' + 'a future major release. Did you mean to render instead?'); - } - - return context.Consumer; - } - }, - displayName: { - get: function () { - return context.displayName; + enqueueForceUpdate: function (publicInstance) { + warnNoop(publicInstance, "forceUpdate"); }, - set: function (displayName) { - if (!hasWarnedAboutDisplayNameOnConsumer) { - warn('Setting `displayName` on Context.Consumer has no effect. ' + "You should set it directly on the context with Context.displayName = '%s'.", displayName); - - hasWarnedAboutDisplayNameOnConsumer = true; - } - } - } - }); // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty - - context.Consumer = Consumer; - } - - { - context._currentRenderer = null; - context._currentRenderer2 = null; - } - - return context; - } - - var Uninitialized = -1; - var Pending = 0; - var Resolved = 1; - var Rejected = 2; - - function lazyInitializer(payload) { - if (payload._status === Uninitialized) { - var ctor = payload._result; - var thenable = ctor(); // Transition to the next state. - // This might throw either because it's missing or throws. If so, we treat it - // as still uninitialized and try again next time. Which is the same as what - // happens if the ctor or any wrappers processing the ctor throws. This might - // end up fixing it if the resolution was a concurrency bug. - - thenable.then(function (moduleObject) { - if (payload._status === Pending || payload._status === Uninitialized) { - // Transition to the next state. - var resolved = payload; - resolved._status = Resolved; - resolved._result = moduleObject; - } - }, function (error) { - if (payload._status === Pending || payload._status === Uninitialized) { - // Transition to the next state. - var rejected = payload; - rejected._status = Rejected; - rejected._result = error; - } - }); - - if (payload._status === Uninitialized) { - // In case, we're still uninitialized, then we're waiting for the thenable - // to resolve. Set it as pending in the meantime. - var pending = payload; - pending._status = Pending; - pending._result = thenable; - } - } - - if (payload._status === Resolved) { - var moduleObject = payload._result; - - { - if (moduleObject === undefined) { - error('lazy: Expected the result of a dynamic imp' + 'ort() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + // Break up imports to avoid accidentally parsing them as dependencies. - 'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))\n\n" + 'Did you accidentally put curly braces around the import?', moduleObject); - } - } - - { - if (!('default' in moduleObject)) { - error('lazy: Expected the result of a dynamic imp' + 'ort() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + // Break up imports to avoid accidentally parsing them as dependencies. - 'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))", moduleObject); - } - } - - return moduleObject.default; - } else { - throw payload._result; - } - } - - function lazy(ctor) { - var payload = { - // We use these fields to store the result. - _status: Uninitialized, - _result: ctor - }; - var lazyType = { - $$typeof: REACT_LAZY_TYPE, - _payload: payload, - _init: lazyInitializer - }; - - { - // In production, this would just set it on the object. - var defaultProps; - var propTypes; // $FlowFixMe - - Object.defineProperties(lazyType, { - defaultProps: { - configurable: true, - get: function () { - return defaultProps; + enqueueReplaceState: function (publicInstance) { + warnNoop(publicInstance, "replaceState"); }, - set: function (newDefaultProps) { - error('React.lazy(...): It is not supported to assign `defaultProps` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.'); - - defaultProps = newDefaultProps; // Match production behavior more closely: - // $FlowFixMe - - Object.defineProperty(lazyType, 'defaultProps', { - enumerable: true - }); + enqueueSetState: function (publicInstance) { + warnNoop(publicInstance, "setState"); } }, - propTypes: { - configurable: true, - get: function () { - return propTypes; - }, - set: function (newPropTypes) { - error('React.lazy(...): It is not supported to assign `propTypes` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.'); - - propTypes = newPropTypes; // Match production behavior more closely: - // $FlowFixMe - - Object.defineProperty(lazyType, 'propTypes', { - enumerable: true - }); - } - } - }); - } - - return lazyType; - } - - function forwardRef(render) { - { - if (render != null && render.$$typeof === REACT_MEMO_TYPE) { - error('forwardRef requires a render function but received a `memo` ' + 'component. Instead of forwardRef(memo(...)), use ' + 'memo(forwardRef(...)).'); - } else if (typeof render !== 'function') { - error('forwardRef requires a render function but was given %s.', render === null ? 'null' : typeof render); - } else { - if (render.length !== 0 && render.length !== 2) { - error('forwardRef render functions accept exactly two parameters: props and ref. %s', render.length === 1 ? 'Did you forget to use the ref parameter?' : 'Any additional parameter will be undefined.'); - } - } - - if (render != null) { - if (render.defaultProps != null || render.propTypes != null) { - error('forwardRef render functions do not support propTypes or defaultProps. ' + 'Did you accidentally pass a React component?'); - } - } - } - - var elementType = { - $$typeof: REACT_FORWARD_REF_TYPE, - render: render - }; - - { - var ownName; - Object.defineProperty(elementType, 'displayName', { - enumerable: false, - configurable: true, - get: function () { - return ownName; + assign = Object.assign, + emptyObject = {}; + Object.freeze(emptyObject); + Component.prototype.isReactComponent = {}; + Component.prototype.setState = function (partialState, callback) { + if ( + "object" !== typeof partialState && + "function" !== typeof partialState && + null != partialState + ) + throw Error( + "takes an object of state variables to update or a function which returns an object of state variables." + ); + this.updater.enqueueSetState(this, partialState, callback, "setState"); + }; + Component.prototype.forceUpdate = function (callback) { + this.updater.enqueueForceUpdate(this, callback, "forceUpdate"); + }; + var deprecatedAPIs = { + isMounted: [ + "isMounted", + "Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks." + ], + replaceState: [ + "replaceState", + "Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)." + ] }, - set: function (name) { - ownName = name; // The inner component shouldn't inherit this display name in most cases, - // because the component may be used elsewhere. - // But it's nice for anonymous functions to inherit the name, - // so that our component-stack generation logic will display their frames. - // An anonymous function generally suggests a pattern like: - // React.forwardRef((props, ref) => {...}); - // This kind of inner function is not used elsewhere so the side effect is okay. - - if (!render.name && !render.displayName) { - render.displayName = name; - } - } - }); - } - - return elementType; - } - - var REACT_MODULE_REFERENCE; - - { - REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); - } - - function isValidElementType(type) { - if (typeof type === 'string' || typeof type === 'function') { - return true; - } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). - - - if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) { - return true; - } - - if (typeof type === 'object' && type !== null) { - if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object - // types supported by any Flight configuration anywhere since - // we don't know which Flight build this will end up being used - // with. - type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) { - return true; - } - } - - return false; - } - - function memo(type, compare) { - { - if (!isValidElementType(type)) { - error('memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type); - } - } - - var elementType = { - $$typeof: REACT_MEMO_TYPE, - type: type, - compare: compare === undefined ? null : compare - }; - - { - var ownName; - Object.defineProperty(elementType, 'displayName', { - enumerable: false, - configurable: true, - get: function () { - return ownName; + fnName; + for (fnName in deprecatedAPIs) + deprecatedAPIs.hasOwnProperty(fnName) && + defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); + ComponentDummy.prototype = Component.prototype; + deprecatedAPIs = PureComponent.prototype = new ComponentDummy(); + deprecatedAPIs.constructor = PureComponent; + assign(deprecatedAPIs, Component.prototype); + deprecatedAPIs.isPureReactComponent = !0; + var isArrayImpl = Array.isArray, + REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), + ReactSharedInternals = { + H: null, + A: null, + T: null, + S: null, + V: null, + actQueue: null, + isBatchingLegacy: !1, + didScheduleLegacyUpdate: !1, + didUsePromise: !1, + thrownErrors: [], + getCurrentStack: null, + recentlyCreatedOwnerStacks: 0 }, - set: function (name) { - ownName = name; // The inner component shouldn't inherit this display name in most cases, - // because the component may be used elsewhere. - // But it's nice for anonymous functions to inherit the name, - // so that our component-stack generation logic will display their frames. - // An anonymous function generally suggests a pattern like: - // React.memo((props) => {...}); - // This kind of inner function is not used elsewhere so the side effect is okay. - - if (!type.name && !type.displayName) { - type.displayName = name; - } - } - }); - } - - return elementType; - } - - function resolveDispatcher() { - var dispatcher = ReactCurrentDispatcher.current; - - { - if (dispatcher === null) { - error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'); - } - } // Will result in a null access error if accessed outside render phase. We - // intentionally don't throw our own error because this is in a hot path. - // Also helps ensure this is inlined. - - - return dispatcher; - } - function useContext(Context) { - var dispatcher = resolveDispatcher(); - - { - // TODO: add a more generic warning for invalid values. - if (Context._context !== undefined) { - var realContext = Context._context; // Don't deduplicate because this legitimately causes bugs - // and nobody should be using this in existing code. - - if (realContext.Consumer === Context) { - error('Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' + 'removed in a future major release. Did you mean to call useContext(Context) instead?'); - } else if (realContext.Provider === Context) { - error('Calling useContext(Context.Provider) is not supported. ' + 'Did you mean to call useContext(Context) instead?'); - } - } - } - - return dispatcher.useContext(Context); - } - function useState(initialState) { - var dispatcher = resolveDispatcher(); - return dispatcher.useState(initialState); - } - function useReducer(reducer, initialArg, init) { - var dispatcher = resolveDispatcher(); - return dispatcher.useReducer(reducer, initialArg, init); - } - function useRef(initialValue) { - var dispatcher = resolveDispatcher(); - return dispatcher.useRef(initialValue); - } - function useEffect(create, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useEffect(create, deps); - } - function useInsertionEffect(create, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useInsertionEffect(create, deps); - } - function useLayoutEffect(create, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useLayoutEffect(create, deps); - } - function useCallback(callback, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useCallback(callback, deps); - } - function useMemo(create, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useMemo(create, deps); - } - function useImperativeHandle(ref, create, deps) { - var dispatcher = resolveDispatcher(); - return dispatcher.useImperativeHandle(ref, create, deps); - } - function useDebugValue(value, formatterFn) { - { - var dispatcher = resolveDispatcher(); - return dispatcher.useDebugValue(value, formatterFn); - } - } - function useTransition() { - var dispatcher = resolveDispatcher(); - return dispatcher.useTransition(); - } - function useDeferredValue(value) { - var dispatcher = resolveDispatcher(); - return dispatcher.useDeferredValue(value); - } - function useId() { - var dispatcher = resolveDispatcher(); - return dispatcher.useId(); - } - function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) { - var dispatcher = resolveDispatcher(); - return dispatcher.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - } - - // Helpers to patch console.logs to avoid logging during side-effect free - // replaying on render function. This currently only patches the object - // lazily which won't cover if the log function was extracted eagerly. - // We could also eagerly patch the method. - var disabledDepth = 0; - var prevLog; - var prevInfo; - var prevWarn; - var prevError; - var prevGroup; - var prevGroupCollapsed; - var prevGroupEnd; - - function disabledLog() {} - - disabledLog.__reactDisabledLog = true; - function disableLogs() { - { - if (disabledDepth === 0) { - /* eslint-disable react-internal/no-production-logging */ - prevLog = console.log; - prevInfo = console.info; - prevWarn = console.warn; - prevError = console.error; - prevGroup = console.group; - prevGroupCollapsed = console.groupCollapsed; - prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099 - - var props = { - configurable: true, - enumerable: true, - value: disabledLog, - writable: true - }; // $FlowFixMe Flow thinks console is immutable. - - Object.defineProperties(console, { - info: props, - log: props, - warn: props, - error: props, - group: props, - groupCollapsed: props, - groupEnd: props - }); - /* eslint-enable react-internal/no-production-logging */ - } - - disabledDepth++; - } - } - function reenableLogs() { - { - disabledDepth--; - - if (disabledDepth === 0) { - /* eslint-disable react-internal/no-production-logging */ - var props = { - configurable: true, - enumerable: true, - writable: true - }; // $FlowFixMe Flow thinks console is immutable. - - Object.defineProperties(console, { - log: assign({}, props, { - value: prevLog - }), - info: assign({}, props, { - value: prevInfo - }), - warn: assign({}, props, { - value: prevWarn - }), - error: assign({}, props, { - value: prevError - }), - group: assign({}, props, { - value: prevGroup - }), - groupCollapsed: assign({}, props, { - value: prevGroupCollapsed - }), - groupEnd: assign({}, props, { - value: prevGroupEnd - }) - }); - /* eslint-enable react-internal/no-production-logging */ - } - - if (disabledDepth < 0) { - error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.'); - } - } - } - - var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher; - var prefix; - function describeBuiltInComponentFrame(name, source, ownerFn) { - { - if (prefix === undefined) { - // Extract the VM specific prefix used by each line. - try { - throw Error(); - } catch (x) { - var match = x.stack.trim().match(/\n( *(at )?)/); - prefix = match && match[1] || ''; - } - } // We use the prefix to ensure our stacks line up with native stack frames. - - - return '\n' + prefix + name; - } - } - var reentry = false; - var componentFrameCache; - - { - var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; - componentFrameCache = new PossiblyWeakMap(); - } - - function describeNativeComponentFrame(fn, construct) { - // If something asked for a stack inside a fake render, it should get ignored. - if ( !fn || reentry) { - return ''; - } - - { - var frame = componentFrameCache.get(fn); - - if (frame !== undefined) { - return frame; - } - } - - var control; - reentry = true; - var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined. - - Error.prepareStackTrace = undefined; - var previousDispatcher; - - { - previousDispatcher = ReactCurrentDispatcher$1.current; // Set the dispatcher in DEV because this might be call in the render function - // for warnings. - - ReactCurrentDispatcher$1.current = null; - disableLogs(); - } - - try { - // This should throw. - if (construct) { - // Something should be setting the props in the constructor. - var Fake = function () { - throw Error(); - }; // $FlowFixMe - - - Object.defineProperty(Fake.prototype, 'props', { - set: function () { - // We use a throwing setter instead of frozen or non-writable props - // because that won't throw in a non-strict mode function. - throw Error(); - } - }); - - if (typeof Reflect === 'object' && Reflect.construct) { - // We construct a different control for this case to include any extra - // frames added by the construct call. - try { - Reflect.construct(Fake, []); - } catch (x) { - control = x; - } - - Reflect.construct(fn, [], Fake); - } else { - try { - Fake.call(); - } catch (x) { - control = x; - } - - fn.call(Fake.prototype); - } - } else { - try { - throw Error(); - } catch (x) { - control = x; - } - - fn(); - } - } catch (sample) { - // This is inlined manually because closure doesn't do it for us. - if (sample && control && typeof sample.stack === 'string') { - // This extracts the first frame from the sample that isn't also in the control. - // Skipping one frame that we assume is the frame that calls the two. - var sampleLines = sample.stack.split('\n'); - var controlLines = control.stack.split('\n'); - var s = sampleLines.length - 1; - var c = controlLines.length - 1; - - while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) { - // We expect at least one stack frame to be shared. - // Typically this will be the root most one. However, stack frames may be - // cut off due to maximum stack limits. In this case, one maybe cut off - // earlier than the other. We assume that the sample is longer or the same - // and there for cut off earlier. So we should find the root most frame in - // the sample somewhere in the control. - c--; + hasOwnProperty = Object.prototype.hasOwnProperty, + createTask = console.createTask + ? console.createTask + : function () { + return null; + }; + deprecatedAPIs = { + "react-stack-bottom-frame": function (callStackForError) { + return callStackForError(); } - - for (; s >= 1 && c >= 0; s--, c--) { - // Next we find the first one that isn't the same which should be the - // frame that called our sample function and the control. - if (sampleLines[s] !== controlLines[c]) { - // In V8, the first line is describing the message but other VMs don't. - // If we're about to return the first line, and the control is also on the same - // line, that's a pretty good indicator that our sample threw at same line as - // the control. I.e. before we entered the sample frame. So we ignore this result. - // This can happen if you passed a class to function component, or non-function. - if (s !== 1 || c !== 1) { - do { - s--; - c--; // We may still have similar intermediate frames from the construct call. - // The next one that isn't the same should be our match though. - - if (c < 0 || sampleLines[s] !== controlLines[c]) { - // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier. - var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "" - // but we have a user-provided "displayName" - // splice it in to make the stack more readable. - - - if (fn.displayName && _frame.includes('')) { - _frame = _frame.replace('', fn.displayName); - } - - { - if (typeof fn === 'function') { - componentFrameCache.set(fn, _frame); - } - } // Return the line we found. - - - return _frame; + }; + var specialPropKeyWarningShown, didWarnAboutOldJSXRuntime; + var didWarnAboutElementRef = {}; + var unknownOwnerDebugStack = deprecatedAPIs[ + "react-stack-bottom-frame" + ].bind(deprecatedAPIs, UnknownOwner)(); + var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner)); + var didWarnAboutMaps = !1, + userProvidedKeyEscapeRegex = /\/+/g, + reportGlobalError = + "function" === typeof reportError + ? reportError + : function (error) { + if ( + "object" === typeof window && + "function" === typeof window.ErrorEvent + ) { + var event = new window.ErrorEvent("error", { + bubbles: !0, + cancelable: !0, + message: + "object" === typeof error && + null !== error && + "string" === typeof error.message + ? String(error.message) + : String(error), + error: error + }); + if (!window.dispatchEvent(event)) return; + } else if ( + "object" === typeof process && + "function" === typeof process.emit + ) { + process.emit("uncaughtException", error); + return; } - } while (s >= 1 && c >= 0); - } - - break; - } - } - } - } finally { - reentry = false; - - { - ReactCurrentDispatcher$1.current = previousDispatcher; - reenableLogs(); - } - - Error.prepareStackTrace = previousPrepareStackTrace; - } // Fallback to just using the name if we couldn't make it throw. - - - var name = fn ? fn.displayName || fn.name : ''; - var syntheticFrame = name ? describeBuiltInComponentFrame(name) : ''; - - { - if (typeof fn === 'function') { - componentFrameCache.set(fn, syntheticFrame); - } - } - - return syntheticFrame; - } - function describeFunctionComponentFrame(fn, source, ownerFn) { - { - return describeNativeComponentFrame(fn, false); - } - } - - function shouldConstruct(Component) { - var prototype = Component.prototype; - return !!(prototype && prototype.isReactComponent); - } - - function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) { - - if (type == null) { - return ''; - } - - if (typeof type === 'function') { - { - return describeNativeComponentFrame(type, shouldConstruct(type)); - } - } - - if (typeof type === 'string') { - return describeBuiltInComponentFrame(type); - } - - switch (type) { - case REACT_SUSPENSE_TYPE: - return describeBuiltInComponentFrame('Suspense'); - - case REACT_SUSPENSE_LIST_TYPE: - return describeBuiltInComponentFrame('SuspenseList'); - } - - if (typeof type === 'object') { - switch (type.$$typeof) { - case REACT_FORWARD_REF_TYPE: - return describeFunctionComponentFrame(type.render); - - case REACT_MEMO_TYPE: - // Memo may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn); - - case REACT_LAZY_TYPE: - { - var lazyComponent = type; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - // Lazy may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn); - } catch (x) {} - } - } - } - - return ''; - } - - var loggedTypeFailures = {}; - var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; - - function setCurrentlyValidatingElement(element) { - { - if (element) { - var owner = element._owner; - var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); - ReactDebugCurrentFrame$1.setExtraStackFrame(stack); - } else { - ReactDebugCurrentFrame$1.setExtraStackFrame(null); - } - } - } - - function checkPropTypes(typeSpecs, values, location, componentName, element) { - { - // $FlowFixMe This is okay but Flow doesn't know it. - var has = Function.call.bind(hasOwnProperty); - - for (var typeSpecName in typeSpecs) { - if (has(typeSpecs, typeSpecName)) { - var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to - // fail the render phase where it didn't fail before. So we log it. - // After these have been cleaned up, we'll let them throw. - - try { - // This is intentionally an invariant that gets caught. It's the same - // behavior as without this statement except with a better message. - if (typeof typeSpecs[typeSpecName] !== 'function') { - // eslint-disable-next-line react-internal/prod-error-codes - var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'); - err.name = 'Invariant Violation'; - throw err; - } - - error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'); - } catch (ex) { - error$1 = ex; - } - - if (error$1 && !(error$1 instanceof Error)) { - setCurrentlyValidatingElement(element); - - error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1); - - setCurrentlyValidatingElement(null); - } - - if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) { - // Only monitor this failure once because there tends to be a lot of the - // same error. - loggedTypeFailures[error$1.message] = true; - setCurrentlyValidatingElement(element); - - error('Failed %s type: %s', location, error$1.message); - - setCurrentlyValidatingElement(null); - } - } - } - } - } - - function setCurrentlyValidatingElement$1(element) { - { - if (element) { - var owner = element._owner; - var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); - setExtraStackFrame(stack); - } else { - setExtraStackFrame(null); - } - } - } - - var propTypesMisspellWarningShown; - - { - propTypesMisspellWarningShown = false; - } - - function getDeclarationErrorAddendum() { - if (ReactCurrentOwner.current) { - var name = getComponentNameFromType(ReactCurrentOwner.current.type); - - if (name) { - return '\n\nCheck the render method of `' + name + '`.'; - } - } - - return ''; - } - - function getSourceInfoErrorAddendum(source) { - if (source !== undefined) { - var fileName = source.fileName.replace(/^.*[\\\/]/, ''); - var lineNumber = source.lineNumber; - return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.'; - } - - return ''; - } - - function getSourceInfoErrorAddendumForProps(elementProps) { - if (elementProps !== null && elementProps !== undefined) { - return getSourceInfoErrorAddendum(elementProps.__source); - } - - return ''; - } - /** - * Warn if there's no key explicitly set on dynamic arrays of children or - * object keys are not valid. This allows us to keep track of children between - * updates. - */ - - - var ownerHasKeyUseWarning = {}; - - function getCurrentComponentErrorInfo(parentType) { - var info = getDeclarationErrorAddendum(); - - if (!info) { - var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name; - - if (parentName) { - info = "\n\nCheck the top-level render call using <" + parentName + ">."; - } - } - - return info; - } - /** - * Warn if the element doesn't have an explicit key assigned to it. - * This element is in an array. The array could grow and shrink or be - * reordered. All children that haven't already been validated are required to - * have a "key" property assigned to it. Error statuses are cached so a warning - * will only be shown once. - * - * @internal - * @param {ReactElement} element Element that requires a key. - * @param {*} parentType element's parent's type. - */ - - - function validateExplicitKey(element, parentType) { - if (!element._store || element._store.validated || element.key != null) { - return; - } - - element._store.validated = true; - var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); - - if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { - return; - } - - ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a - // property, it may be the creator of the child that's responsible for - // assigning it a key. - - var childOwner = ''; - - if (element && element._owner && element._owner !== ReactCurrentOwner.current) { - // Give the component that originally created this child. - childOwner = " It was passed a child from " + getComponentNameFromType(element._owner.type) + "."; - } - - { - setCurrentlyValidatingElement$1(element); - - error('Each child in a list should have a unique "key" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner); - - setCurrentlyValidatingElement$1(null); - } - } - /** - * Ensure that every element either is passed in a static location, in an - * array with an explicit keys property defined, or in an object literal - * with valid key property. - * - * @internal - * @param {ReactNode} node Statically passed child of any type. - * @param {*} parentType node's parent's type. - */ - - - function validateChildKeys(node, parentType) { - if (typeof node !== 'object') { - return; - } - - if (isArray(node)) { - for (var i = 0; i < node.length; i++) { - var child = node[i]; - - if (isValidElement(child)) { - validateExplicitKey(child, parentType); - } - } - } else if (isValidElement(node)) { - // This element was passed in a valid location. - if (node._store) { - node._store.validated = true; - } - } else if (node) { - var iteratorFn = getIteratorFn(node); - - if (typeof iteratorFn === 'function') { - // Entry iterators used to provide implicit keys, - // but now we print a separate warning for them later. - if (iteratorFn !== node.entries) { - var iterator = iteratorFn.call(node); - var step; - - while (!(step = iterator.next()).done) { - if (isValidElement(step.value)) { - validateExplicitKey(step.value, parentType); - } - } - } - } - } - } - /** - * Given an element, validate that its props follow the propTypes definition, - * provided by the type. - * - * @param {ReactElement} element - */ - - - function validatePropTypes(element) { - { - var type = element.type; - - if (type === null || type === undefined || typeof type === 'string') { - return; - } - - var propTypes; - - if (typeof type === 'function') { - propTypes = type.propTypes; - } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here. - // Inner props are checked in the reconciler. - type.$$typeof === REACT_MEMO_TYPE)) { - propTypes = type.propTypes; - } else { - return; - } - - if (propTypes) { - // Intentionally inside to avoid triggering lazy initializers: - var name = getComponentNameFromType(type); - checkPropTypes(propTypes, element.props, 'prop', name, element); - } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) { - propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers: - - var _name = getComponentNameFromType(type); - - error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown'); - } - - if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) { - error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.'); - } - } - } - /** - * Given a fragment, validate that it can only be provided with fragment props - * @param {ReactElement} fragment - */ - - - function validateFragmentProps(fragment) { - { - var keys = Object.keys(fragment.props); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - - if (key !== 'children' && key !== 'key') { - setCurrentlyValidatingElement$1(fragment); - - error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key); - - setCurrentlyValidatingElement$1(null); - break; - } - } - - if (fragment.ref !== null) { - setCurrentlyValidatingElement$1(fragment); - - error('Invalid attribute `ref` supplied to `React.Fragment`.'); - - setCurrentlyValidatingElement$1(null); - } - } - } - function createElementWithValidation(type, props, children) { - var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to - // succeed and there will likely be errors in render. - - if (!validType) { - var info = ''; - - if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { - info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; - } - - var sourceInfo = getSourceInfoErrorAddendumForProps(props); - - if (sourceInfo) { - info += sourceInfo; - } else { - info += getDeclarationErrorAddendum(); - } - - var typeString; - - if (type === null) { - typeString = 'null'; - } else if (isArray(type)) { - typeString = 'array'; - } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) { - typeString = "<" + (getComponentNameFromType(type.type) || 'Unknown') + " />"; - info = ' Did you accidentally export a JSX literal instead of a component?'; - } else { - typeString = typeof type; - } - - { - error('React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info); - } - } - - var element = createElement.apply(this, arguments); // The result can be nullish if a mock or a custom function is used. - // TODO: Drop this when these are no longer allowed as the type argument. - - if (element == null) { - return element; - } // Skip key warning if the type isn't valid since our key validation logic - // doesn't expect a non-string/function type and can throw confusing errors. - // We don't want exception behavior to differ between dev and prod. - // (Rendering will throw with a helpful message and as soon as the type is - // fixed, the key warnings will appear.) - - - if (validType) { - for (var i = 2; i < arguments.length; i++) { - validateChildKeys(arguments[i], type); - } - } - - if (type === REACT_FRAGMENT_TYPE) { - validateFragmentProps(element); - } else { - validatePropTypes(element); - } - - return element; - } - var didWarnAboutDeprecatedCreateFactory = false; - function createFactoryWithValidation(type) { - var validatedFactory = createElementWithValidation.bind(null, type); - validatedFactory.type = type; - - { - if (!didWarnAboutDeprecatedCreateFactory) { - didWarnAboutDeprecatedCreateFactory = true; - - warn('React.createFactory() is deprecated and will be removed in ' + 'a future major release. Consider using JSX ' + 'or use React.createElement() directly instead.'); - } // Legacy hook: remove it - - - Object.defineProperty(validatedFactory, 'type', { - enumerable: false, - get: function () { - warn('Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.'); - - Object.defineProperty(this, 'type', { - value: type - }); - return type; + console.error(error); + }, + didWarnAboutMessageChannel = !1, + enqueueTaskImpl = null, + actScopeDepth = 0, + didWarnNoAwaitAct = !1, + isFlushing = !1, + queueSeveralMicrotasks = + "function" === typeof queueMicrotask + ? function (callback) { + queueMicrotask(function () { + return queueMicrotask(callback); + }); + } + : enqueueTask; + deprecatedAPIs = Object.freeze({ + __proto__: null, + c: function (size) { + return resolveDispatcher().useMemoCache(size); } }); - } - - return validatedFactory; - } - function cloneElementWithValidation(element, props, children) { - var newElement = cloneElement.apply(this, arguments); - - for (var i = 2; i < arguments.length; i++) { - validateChildKeys(arguments[i], newElement.type); - } - - validatePropTypes(newElement); - return newElement; - } - - function startTransition(scope, options) { - var prevTransition = ReactCurrentBatchConfig.transition; - ReactCurrentBatchConfig.transition = {}; - var currentTransition = ReactCurrentBatchConfig.transition; - - { - ReactCurrentBatchConfig.transition._updatedFibers = new Set(); - } - - try { - scope(); - } finally { - ReactCurrentBatchConfig.transition = prevTransition; - - { - if (prevTransition === null && currentTransition._updatedFibers) { - var updatedFibersCount = currentTransition._updatedFibers.size; - - if (updatedFibersCount > 10) { - warn('Detected a large number of updates inside startTransition. ' + 'If this is due to a subscription please re-write it to use React provided hooks. ' + 'Otherwise concurrent mode guarantees are off the table.'); - } - - currentTransition._updatedFibers.clear(); - } - } - } - } - - var didWarnAboutMessageChannel = false; - var enqueueTaskImpl = null; - function enqueueTask(task) { - if (enqueueTaskImpl === null) { - try { - // read require off the module object to get around the bundlers. - // we don't want them to detect a require and bundle a Node polyfill. - var requireString = ('require' + Math.random()).slice(0, 7); - var nodeRequire = module && module[requireString]; // assuming we're in node, let's try to get node's - // version of setImmediate, bypassing fake timers if any. - - enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate; - } catch (_err) { - // we're in a browser - // we can't use regular timers because they may still be faked - // so we try MessageChannel+postMessage instead - enqueueTaskImpl = function (callback) { - { - if (didWarnAboutMessageChannel === false) { - didWarnAboutMessageChannel = true; - - if (typeof MessageChannel === 'undefined') { - error('This browser does not have a MessageChannel implementation, ' + 'so enqueuing tasks via await act(async () => ...) will fail. ' + 'Please file an issue at https://github.com/facebook/react/issues ' + 'if you encounter this warning.'); - } - } - } - - var channel = new MessageChannel(); - channel.port1.onmessage = callback; - channel.port2.postMessage(undefined); - }; - } - } - - return enqueueTaskImpl(task); - } - - var actScopeDepth = 0; - var didWarnNoAwaitAct = false; - function act(callback) { - { - // `act` calls can be nested, so we track the depth. This represents the - // number of `act` scopes on the stack. - var prevActScopeDepth = actScopeDepth; - actScopeDepth++; - - if (ReactCurrentActQueue.current === null) { - // This is the outermost `act` scope. Initialize the queue. The reconciler - // will detect the queue and use it instead of Scheduler. - ReactCurrentActQueue.current = []; - } - - var prevIsBatchingLegacy = ReactCurrentActQueue.isBatchingLegacy; - var result; - - try { - // Used to reproduce behavior of `batchedUpdates` in legacy mode. Only - // set to `true` while the given callback is executed, not for updates - // triggered during an async event, because this is how the legacy - // implementation of `act` behaved. - ReactCurrentActQueue.isBatchingLegacy = true; - result = callback(); // Replicate behavior of original `act` implementation in legacy mode, - // which flushed updates immediately after the scope function exits, even - // if it's an async function. - - if (!prevIsBatchingLegacy && ReactCurrentActQueue.didScheduleLegacyUpdate) { - var queue = ReactCurrentActQueue.current; - - if (queue !== null) { - ReactCurrentActQueue.didScheduleLegacyUpdate = false; - flushActQueue(queue); - } - } - } catch (error) { - popActScope(prevActScopeDepth); - throw error; - } finally { - ReactCurrentActQueue.isBatchingLegacy = prevIsBatchingLegacy; - } - - if (result !== null && typeof result === 'object' && typeof result.then === 'function') { - var thenableResult = result; // The callback is an async function (i.e. returned a promise). Wait - // for it to resolve before exiting the current scope. - - var wasAwaited = false; - var thenable = { - then: function (resolve, reject) { - wasAwaited = true; - thenableResult.then(function (returnValue) { - popActScope(prevActScopeDepth); - - if (actScopeDepth === 0) { - // We've exited the outermost act scope. Recursively flush the - // queue until there's no remaining work. - recursivelyFlushAsyncActWork(returnValue, resolve, reject); - } else { - resolve(returnValue); - } - }, function (error) { - // The callback threw an error. - popActScope(prevActScopeDepth); - reject(error); - }); - } - }; - - { - if (!didWarnNoAwaitAct && typeof Promise !== 'undefined') { - // eslint-disable-next-line no-undef - Promise.resolve().then(function () {}).then(function () { - if (!wasAwaited) { - didWarnNoAwaitAct = true; - - error('You called act(async () => ...) without await. ' + 'This could lead to unexpected testing behaviour, ' + 'interleaving multiple act calls and mixing their ' + 'scopes. ' + 'You should - await act(async () => ...);'); - } - }); - } - } - - return thenable; - } else { - var returnValue = result; // The callback is not an async function. Exit the current scope - // immediately, without awaiting. - - popActScope(prevActScopeDepth); - - if (actScopeDepth === 0) { - // Exiting the outermost act scope. Flush the queue. - var _queue = ReactCurrentActQueue.current; - - if (_queue !== null) { - flushActQueue(_queue); - ReactCurrentActQueue.current = null; - } // Return a thenable. If the user awaits it, we'll flush again in - // case additional work was scheduled by a microtask. - - - var _thenable = { - then: function (resolve, reject) { - // Confirm we haven't re-entered another `act` scope, in case - // the user does something weird like await the thenable - // multiple times. - if (ReactCurrentActQueue.current === null) { - // Recursively flush the queue until there's no remaining work. - ReactCurrentActQueue.current = []; - recursivelyFlushAsyncActWork(returnValue, resolve, reject); - } else { - resolve(returnValue); - } - } - }; - return _thenable; - } else { - // Since we're inside a nested `act` scope, the returned thenable - // immediately resolves. The outer scope will flush the queue. - var _thenable2 = { - then: function (resolve, reject) { - resolve(returnValue); - } - }; - return _thenable2; - } - } - } - } - - function popActScope(prevActScopeDepth) { - { - if (prevActScopeDepth !== actScopeDepth - 1) { - error('You seem to have overlapping act() calls, this is not supported. ' + 'Be sure to await previous act() calls before making a new one. '); - } - - actScopeDepth = prevActScopeDepth; - } - } - - function recursivelyFlushAsyncActWork(returnValue, resolve, reject) { - { - var queue = ReactCurrentActQueue.current; - - if (queue !== null) { - try { - flushActQueue(queue); - enqueueTask(function () { - if (queue.length === 0) { - // No additional work was scheduled. Finish. - ReactCurrentActQueue.current = null; - resolve(returnValue); - } else { - // Keep flushing work until there's none left. - recursivelyFlushAsyncActWork(returnValue, resolve, reject); - } - }); - } catch (error) { - reject(error); - } - } else { - resolve(returnValue); - } - } - } - - var isFlushing = false; - - function flushActQueue(queue) { - { - if (!isFlushing) { - // Prevent re-entrance. - isFlushing = true; - var i = 0; - - try { - for (; i < queue.length; i++) { - var callback = queue[i]; - - do { - callback = callback(true); - } while (callback !== null); - } - - queue.length = 0; - } catch (error) { - // If something throws, leave the remaining callbacks on the queue. - queue = queue.slice(i + 1); - throw error; - } finally { - isFlushing = false; - } - } - } - } - - var createElement$1 = createElementWithValidation ; - var cloneElement$1 = cloneElementWithValidation ; - var createFactory = createFactoryWithValidation ; - var Children = { - map: mapChildren, - forEach: forEachChildren, - count: countChildren, - toArray: toArray, - only: onlyChild - }; - - exports.Children = Children; - exports.Component = Component; - exports.Fragment = REACT_FRAGMENT_TYPE; - exports.Profiler = REACT_PROFILER_TYPE; - exports.PureComponent = PureComponent; - exports.StrictMode = REACT_STRICT_MODE_TYPE; - exports.Suspense = REACT_SUSPENSE_TYPE; - exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals; - exports.act = act; - exports.cloneElement = cloneElement$1; - exports.createContext = createContext; - exports.createElement = createElement$1; - exports.createFactory = createFactory; - exports.createRef = createRef; - exports.forwardRef = forwardRef; - exports.isValidElement = isValidElement; - exports.lazy = lazy; - exports.memo = memo; - exports.startTransition = startTransition; - exports.unstable_act = act; - exports.useCallback = useCallback; - exports.useContext = useContext; - exports.useDebugValue = useDebugValue; - exports.useDeferredValue = useDeferredValue; - exports.useEffect = useEffect; - exports.useId = useId; - exports.useImperativeHandle = useImperativeHandle; - exports.useInsertionEffect = useInsertionEffect; - exports.useLayoutEffect = useLayoutEffect; - exports.useMemo = useMemo; - exports.useReducer = useReducer; - exports.useRef = useRef; - exports.useState = useState; - exports.useSyncExternalStore = useSyncExternalStore; - exports.useTransition = useTransition; - exports.version = ReactVersion; - /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ - if ( - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && - typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop === - 'function' - ) { - __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error()); - } - - })(); - } -} (react_development, react_developmentExports)); - return react_developmentExports; -} - -(function (module) { - - if (process.env.NODE_ENV === 'production') { - module.exports = requireReact_production_min(); - } else { - module.exports = requireReact_development(); - } -} (react)); - -/** - * @license React - * react-jsx-runtime.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -var hasRequiredReactJsxRuntime_production_min; - -function requireReactJsxRuntime_production_min () { - if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min; - hasRequiredReactJsxRuntime_production_min = 1; -var f=reactExports,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0}; - function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q; - return reactJsxRuntime_production_min; -} - -var reactJsxRuntime_development = {}; - -/** - * @license React - * react-jsx-runtime.development.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -var hasRequiredReactJsxRuntime_development; - -function requireReactJsxRuntime_development () { - if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development; - hasRequiredReactJsxRuntime_development = 1; - - if (process.env.NODE_ENV !== "production") { - (function() { - - var React = reactExports; - - // ATTENTION - // When adding new symbols to this file, - // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' - // The Symbol used to tag the ReactElement-like types. - var REACT_ELEMENT_TYPE = Symbol.for('react.element'); - var REACT_PORTAL_TYPE = Symbol.for('react.portal'); - var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); - var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); - var REACT_PROFILER_TYPE = Symbol.for('react.profiler'); - var REACT_PROVIDER_TYPE = Symbol.for('react.provider'); - var REACT_CONTEXT_TYPE = Symbol.for('react.context'); - var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); - var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); - var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); - var REACT_MEMO_TYPE = Symbol.for('react.memo'); - var REACT_LAZY_TYPE = Symbol.for('react.lazy'); - var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); - var MAYBE_ITERATOR_SYMBOL = Symbol.iterator; - var FAUX_ITERATOR_SYMBOL = '@@iterator'; - function getIteratorFn(maybeIterable) { - if (maybeIterable === null || typeof maybeIterable !== 'object') { - return null; - } - - var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; - - if (typeof maybeIterator === 'function') { - return maybeIterator; - } - - return null; - } - - var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - - function error(format) { - { - { - for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { - args[_key2 - 1] = arguments[_key2]; - } - - printWarning('error', format, args); - } - } - } - - function printWarning(level, format, args) { - // When changing this logic, you might want to also - // update consoleWithStackDev.www.js as well. - { - var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; - var stack = ReactDebugCurrentFrame.getStackAddendum(); - - if (stack !== '') { - format += '%s'; - args = args.concat([stack]); - } // eslint-disable-next-line react-internal/safe-string-coercion - - - var argsWithFormat = args.map(function (item) { - return String(item); - }); // Careful: RN currently depends on this prefix - - argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it - // breaks IE9: https://github.com/facebook/react/issues/13610 - // eslint-disable-next-line react-internal/no-production-logging - - Function.prototype.apply.call(console[level], console, argsWithFormat); - } - } - - // ----------------------------------------------------------------------------- - - var enableScopeAPI = false; // Experimental Create Event Handle API. - var enableCacheElement = false; - var enableTransitionTracing = false; // No known bugs, but needs performance testing - - var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber - // stuff. Intended to enable React core members to more easily debug scheduling - // issues in DEV builds. - - var enableDebugTracing = false; // Track which Fiber(s) schedule render work. - - var REACT_MODULE_REFERENCE; - - { - REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); - } - - function isValidElementType(type) { - if (typeof type === 'string' || typeof type === 'function') { - return true; - } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). - - - if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) { - return true; - } - - if (typeof type === 'object' && type !== null) { - if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object - // types supported by any Flight configuration anywhere since - // we don't know which Flight build this will end up being used - // with. - type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) { - return true; - } - } - - return false; - } - - function getWrappedName(outerType, innerType, wrapperName) { - var displayName = outerType.displayName; - - if (displayName) { - return displayName; - } - - var functionName = innerType.displayName || innerType.name || ''; - return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName; - } // Keep in sync with react-reconciler/getComponentNameFromFiber - - - function getContextName(type) { - return type.displayName || 'Context'; - } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead. - - - function getComponentNameFromType(type) { - if (type == null) { - // Host root, text node or just invalid type. - return null; - } - - { - if (typeof type.tag === 'number') { - error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.'); - } - } - - if (typeof type === 'function') { - return type.displayName || type.name || null; - } - - if (typeof type === 'string') { - return type; - } - - switch (type) { - case REACT_FRAGMENT_TYPE: - return 'Fragment'; - - case REACT_PORTAL_TYPE: - return 'Portal'; - - case REACT_PROFILER_TYPE: - return 'Profiler'; - - case REACT_STRICT_MODE_TYPE: - return 'StrictMode'; - - case REACT_SUSPENSE_TYPE: - return 'Suspense'; - - case REACT_SUSPENSE_LIST_TYPE: - return 'SuspenseList'; - - } - - if (typeof type === 'object') { - switch (type.$$typeof) { - case REACT_CONTEXT_TYPE: - var context = type; - return getContextName(context) + '.Consumer'; - - case REACT_PROVIDER_TYPE: - var provider = type; - return getContextName(provider._context) + '.Provider'; - - case REACT_FORWARD_REF_TYPE: - return getWrappedName(type, type.render, 'ForwardRef'); - - case REACT_MEMO_TYPE: - var outerName = type.displayName || null; - - if (outerName !== null) { - return outerName; - } - - return getComponentNameFromType(type.type) || 'Memo'; - - case REACT_LAZY_TYPE: - { - var lazyComponent = type; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - return getComponentNameFromType(init(payload)); - } catch (x) { - return null; - } - } - - // eslint-disable-next-line no-fallthrough - } - } - - return null; - } - - var assign = Object.assign; - - // Helpers to patch console.logs to avoid logging during side-effect free - // replaying on render function. This currently only patches the object - // lazily which won't cover if the log function was extracted eagerly. - // We could also eagerly patch the method. - var disabledDepth = 0; - var prevLog; - var prevInfo; - var prevWarn; - var prevError; - var prevGroup; - var prevGroupCollapsed; - var prevGroupEnd; - - function disabledLog() {} - - disabledLog.__reactDisabledLog = true; - function disableLogs() { - { - if (disabledDepth === 0) { - /* eslint-disable react-internal/no-production-logging */ - prevLog = console.log; - prevInfo = console.info; - prevWarn = console.warn; - prevError = console.error; - prevGroup = console.group; - prevGroupCollapsed = console.groupCollapsed; - prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099 - - var props = { - configurable: true, - enumerable: true, - value: disabledLog, - writable: true - }; // $FlowFixMe Flow thinks console is immutable. - - Object.defineProperties(console, { - info: props, - log: props, - warn: props, - error: props, - group: props, - groupCollapsed: props, - groupEnd: props - }); - /* eslint-enable react-internal/no-production-logging */ - } - - disabledDepth++; - } - } - function reenableLogs() { - { - disabledDepth--; - - if (disabledDepth === 0) { - /* eslint-disable react-internal/no-production-logging */ - var props = { - configurable: true, - enumerable: true, - writable: true - }; // $FlowFixMe Flow thinks console is immutable. - - Object.defineProperties(console, { - log: assign({}, props, { - value: prevLog - }), - info: assign({}, props, { - value: prevInfo - }), - warn: assign({}, props, { - value: prevWarn - }), - error: assign({}, props, { - value: prevError - }), - group: assign({}, props, { - value: prevGroup - }), - groupCollapsed: assign({}, props, { - value: prevGroupCollapsed - }), - groupEnd: assign({}, props, { - value: prevGroupEnd - }) - }); - /* eslint-enable react-internal/no-production-logging */ - } - - if (disabledDepth < 0) { - error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.'); - } - } - } - - var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; - var prefix; - function describeBuiltInComponentFrame(name, source, ownerFn) { - { - if (prefix === undefined) { - // Extract the VM specific prefix used by each line. - try { - throw Error(); - } catch (x) { - var match = x.stack.trim().match(/\n( *(at )?)/); - prefix = match && match[1] || ''; - } - } // We use the prefix to ensure our stacks line up with native stack frames. - - - return '\n' + prefix + name; - } - } - var reentry = false; - var componentFrameCache; - - { - var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; - componentFrameCache = new PossiblyWeakMap(); - } - - function describeNativeComponentFrame(fn, construct) { - // If something asked for a stack inside a fake render, it should get ignored. - if ( !fn || reentry) { - return ''; - } - - { - var frame = componentFrameCache.get(fn); - - if (frame !== undefined) { - return frame; - } - } - - var control; - reentry = true; - var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined. - - Error.prepareStackTrace = undefined; - var previousDispatcher; - - { - previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function - // for warnings. - - ReactCurrentDispatcher.current = null; - disableLogs(); - } - - try { - // This should throw. - if (construct) { - // Something should be setting the props in the constructor. - var Fake = function () { - throw Error(); - }; // $FlowFixMe - - - Object.defineProperty(Fake.prototype, 'props', { - set: function () { - // We use a throwing setter instead of frozen or non-writable props - // because that won't throw in a non-strict mode function. - throw Error(); - } - }); - - if (typeof Reflect === 'object' && Reflect.construct) { - // We construct a different control for this case to include any extra - // frames added by the construct call. - try { - Reflect.construct(Fake, []); - } catch (x) { - control = x; - } - - Reflect.construct(fn, [], Fake); - } else { - try { - Fake.call(); - } catch (x) { - control = x; - } - - fn.call(Fake.prototype); - } - } else { - try { - throw Error(); - } catch (x) { - control = x; - } - - fn(); - } - } catch (sample) { - // This is inlined manually because closure doesn't do it for us. - if (sample && control && typeof sample.stack === 'string') { - // This extracts the first frame from the sample that isn't also in the control. - // Skipping one frame that we assume is the frame that calls the two. - var sampleLines = sample.stack.split('\n'); - var controlLines = control.stack.split('\n'); - var s = sampleLines.length - 1; - var c = controlLines.length - 1; - - while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) { - // We expect at least one stack frame to be shared. - // Typically this will be the root most one. However, stack frames may be - // cut off due to maximum stack limits. In this case, one maybe cut off - // earlier than the other. We assume that the sample is longer or the same - // and there for cut off earlier. So we should find the root most frame in - // the sample somewhere in the control. - c--; - } - - for (; s >= 1 && c >= 0; s--, c--) { - // Next we find the first one that isn't the same which should be the - // frame that called our sample function and the control. - if (sampleLines[s] !== controlLines[c]) { - // In V8, the first line is describing the message but other VMs don't. - // If we're about to return the first line, and the control is also on the same - // line, that's a pretty good indicator that our sample threw at same line as - // the control. I.e. before we entered the sample frame. So we ignore this result. - // This can happen if you passed a class to function component, or non-function. - if (s !== 1 || c !== 1) { - do { - s--; - c--; // We may still have similar intermediate frames from the construct call. - // The next one that isn't the same should be our match though. - - if (c < 0 || sampleLines[s] !== controlLines[c]) { - // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier. - var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "" - // but we have a user-provided "displayName" - // splice it in to make the stack more readable. - - - if (fn.displayName && _frame.includes('')) { - _frame = _frame.replace('', fn.displayName); - } - - { - if (typeof fn === 'function') { - componentFrameCache.set(fn, _frame); - } - } // Return the line we found. - - - return _frame; - } - } while (s >= 1 && c >= 0); - } - - break; - } - } - } - } finally { - reentry = false; - - { - ReactCurrentDispatcher.current = previousDispatcher; - reenableLogs(); - } - - Error.prepareStackTrace = previousPrepareStackTrace; - } // Fallback to just using the name if we couldn't make it throw. - - - var name = fn ? fn.displayName || fn.name : ''; - var syntheticFrame = name ? describeBuiltInComponentFrame(name) : ''; - - { - if (typeof fn === 'function') { - componentFrameCache.set(fn, syntheticFrame); - } - } - - return syntheticFrame; - } - function describeFunctionComponentFrame(fn, source, ownerFn) { - { - return describeNativeComponentFrame(fn, false); - } - } - - function shouldConstruct(Component) { - var prototype = Component.prototype; - return !!(prototype && prototype.isReactComponent); - } - - function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) { - - if (type == null) { - return ''; - } - - if (typeof type === 'function') { - { - return describeNativeComponentFrame(type, shouldConstruct(type)); - } - } - - if (typeof type === 'string') { - return describeBuiltInComponentFrame(type); - } - - switch (type) { - case REACT_SUSPENSE_TYPE: - return describeBuiltInComponentFrame('Suspense'); - - case REACT_SUSPENSE_LIST_TYPE: - return describeBuiltInComponentFrame('SuspenseList'); - } - - if (typeof type === 'object') { - switch (type.$$typeof) { - case REACT_FORWARD_REF_TYPE: - return describeFunctionComponentFrame(type.render); - - case REACT_MEMO_TYPE: - // Memo may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn); - - case REACT_LAZY_TYPE: - { - var lazyComponent = type; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - // Lazy may contain any component type so we recursively resolve it. - return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn); - } catch (x) {} - } - } - } - - return ''; - } - - var hasOwnProperty = Object.prototype.hasOwnProperty; - - var loggedTypeFailures = {}; - var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; - - function setCurrentlyValidatingElement(element) { - { - if (element) { - var owner = element._owner; - var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); - ReactDebugCurrentFrame.setExtraStackFrame(stack); - } else { - ReactDebugCurrentFrame.setExtraStackFrame(null); - } - } - } - - function checkPropTypes(typeSpecs, values, location, componentName, element) { - { - // $FlowFixMe This is okay but Flow doesn't know it. - var has = Function.call.bind(hasOwnProperty); - - for (var typeSpecName in typeSpecs) { - if (has(typeSpecs, typeSpecName)) { - var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to - // fail the render phase where it didn't fail before. So we log it. - // After these have been cleaned up, we'll let them throw. - - try { - // This is intentionally an invariant that gets caught. It's the same - // behavior as without this statement except with a better message. - if (typeof typeSpecs[typeSpecName] !== 'function') { - // eslint-disable-next-line react-internal/prod-error-codes - var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'); - err.name = 'Invariant Violation'; - throw err; - } - - error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'); - } catch (ex) { - error$1 = ex; - } - - if (error$1 && !(error$1 instanceof Error)) { - setCurrentlyValidatingElement(element); - - error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1); - - setCurrentlyValidatingElement(null); - } - - if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) { - // Only monitor this failure once because there tends to be a lot of the - // same error. - loggedTypeFailures[error$1.message] = true; - setCurrentlyValidatingElement(element); - - error('Failed %s type: %s', location, error$1.message); - - setCurrentlyValidatingElement(null); - } - } - } - } - } - - var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare - - function isArray(a) { - return isArrayImpl(a); - } - - /* - * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol - * and Temporal.* types. See https://github.com/facebook/react/pull/22064. - * - * The functions in this module will throw an easier-to-understand, - * easier-to-debug exception with a clear errors message message explaining the - * problem. (Instead of a confusing exception thrown inside the implementation - * of the `value` object). - */ - // $FlowFixMe only called in DEV, so void return is not possible. - function typeName(value) { - { - // toStringTag is needed for namespaced types like Temporal.Instant - var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; - var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; - return type; - } - } // $FlowFixMe only called in DEV, so void return is not possible. - - - function willCoercionThrow(value) { - { - try { - testStringCoercion(value); - return false; - } catch (e) { - return true; - } - } - } - - function testStringCoercion(value) { - // If you ended up here by following an exception call stack, here's what's - // happened: you supplied an object or symbol value to React (as a prop, key, - // DOM attribute, CSS property, string ref, etc.) and when React tried to - // coerce it to a string using `'' + value`, an exception was thrown. - // - // The most common types that will cause this exception are `Symbol` instances - // and Temporal objects like `Temporal.Instant`. But any object that has a - // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this - // exception. (Library authors do this to prevent users from using built-in - // numeric operators like `+` or comparison operators like `>=` because custom - // methods are needed to perform accurate arithmetic or comparison.) - // - // To fix the problem, coerce this object or symbol value to a string before - // passing it to React. The most reliable way is usually `String(value)`. - // - // To find which value is throwing, check the browser or debugger console. - // Before this exception was thrown, there should be `console.error` output - // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the - // problem and how that type was used: key, atrribute, input value prop, etc. - // In most cases, this console output also shows the component and its - // ancestor components where the exception happened. - // - // eslint-disable-next-line react-internal/safe-string-coercion - return '' + value; - } - function checkKeyStringCoercion(value) { - { - if (willCoercionThrow(value)) { - error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); - - return testStringCoercion(value); // throw (to help callers find troubleshooting comments) - } - } - } - - var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; - var RESERVED_PROPS = { - key: true, - ref: true, - __self: true, - __source: true - }; - var specialPropKeyWarningShown; - var specialPropRefWarningShown; - var didWarnAboutStringRefs; - - { - didWarnAboutStringRefs = {}; - } - - function hasValidRef(config) { - { - if (hasOwnProperty.call(config, 'ref')) { - var getter = Object.getOwnPropertyDescriptor(config, 'ref').get; - - if (getter && getter.isReactWarning) { - return false; - } - } - } - - return config.ref !== undefined; - } - - function hasValidKey(config) { - { - if (hasOwnProperty.call(config, 'key')) { - var getter = Object.getOwnPropertyDescriptor(config, 'key').get; - - if (getter && getter.isReactWarning) { - return false; - } - } - } - - return config.key !== undefined; - } - - function warnIfStringRefCannotBeAutoConverted(config, self) { - { - if (typeof config.ref === 'string' && ReactCurrentOwner.current && self && ReactCurrentOwner.current.stateNode !== self) { - var componentName = getComponentNameFromType(ReactCurrentOwner.current.type); - - if (!didWarnAboutStringRefs[componentName]) { - error('Component "%s" contains the string ref "%s". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref', getComponentNameFromType(ReactCurrentOwner.current.type), config.ref); - - didWarnAboutStringRefs[componentName] = true; - } - } - } - } - - function defineKeyPropWarningGetter(props, displayName) { - { - var warnAboutAccessingKey = function () { - if (!specialPropKeyWarningShown) { - specialPropKeyWarningShown = true; - - error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); - } - }; - - warnAboutAccessingKey.isReactWarning = true; - Object.defineProperty(props, 'key', { - get: warnAboutAccessingKey, - configurable: true - }); - } - } - - function defineRefPropWarningGetter(props, displayName) { - { - var warnAboutAccessingRef = function () { - if (!specialPropRefWarningShown) { - specialPropRefWarningShown = true; + exports.Children = { + map: mapChildren, + forEach: function (children, forEachFunc, forEachContext) { + mapChildren( + children, + function () { + forEachFunc.apply(this, arguments); + }, + forEachContext + ); + }, + count: function (children) { + var n = 0; + mapChildren(children, function () { + n++; + }); + return n; + }, + toArray: function (children) { + return ( + mapChildren(children, function (child) { + return child; + }) || [] + ); + }, + only: function (children) { + if (!isValidElement(children)) + throw Error( + "React.Children.only expected to receive a single React element child." + ); + return children; + } + }; + exports.Component = Component; + exports.Fragment = REACT_FRAGMENT_TYPE; + exports.Profiler = REACT_PROFILER_TYPE; + exports.PureComponent = PureComponent; + exports.StrictMode = REACT_STRICT_MODE_TYPE; + exports.Suspense = REACT_SUSPENSE_TYPE; + exports.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = + ReactSharedInternals; + exports.__COMPILER_RUNTIME = deprecatedAPIs; + exports.act = function (callback) { + var prevActQueue = ReactSharedInternals.actQueue, + prevActScopeDepth = actScopeDepth; + actScopeDepth++; + var queue = (ReactSharedInternals.actQueue = + null !== prevActQueue ? prevActQueue : []), + didAwaitActCall = !1; + try { + var result = callback(); + } catch (error) { + ReactSharedInternals.thrownErrors.push(error); + } + if (0 < ReactSharedInternals.thrownErrors.length) + throw ( + (popActScope(prevActQueue, prevActScopeDepth), + (callback = aggregateErrors(ReactSharedInternals.thrownErrors)), + (ReactSharedInternals.thrownErrors.length = 0), + callback) + ); + if ( + null !== result && + "object" === typeof result && + "function" === typeof result.then + ) { + var thenable = result; + queueSeveralMicrotasks(function () { + didAwaitActCall || + didWarnNoAwaitAct || + ((didWarnNoAwaitAct = !0), + console.error( + "You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);" + )); + }); + return { + then: function (resolve, reject) { + didAwaitActCall = !0; + thenable.then( + function (returnValue) { + popActScope(prevActQueue, prevActScopeDepth); + if (0 === prevActScopeDepth) { + try { + flushActQueue(queue), + enqueueTask(function () { + return recursivelyFlushAsyncActWork( + returnValue, + resolve, + reject + ); + }); + } catch (error$0) { + ReactSharedInternals.thrownErrors.push(error$0); + } + if (0 < ReactSharedInternals.thrownErrors.length) { + var _thrownError = aggregateErrors( + ReactSharedInternals.thrownErrors + ); + ReactSharedInternals.thrownErrors.length = 0; + reject(_thrownError); + } + } else resolve(returnValue); + }, + function (error) { + popActScope(prevActQueue, prevActScopeDepth); + 0 < ReactSharedInternals.thrownErrors.length + ? ((error = aggregateErrors( + ReactSharedInternals.thrownErrors + )), + (ReactSharedInternals.thrownErrors.length = 0), + reject(error)) + : reject(error); + } + ); + } + }; + } + var returnValue$jscomp$0 = result; + popActScope(prevActQueue, prevActScopeDepth); + 0 === prevActScopeDepth && + (flushActQueue(queue), + 0 !== queue.length && + queueSeveralMicrotasks(function () { + didAwaitActCall || + didWarnNoAwaitAct || + ((didWarnNoAwaitAct = !0), + console.error( + "A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)" + )); + }), + (ReactSharedInternals.actQueue = null)); + if (0 < ReactSharedInternals.thrownErrors.length) + throw ( + ((callback = aggregateErrors(ReactSharedInternals.thrownErrors)), + (ReactSharedInternals.thrownErrors.length = 0), + callback) + ); + return { + then: function (resolve, reject) { + didAwaitActCall = !0; + 0 === prevActScopeDepth + ? ((ReactSharedInternals.actQueue = queue), + enqueueTask(function () { + return recursivelyFlushAsyncActWork( + returnValue$jscomp$0, + resolve, + reject + ); + })) + : resolve(returnValue$jscomp$0); + } + }; + }; + exports.cache = function (fn) { + return function () { + return fn.apply(null, arguments); + }; + }; + exports.captureOwnerStack = function () { + var getCurrentStack = ReactSharedInternals.getCurrentStack; + return null === getCurrentStack ? null : getCurrentStack(); + }; + exports.cloneElement = function (element, config, children) { + if (null === element || void 0 === element) + throw Error( + "The argument must be a React element, but you passed " + + element + + "." + ); + var props = assign({}, element.props), + key = element.key, + owner = element._owner; + if (null != config) { + var JSCompiler_inline_result; + a: { + if ( + hasOwnProperty.call(config, "ref") && + (JSCompiler_inline_result = Object.getOwnPropertyDescriptor( + config, + "ref" + ).get) && + JSCompiler_inline_result.isReactWarning + ) { + JSCompiler_inline_result = !1; + break a; + } + JSCompiler_inline_result = void 0 !== config.ref; + } + JSCompiler_inline_result && (owner = getOwner()); + hasValidKey(config) && + (checkKeyStringCoercion(config.key), (key = "" + config.key)); + for (propName in config) + !hasOwnProperty.call(config, propName) || + "key" === propName || + "__self" === propName || + "__source" === propName || + ("ref" === propName && void 0 === config.ref) || + (props[propName] = config[propName]); + } + var propName = arguments.length - 2; + if (1 === propName) props.children = children; + else if (1 < propName) { + JSCompiler_inline_result = Array(propName); + for (var i = 0; i < propName; i++) + JSCompiler_inline_result[i] = arguments[i + 2]; + props.children = JSCompiler_inline_result; + } + props = ReactElement( + element.type, + key, + void 0, + void 0, + owner, + props, + element._debugStack, + element._debugTask + ); + for (key = 2; key < arguments.length; key++) + (owner = arguments[key]), + isValidElement(owner) && owner._store && (owner._store.validated = 1); + return props; + }; + exports.createContext = function (defaultValue) { + defaultValue = { + $$typeof: REACT_CONTEXT_TYPE, + _currentValue: defaultValue, + _currentValue2: defaultValue, + _threadCount: 0, + Provider: null, + Consumer: null + }; + defaultValue.Provider = defaultValue; + defaultValue.Consumer = { + $$typeof: REACT_CONSUMER_TYPE, + _context: defaultValue + }; + defaultValue._currentRenderer = null; + defaultValue._currentRenderer2 = null; + return defaultValue; + }; + exports.createElement = function (type, config, children) { + for (var i = 2; i < arguments.length; i++) { + var node = arguments[i]; + isValidElement(node) && node._store && (node._store.validated = 1); + } + i = {}; + node = null; + if (null != config) + for (propName in (didWarnAboutOldJSXRuntime || + !("__self" in config) || + "key" in config || + ((didWarnAboutOldJSXRuntime = !0), + console.warn( + "Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform" + )), + hasValidKey(config) && + (checkKeyStringCoercion(config.key), (node = "" + config.key)), + config)) + hasOwnProperty.call(config, propName) && + "key" !== propName && + "__self" !== propName && + "__source" !== propName && + (i[propName] = config[propName]); + var childrenLength = arguments.length - 2; + if (1 === childrenLength) i.children = children; + else if (1 < childrenLength) { + for ( + var childArray = Array(childrenLength), _i = 0; + _i < childrenLength; + _i++ + ) + childArray[_i] = arguments[_i + 2]; + Object.freeze && Object.freeze(childArray); + i.children = childArray; + } + if (type && type.defaultProps) + for (propName in ((childrenLength = type.defaultProps), childrenLength)) + void 0 === i[propName] && (i[propName] = childrenLength[propName]); + node && + defineKeyPropWarningGetter( + i, + "function" === typeof type + ? type.displayName || type.name || "Unknown" + : type + ); + var propName = 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; + return ReactElement( + type, + node, + void 0, + void 0, + getOwner(), + i, + propName ? Error("react-stack-top-frame") : unknownOwnerDebugStack, + propName ? createTask(getTaskName(type)) : unknownOwnerDebugTask + ); + }; + exports.createRef = function () { + var refObject = { current: null }; + Object.seal(refObject); + return refObject; + }; + exports.forwardRef = function (render) { + null != render && render.$$typeof === REACT_MEMO_TYPE + ? console.error( + "forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))." + ) + : "function" !== typeof render + ? console.error( + "forwardRef requires a render function but was given %s.", + null === render ? "null" : typeof render + ) + : 0 !== render.length && + 2 !== render.length && + console.error( + "forwardRef render functions accept exactly two parameters: props and ref. %s", + 1 === render.length + ? "Did you forget to use the ref parameter?" + : "Any additional parameter will be undefined." + ); + null != render && + null != render.defaultProps && + console.error( + "forwardRef render functions do not support defaultProps. Did you accidentally pass a React component?" + ); + var elementType = { $$typeof: REACT_FORWARD_REF_TYPE, render: render }, + ownName; + Object.defineProperty(elementType, "displayName", { + enumerable: !1, + configurable: !0, + get: function () { + return ownName; + }, + set: function (name) { + ownName = name; + render.name || + render.displayName || + (Object.defineProperty(render, "name", { value: name }), + (render.displayName = name)); + } + }); + return elementType; + }; + exports.isValidElement = isValidElement; + exports.lazy = function (ctor) { + return { + $$typeof: REACT_LAZY_TYPE, + _payload: { _status: -1, _result: ctor }, + _init: lazyInitializer + }; + }; + exports.memo = function (type, compare) { + null == type && + console.error( + "memo: The first argument must be a component. Instead received: %s", + null === type ? "null" : typeof type + ); + compare = { + $$typeof: REACT_MEMO_TYPE, + type: type, + compare: void 0 === compare ? null : compare + }; + var ownName; + Object.defineProperty(compare, "displayName", { + enumerable: !1, + configurable: !0, + get: function () { + return ownName; + }, + set: function (name) { + ownName = name; + type.name || + type.displayName || + (Object.defineProperty(type, "name", { value: name }), + (type.displayName = name)); + } + }); + return compare; + }; + exports.startTransition = function (scope) { + var prevTransition = ReactSharedInternals.T, + currentTransition = {}; + ReactSharedInternals.T = currentTransition; + currentTransition._updatedFibers = new Set(); + try { + var returnValue = scope(), + onStartTransitionFinish = ReactSharedInternals.S; + null !== onStartTransitionFinish && + onStartTransitionFinish(currentTransition, returnValue); + "object" === typeof returnValue && + null !== returnValue && + "function" === typeof returnValue.then && + returnValue.then(noop, reportGlobalError); + } catch (error) { + reportGlobalError(error); + } finally { + null === prevTransition && + currentTransition._updatedFibers && + ((scope = currentTransition._updatedFibers.size), + currentTransition._updatedFibers.clear(), + 10 < scope && + console.warn( + "Detected a large number of updates inside startTransition. If this is due to a subscription please re-write it to use React provided hooks. Otherwise concurrent mode guarantees are off the table." + )), + (ReactSharedInternals.T = prevTransition); + } + }; + exports.unstable_useCacheRefresh = function () { + return resolveDispatcher().useCacheRefresh(); + }; + exports.use = function (usable) { + return resolveDispatcher().use(usable); + }; + exports.useActionState = function (action, initialState, permalink) { + return resolveDispatcher().useActionState( + action, + initialState, + permalink + ); + }; + exports.useCallback = function (callback, deps) { + return resolveDispatcher().useCallback(callback, deps); + }; + exports.useContext = function (Context) { + var dispatcher = resolveDispatcher(); + Context.$$typeof === REACT_CONSUMER_TYPE && + console.error( + "Calling useContext(Context.Consumer) is not supported and will cause bugs. Did you mean to call useContext(Context) instead?" + ); + return dispatcher.useContext(Context); + }; + exports.useDebugValue = function (value, formatterFn) { + return resolveDispatcher().useDebugValue(value, formatterFn); + }; + exports.useDeferredValue = function (value, initialValue) { + return resolveDispatcher().useDeferredValue(value, initialValue); + }; + exports.useEffect = function (create, createDeps, update) { + null == create && + console.warn( + "React Hook useEffect requires an effect callback. Did you forget to pass a callback to the hook?" + ); + var dispatcher = resolveDispatcher(); + if ("function" === typeof update) + throw Error( + "useEffect CRUD overload is not enabled in this build of React." + ); + return dispatcher.useEffect(create, createDeps); + }; + exports.useId = function () { + return resolveDispatcher().useId(); + }; + exports.useImperativeHandle = function (ref, create, deps) { + return resolveDispatcher().useImperativeHandle(ref, create, deps); + }; + exports.useInsertionEffect = function (create, deps) { + null == create && + console.warn( + "React Hook useInsertionEffect requires an effect callback. Did you forget to pass a callback to the hook?" + ); + return resolveDispatcher().useInsertionEffect(create, deps); + }; + exports.useLayoutEffect = function (create, deps) { + null == create && + console.warn( + "React Hook useLayoutEffect requires an effect callback. Did you forget to pass a callback to the hook?" + ); + return resolveDispatcher().useLayoutEffect(create, deps); + }; + exports.useMemo = function (create, deps) { + return resolveDispatcher().useMemo(create, deps); + }; + exports.useOptimistic = function (passthrough, reducer) { + return resolveDispatcher().useOptimistic(passthrough, reducer); + }; + exports.useReducer = function (reducer, initialArg, init) { + return resolveDispatcher().useReducer(reducer, initialArg, init); + }; + exports.useRef = function (initialValue) { + return resolveDispatcher().useRef(initialValue); + }; + exports.useState = function (initialState) { + return resolveDispatcher().useState(initialState); + }; + exports.useSyncExternalStore = function ( + subscribe, + getSnapshot, + getServerSnapshot + ) { + return resolveDispatcher().useSyncExternalStore( + subscribe, + getSnapshot, + getServerSnapshot + ); + }; + exports.useTransition = function () { + return resolveDispatcher().useTransition(); + }; + exports.version = "19.1.0"; + "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && + "function" === + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && + __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error()); + })(); +} (react_development, react_developmentExports)); + return react_developmentExports; +} - error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); - } - }; +(function (module) { - warnAboutAccessingRef.isReactWarning = true; - Object.defineProperty(props, 'ref', { - get: warnAboutAccessingRef, - configurable: true - }); - } + if (process.env.NODE_ENV === 'production') { + module.exports = requireReact_production(); + } else { + module.exports = requireReact_development(); } - /** - * Factory method to create a new React element. This no longer adheres to - * the class pattern, so do not use new to call it. Also, instanceof check - * will not work. Instead test $$typeof field against Symbol.for('react.element') to check - * if something is a React Element. - * - * @param {*} type - * @param {*} props - * @param {*} key - * @param {string|object} ref - * @param {*} owner - * @param {*} self A *temporary* helper to detect places where `this` is - * different from the `owner` when React.createElement is called, so that we - * can warn. We want to get rid of owner and replace string `ref`s with arrow - * functions, and as long as `this` and owner are the same, there will be no - * change in behavior. - * @param {*} source An annotation object (added by a transpiler or otherwise) - * indicating filename, line number, and/or other information. - * @internal - */ - - - var ReactElement = function (type, key, ref, self, source, owner, props) { - var element = { - // This tag allows us to uniquely identify this as a React Element - $$typeof: REACT_ELEMENT_TYPE, - // Built-in properties that belong on the element - type: type, - key: key, - ref: ref, - props: props, - // Record the component responsible for creating this element. - _owner: owner - }; - - { - // The validation flag is currently mutative. We put it on - // an external backing store so that we can freeze the whole object. - // This can be replaced with a WeakMap once they are implemented in - // commonly used development environments. - element._store = {}; // To make comparing ReactElements easier for testing purposes, we make - // the validation flag non-enumerable (where possible, which should - // include every environment we run tests in), so the test framework - // ignores it. - - Object.defineProperty(element._store, 'validated', { - configurable: false, - enumerable: false, - writable: true, - value: false - }); // self and source are DEV only properties. - - Object.defineProperty(element, '_self', { - configurable: false, - enumerable: false, - writable: false, - value: self - }); // Two elements created in two different places should be considered - // equal for testing purposes and therefore we hide it from enumeration. - - Object.defineProperty(element, '_source', { - configurable: false, - enumerable: false, - writable: false, - value: source - }); - - if (Object.freeze) { - Object.freeze(element.props); - Object.freeze(element); - } - } - - return element; - }; - /** - * https://github.com/reactjs/rfcs/pull/107 - * @param {*} type - * @param {object} props - * @param {string} key - */ - - function jsxDEV(type, config, maybeKey, source, self) { - { - var propName; // Reserved names are extracted - - var props = {}; - var key = null; - var ref = null; // Currently, key can be spread in as a prop. This causes a potential - // issue if key is also explicitly declared (ie. - // or ). We want to deprecate key spread, - // but as an intermediary step, we will use jsxDEV for everything except - // , because we aren't currently able to tell if - // key is explicitly declared to be undefined or not. - - if (maybeKey !== undefined) { - { - checkKeyStringCoercion(maybeKey); - } - - key = '' + maybeKey; - } - - if (hasValidKey(config)) { - { - checkKeyStringCoercion(config.key); - } - - key = '' + config.key; - } +} (react)); - if (hasValidRef(config)) { - ref = config.ref; - warnIfStringRefCannotBeAutoConverted(config, self); - } // Remaining properties are added to a new props object +/** + * @license React + * react-jsx-runtime.development.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +var hasRequiredReactJsxRuntime_development; - for (propName in config) { - if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { - props[propName] = config[propName]; +function requireReactJsxRuntime_development () { + if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development; + hasRequiredReactJsxRuntime_development = 1; + "production" !== process.env.NODE_ENV && + (function () { + function getComponentNameFromType(type) { + if (null == type) return null; + if ("function" === typeof type) + return type.$$typeof === REACT_CLIENT_REFERENCE + ? null + : type.displayName || type.name || null; + if ("string" === typeof type) return type; + switch (type) { + case REACT_FRAGMENT_TYPE: + return "Fragment"; + case REACT_PROFILER_TYPE: + return "Profiler"; + case REACT_STRICT_MODE_TYPE: + return "StrictMode"; + case REACT_SUSPENSE_TYPE: + return "Suspense"; + case REACT_SUSPENSE_LIST_TYPE: + return "SuspenseList"; + case REACT_ACTIVITY_TYPE: + return "Activity"; } - } // Resolve default props - - - if (type && type.defaultProps) { - var defaultProps = type.defaultProps; - - for (propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; + if ("object" === typeof type) + switch ( + ("number" === typeof type.tag && + console.error( + "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue." + ), + type.$$typeof) + ) { + case REACT_PORTAL_TYPE: + return "Portal"; + case REACT_CONTEXT_TYPE: + return (type.displayName || "Context") + ".Provider"; + case REACT_CONSUMER_TYPE: + return (type._context.displayName || "Context") + ".Consumer"; + case REACT_FORWARD_REF_TYPE: + var innerType = type.render; + type = type.displayName; + type || + ((type = innerType.displayName || innerType.name || ""), + (type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef")); + return type; + case REACT_MEMO_TYPE: + return ( + (innerType = type.displayName || null), + null !== innerType + ? innerType + : getComponentNameFromType(type.type) || "Memo" + ); + case REACT_LAZY_TYPE: + innerType = type._payload; + type = type._init; + try { + return getComponentNameFromType(type(innerType)); + } catch (x) {} } - } - } - - if (key || ref) { - var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; - - if (key) { - defineKeyPropWarningGetter(props, displayName); - } - - if (ref) { - defineRefPropWarningGetter(props, displayName); - } + return null; } - - return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); - } - } - - var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; - var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; - - function setCurrentlyValidatingElement$1(element) { - { - if (element) { - var owner = element._owner; - var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); - ReactDebugCurrentFrame$1.setExtraStackFrame(stack); - } else { - ReactDebugCurrentFrame$1.setExtraStackFrame(null); + function testStringCoercion(value) { + return "" + value; } - } - } - - var propTypesMisspellWarningShown; - - { - propTypesMisspellWarningShown = false; - } - /** - * Verifies the object is a ReactElement. - * See https://reactjs.org/docs/react-api.html#isvalidelement - * @param {?object} object - * @return {boolean} True if `object` is a ReactElement. - * @final - */ - - - function isValidElement(object) { - { - return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; - } - } - - function getDeclarationErrorAddendum() { - { - if (ReactCurrentOwner$1.current) { - var name = getComponentNameFromType(ReactCurrentOwner$1.current.type); - - if (name) { - return '\n\nCheck the render method of `' + name + '`.'; + function checkKeyStringCoercion(value) { + try { + testStringCoercion(value); + var JSCompiler_inline_result = !1; + } catch (e) { + JSCompiler_inline_result = !0; } - } - - return ''; - } - } - - function getSourceInfoErrorAddendum(source) { - { - if (source !== undefined) { - var fileName = source.fileName.replace(/^.*[\\\/]/, ''); - var lineNumber = source.lineNumber; - return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.'; - } - - return ''; - } - } - /** - * Warn if there's no key explicitly set on dynamic arrays of children or - * object keys are not valid. This allows us to keep track of children between - * updates. - */ - - - var ownerHasKeyUseWarning = {}; - - function getCurrentComponentErrorInfo(parentType) { - { - var info = getDeclarationErrorAddendum(); - - if (!info) { - var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name; - - if (parentName) { - info = "\n\nCheck the top-level render call using <" + parentName + ">."; + if (JSCompiler_inline_result) { + JSCompiler_inline_result = console; + var JSCompiler_temp_const = JSCompiler_inline_result.error; + var JSCompiler_inline_result$jscomp$0 = + ("function" === typeof Symbol && + Symbol.toStringTag && + value[Symbol.toStringTag]) || + value.constructor.name || + "Object"; + JSCompiler_temp_const.call( + JSCompiler_inline_result, + "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", + JSCompiler_inline_result$jscomp$0 + ); + return testStringCoercion(value); } } - - return info; - } - } - /** - * Warn if the element doesn't have an explicit key assigned to it. - * This element is in an array. The array could grow and shrink or be - * reordered. All children that haven't already been validated are required to - * have a "key" property assigned to it. Error statuses are cached so a warning - * will only be shown once. - * - * @internal - * @param {ReactElement} element Element that requires a key. - * @param {*} parentType element's parent's type. - */ - - - function validateExplicitKey(element, parentType) { - { - if (!element._store || element._store.validated || element.key != null) { - return; - } - - element._store.validated = true; - var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); - - if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { - return; - } - - ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a - // property, it may be the creator of the child that's responsible for - // assigning it a key. - - var childOwner = ''; - - if (element && element._owner && element._owner !== ReactCurrentOwner$1.current) { - // Give the component that originally created this child. - childOwner = " It was passed a child from " + getComponentNameFromType(element._owner.type) + "."; - } - - setCurrentlyValidatingElement$1(element); - - error('Each child in a list should have a unique "key" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner); - - setCurrentlyValidatingElement$1(null); - } - } - /** - * Ensure that every element either is passed in a static location, in an - * array with an explicit keys property defined, or in an object literal - * with valid key property. - * - * @internal - * @param {ReactNode} node Statically passed child of any type. - * @param {*} parentType node's parent's type. - */ - - - function validateChildKeys(node, parentType) { - { - if (typeof node !== 'object') { - return; - } - - if (isArray(node)) { - for (var i = 0; i < node.length; i++) { - var child = node[i]; - - if (isValidElement(child)) { - validateExplicitKey(child, parentType); - } - } - } else if (isValidElement(node)) { - // This element was passed in a valid location. - if (node._store) { - node._store.validated = true; - } - } else if (node) { - var iteratorFn = getIteratorFn(node); - - if (typeof iteratorFn === 'function') { - // Entry iterators used to provide implicit keys, - // but now we print a separate warning for them later. - if (iteratorFn !== node.entries) { - var iterator = iteratorFn.call(node); - var step; - - while (!(step = iterator.next()).done) { - if (isValidElement(step.value)) { - validateExplicitKey(step.value, parentType); - } - } - } + function getTaskName(type) { + if (type === REACT_FRAGMENT_TYPE) return "<>"; + if ( + "object" === typeof type && + null !== type && + type.$$typeof === REACT_LAZY_TYPE + ) + return "<...>"; + try { + var name = getComponentNameFromType(type); + return name ? "<" + name + ">" : "<...>"; + } catch (x) { + return "<...>"; } } - } - } - /** - * Given an element, validate that its props follow the propTypes definition, - * provided by the type. - * - * @param {ReactElement} element - */ - - - function validatePropTypes(element) { - { - var type = element.type; - - if (type === null || type === undefined || typeof type === 'string') { - return; - } - - var propTypes; - - if (typeof type === 'function') { - propTypes = type.propTypes; - } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here. - // Inner props are checked in the reconciler. - type.$$typeof === REACT_MEMO_TYPE)) { - propTypes = type.propTypes; - } else { - return; - } - - if (propTypes) { - // Intentionally inside to avoid triggering lazy initializers: - var name = getComponentNameFromType(type); - checkPropTypes(propTypes, element.props, 'prop', name, element); - } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) { - propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers: - - var _name = getComponentNameFromType(type); - - error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown'); + function getOwner() { + var dispatcher = ReactSharedInternals.A; + return null === dispatcher ? null : dispatcher.getOwner(); } - - if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) { - error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.'); + function UnknownOwner() { + return Error("react-stack-top-frame"); } - } - } - /** - * Given a fragment, validate that it can only be provided with fragment props - * @param {ReactElement} fragment - */ - - - function validateFragmentProps(fragment) { - { - var keys = Object.keys(fragment.props); - - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - - if (key !== 'children' && key !== 'key') { - setCurrentlyValidatingElement$1(fragment); - - error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key); - - setCurrentlyValidatingElement$1(null); - break; + function hasValidKey(config) { + if (hasOwnProperty.call(config, "key")) { + var getter = Object.getOwnPropertyDescriptor(config, "key").get; + if (getter && getter.isReactWarning) return !1; } + return void 0 !== config.key; } - - if (fragment.ref !== null) { - setCurrentlyValidatingElement$1(fragment); - - error('Invalid attribute `ref` supplied to `React.Fragment`.'); - - setCurrentlyValidatingElement$1(null); - } - } - } - - var didWarnAboutKeySpread = {}; - function jsxWithValidation(type, props, key, isStaticChildren, source, self) { - { - var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to - // succeed and there will likely be errors in render. - - if (!validType) { - var info = ''; - - if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { - info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; - } - - var sourceInfo = getSourceInfoErrorAddendum(source); - - if (sourceInfo) { - info += sourceInfo; - } else { - info += getDeclarationErrorAddendum(); + function defineKeyPropWarningGetter(props, displayName) { + function warnAboutAccessingKey() { + specialPropKeyWarningShown || + ((specialPropKeyWarningShown = !0), + console.error( + "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", + displayName + )); } - - var typeString; - - if (type === null) { - typeString = 'null'; - } else if (isArray(type)) { - typeString = 'array'; - } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) { - typeString = "<" + (getComponentNameFromType(type.type) || 'Unknown') + " />"; - info = ' Did you accidentally export a JSX literal instead of a component?'; - } else { - typeString = typeof type; - } - - error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info); + warnAboutAccessingKey.isReactWarning = !0; + Object.defineProperty(props, "key", { + get: warnAboutAccessingKey, + configurable: !0 + }); } - - var element = jsxDEV(type, props, key, source, self); // The result can be nullish if a mock or a custom function is used. - // TODO: Drop this when these are no longer allowed as the type argument. - - if (element == null) { - return element; - } // Skip key warning if the type isn't valid since our key validation logic - // doesn't expect a non-string/function type and can throw confusing errors. - // We don't want exception behavior to differ between dev and prod. - // (Rendering will throw with a helpful message and as soon as the type is - // fixed, the key warnings will appear.) - - - if (validType) { - var children = props.children; - - if (children !== undefined) { - if (isStaticChildren) { - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - validateChildKeys(children[i], type); - } - - if (Object.freeze) { - Object.freeze(children); - } - } else { - error('React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.'); - } - } else { - validateChildKeys(children, type); - } - } + function elementRefGetterWithDeprecationWarning() { + var componentName = getComponentNameFromType(this.type); + didWarnAboutElementRef[componentName] || + ((didWarnAboutElementRef[componentName] = !0), + console.error( + "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release." + )); + componentName = this.props.ref; + return void 0 !== componentName ? componentName : null; } - - { - if (hasOwnProperty.call(props, 'key')) { - var componentName = getComponentNameFromType(type); - var keys = Object.keys(props).filter(function (k) { - return k !== 'key'; + function ReactElement( + type, + key, + self, + source, + owner, + props, + debugStack, + debugTask + ) { + self = props.ref; + type = { + $$typeof: REACT_ELEMENT_TYPE, + type: type, + key: key, + props: props, + _owner: owner + }; + null !== (void 0 !== self ? self : null) + ? Object.defineProperty(type, "ref", { + enumerable: !1, + get: elementRefGetterWithDeprecationWarning + }) + : Object.defineProperty(type, "ref", { enumerable: !1, value: null }); + type._store = {}; + Object.defineProperty(type._store, "validated", { + configurable: !1, + enumerable: !1, + writable: !0, + value: 0 + }); + Object.defineProperty(type, "_debugInfo", { + configurable: !1, + enumerable: !1, + writable: !0, + value: null + }); + Object.defineProperty(type, "_debugStack", { + configurable: !1, + enumerable: !1, + writable: !0, + value: debugStack + }); + Object.defineProperty(type, "_debugTask", { + configurable: !1, + enumerable: !1, + writable: !0, + value: debugTask + }); + Object.freeze && (Object.freeze(type.props), Object.freeze(type)); + return type; + } + function jsxDEVImpl( + type, + config, + maybeKey, + isStaticChildren, + source, + self, + debugStack, + debugTask + ) { + var children = config.children; + if (void 0 !== children) + if (isStaticChildren) + if (isArrayImpl(children)) { + for ( + isStaticChildren = 0; + isStaticChildren < children.length; + isStaticChildren++ + ) + validateChildKeys(children[isStaticChildren]); + Object.freeze && Object.freeze(children); + } else + console.error( + "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead." + ); + else validateChildKeys(children); + if (hasOwnProperty.call(config, "key")) { + children = getComponentNameFromType(type); + var keys = Object.keys(config).filter(function (k) { + return "key" !== k; }); - var beforeExample = keys.length > 0 ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}' : '{key: someKey}'; - - if (!didWarnAboutKeySpread[componentName + beforeExample]) { - var afterExample = keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}'; - - error('A props object containing a "key" prop is being spread into JSX:\n' + ' let props = %s;\n' + ' <%s {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = %s;\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName); - - didWarnAboutKeySpread[componentName + beforeExample] = true; - } + isStaticChildren = + 0 < keys.length + ? "{key: someKey, " + keys.join(": ..., ") + ": ...}" + : "{key: someKey}"; + didWarnAboutKeySpread[children + isStaticChildren] || + ((keys = + 0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}"), + console.error( + 'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />', + isStaticChildren, + children, + keys, + children + ), + (didWarnAboutKeySpread[children + isStaticChildren] = !0)); } + children = null; + void 0 !== maybeKey && + (checkKeyStringCoercion(maybeKey), (children = "" + maybeKey)); + hasValidKey(config) && + (checkKeyStringCoercion(config.key), (children = "" + config.key)); + if ("key" in config) { + maybeKey = {}; + for (var propName in config) + "key" !== propName && (maybeKey[propName] = config[propName]); + } else maybeKey = config; + children && + defineKeyPropWarningGetter( + maybeKey, + "function" === typeof type + ? type.displayName || type.name || "Unknown" + : type + ); + return ReactElement( + type, + children, + self, + source, + getOwner(), + maybeKey, + debugStack, + debugTask + ); } - - if (type === REACT_FRAGMENT_TYPE) { - validateFragmentProps(element); - } else { - validatePropTypes(element); + function validateChildKeys(node) { + "object" === typeof node && + null !== node && + node.$$typeof === REACT_ELEMENT_TYPE && + node._store && + (node._store.validated = 1); } - - return element; - } - } // These two functions exist to still get child warnings in dev - // even with the prod transform. This means that jsxDEV is purely - // opt-in behavior for better messages but that we won't stop - // giving you warnings if you use production apis. - - function jsxWithValidationStatic(type, props, key) { - { - return jsxWithValidation(type, props, key, true); - } - } - function jsxWithValidationDynamic(type, props, key) { - { - return jsxWithValidation(type, props, key, false); - } - } - - var jsx = jsxWithValidationDynamic ; // we may want to special case jsxs internally to take advantage of static children. - // for now we can ship identical prod functions - - var jsxs = jsxWithValidationStatic ; - - reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE; - reactJsxRuntime_development.jsx = jsx; - reactJsxRuntime_development.jsxs = jsxs; + var React = reactExports, + REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), + REACT_PORTAL_TYPE = Symbol.for("react.portal"), + REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), + REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"), + REACT_PROFILER_TYPE = Symbol.for("react.profiler"); + var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"), + REACT_CONTEXT_TYPE = Symbol.for("react.context"), + REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"), + REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"), + REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"), + REACT_MEMO_TYPE = Symbol.for("react.memo"), + REACT_LAZY_TYPE = Symbol.for("react.lazy"), + REACT_ACTIVITY_TYPE = Symbol.for("react.activity"), + REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"), + ReactSharedInternals = + React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + hasOwnProperty = Object.prototype.hasOwnProperty, + isArrayImpl = Array.isArray, + createTask = console.createTask + ? console.createTask + : function () { + return null; + }; + React = { + "react-stack-bottom-frame": function (callStackForError) { + return callStackForError(); + } + }; + var specialPropKeyWarningShown; + var didWarnAboutElementRef = {}; + var unknownOwnerDebugStack = React["react-stack-bottom-frame"].bind( + React, + UnknownOwner + )(); + var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner)); + var didWarnAboutKeySpread = {}; + reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE; + reactJsxRuntime_development.jsx = function (type, config, maybeKey, source, self) { + var trackActualOwner = + 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; + return jsxDEVImpl( + type, + config, + maybeKey, + !1, + source, + self, + trackActualOwner + ? Error("react-stack-top-frame") + : unknownOwnerDebugStack, + trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask + ); + }; + reactJsxRuntime_development.jsxs = function (type, config, maybeKey, source, self) { + var trackActualOwner = + 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++; + return jsxDEVImpl( + type, + config, + maybeKey, + !0, + source, + self, + trackActualOwner + ? Error("react-stack-top-frame") + : unknownOwnerDebugStack, + trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask + ); + }; })(); - } return reactJsxRuntime_development; } (function (module) { if (process.env.NODE_ENV === 'production') { - module.exports = requireReactJsxRuntime_production_min(); + module.exports = requireReactJsxRuntime_production(); } else { module.exports = requireReactJsxRuntime_development(); } } (jsxRuntime)); -var en = {}; +const en = {}; -var zh = {}; +const zh = {}; -var de = {}; +const de = {}; -var pt = {}; +const pt = {}; // file examples: en, enGB, zh, zhHK @@ -12034,48 +10008,50 @@ var IntlMessageFormat = IntlMessageFormat$1; // this is a copy of the translator from ../../lib/index.js // TODO: check if this file is used at all -var defaultLocale = "en"; -var locales = [defaultLocale]; +const defaultLocale = "en"; +let locales = [defaultLocale]; // Falk - Adapted the central translator to check if a localStorage key is existing. -var uiLanguage = localStorage.getItem('lowcoder_uiLanguage'); +const uiLanguage = localStorage.getItem('lowcoder_uiLanguage'); if (globalThis.navigator) { if (uiLanguage) { locales = [uiLanguage]; } else if (navigator.languages && navigator.languages.length > 0) { - locales = __spreadArray([], navigator.languages, true); + locales = [...navigator.languages]; } else { locales = [navigator.language || navigator.userLanguage || defaultLocale]; } } function parseLocale(s) { - var locale = s.trim(); + const locale = s.trim(); if (!locale) { return; } try { if (Intl.Locale) { - var _a = new Intl.Locale(locale), language = _a.language, region = _a.region; - return { locale: locale, language: language, region: region }; + const { language, region } = new Intl.Locale(locale); + return { locale, language, region }; } - var parts = locale.split("-"); - var r = parts.slice(1, 3).find(function (t) { return t.length === 2; }); - return { locale: locale, language: parts[0].toLowerCase(), region: r === null || r === void 0 ? void 0 : r.toUpperCase() }; + const parts = locale.split("-"); + const r = parts.slice(1, 3).find((t) => t.length === 2); + return { locale, language: parts[0].toLowerCase(), region: r?.toUpperCase() }; } catch (e) { - log.error("Parse locale:".concat(locale, " failed."), e); + log.error(`Parse locale:${locale} failed.`, e); } } function parseLocales(list) { - return list.map(parseLocale).filter(function (t) { return t; }); + return list.map(parseLocale).filter((t) => t); } -var fallbackLocaleInfos = parseLocales(locales.includes(defaultLocale) ? locales : __spreadArray(__spreadArray([], locales, true), [defaultLocale], false)); -var i18n = __assign({ locales: locales }, fallbackLocaleInfos[0]); +const fallbackLocaleInfos = parseLocales(locales.includes(defaultLocale) ? locales : [...locales, defaultLocale]); +const i18n = { + locales, + ...fallbackLocaleInfos[0], +}; function getValueByLocale(defaultValue, func) { - for (var _i = 0, fallbackLocaleInfos_1 = fallbackLocaleInfos; _i < fallbackLocaleInfos_1.length; _i++) { - var info = fallbackLocaleInfos_1[_i]; - var t = func(info); + for (const info of fallbackLocaleInfos) { + const t = func(info); if (t !== undefined) { return t; } @@ -12083,90 +10059,86 @@ function getValueByLocale(defaultValue, func) { return defaultValue; } function getDataByLocale(fileData, suffix, filterLocales, targetLocales) { - var localeInfos = __spreadArray([], fallbackLocaleInfos, true); - var targetLocaleInfo = parseLocales(targetLocales || []); + let localeInfos = [...fallbackLocaleInfos]; + const targetLocaleInfo = parseLocales(targetLocales || []); if (targetLocaleInfo.length > 0) { - localeInfos = __spreadArray(__spreadArray([], targetLocaleInfo, true), localeInfos, true); - } - var filterNames = parseLocales((filterLocales !== null && filterLocales !== void 0 ? filterLocales : "").split(",")) - .map(function (l) { var _a; return l.language + ((_a = l.region) !== null && _a !== void 0 ? _a : ""); }) - .filter(function (s) { return fileData[s + suffix] !== undefined; }); - var names = __spreadArray(__spreadArray([], localeInfos - .flatMap(function (_a) { - var language = _a.language, region = _a.region; - return [ + localeInfos = [...targetLocaleInfo, ...localeInfos]; + } + const filterNames = parseLocales((filterLocales ?? "").split(",")) + .map((l) => l.language + (l.region ?? "")) + .filter((s) => fileData[s + suffix] !== undefined); + const names = [ + ...localeInfos + .flatMap(({ language, region }) => [ region ? language + region : undefined, language, - filterNames.find(function (n) { return n.startsWith(language); }), - ]; - }) - .filter(function (s) { return s && (!filterLocales || filterNames.includes(s)); }), true), filterNames, true).map(function (s) { return s + suffix; }); - for (var _i = 0, names_1 = names; _i < names_1.length; _i++) { - var name_1 = names_1[_i]; - var data = fileData[name_1]; + filterNames.find((n) => n.startsWith(language)), + ]) + .filter((s) => s && (!filterLocales || filterNames.includes(s))), + ...filterNames, + ].map((s) => s + suffix); + for (const name of names) { + const data = fileData[name]; if (data !== undefined) { - return { data: data, language: name_1.slice(0, 2) }; + return { data: data, language: name.slice(0, 2) }; } } - console.error("Not found ".concat(names)); + console.error(`Not found ${names}`); // return fallback data for en language return { data: fileData['en'], language: 'en' }; // throw new Error(`Not found ${names}`); } -var globalMessageKeyPrefix = "@"; -var globalMessages = Object.fromEntries(Object.entries(getDataByLocale(localeData, "").data).map(function (_a) { - var k = _a[0], v = _a[1]; - return [ - globalMessageKeyPrefix + k, - v, - ]; -})); -var Translator = /** @class */ (function () { - function Translator(fileData, filterLocales, locales) { - var _a = getDataByLocale(fileData, "", filterLocales, locales), data = _a.data, language = _a.language; +const globalMessageKeyPrefix = "@"; +const globalMessages = Object.fromEntries(Object.entries(getDataByLocale(localeData, "").data).map(([k, v]) => [ + globalMessageKeyPrefix + k, + v, +])); +class Translator { + messages; + // language of Translator, can be different from i18n.language + language; + constructor(fileData, filterLocales, locales) { + const { data, language } = getDataByLocale(fileData, "", filterLocales, locales); this.messages = Object.assign({}, data, globalMessages); this.language = language; this.trans = this.trans.bind(this); this.transToNode = this.transToNode.bind(this); } - Translator.prototype.trans = function (key, variables) { + trans(key, variables) { return this.transToNode(key, variables).toString(); - }; - Translator.prototype.transToNode = function (key, variables) { - var message = this.getMessage(key); - var node = new IntlMessageFormat(message, i18n.locale).format(variables); + } + transToNode(key, variables) { + const message = this.getMessage(key); + const node = new IntlMessageFormat(message, i18n.locale).format(variables); if (Array.isArray(node)) { - return node.map(function (n, i) { return jsxRuntimeExports.jsx(reactExports.Fragment, { children: n }, i); }); + return node.map((n, i) => jsxRuntimeExports.jsx(reactExports.Fragment, { children: n }, i)); } return node; - }; - Translator.prototype.getMessage = function (key) { - var message = this.getNestedMessage(this.messages, key); + } + getMessage(key) { + let message = this.getNestedMessage(this.messages, key); // Fallback to English if the message is not found if (message === undefined) { message = this.getNestedMessage(en, key); // Assuming localeData.en contains English translations } // If still not found, return a default message or the key itself if (message === undefined) { - console.warn("Translation missing for key: ".concat(key)); - message = "oups! ".concat(key); + console.warn(`Translation missing for key: ${key}`); + message = `oups! ${key}`; } return message; - }; - Translator.prototype.getNestedMessage = function (obj, key) { - for (var _i = 0, _a = key.split("."); _i < _a.length; _i++) { - var k = _a[_i]; + } + getNestedMessage(obj, key) { + for (const k of key.split(".")) { if (obj !== undefined) { obj = obj[k]; } } return obj; - }; - return Translator; -}()); + } +} function getI18nObjects(fileData, filterLocales) { - var _a; - return (_a = getDataByLocale(fileData, "Obj", filterLocales)) === null || _a === void 0 ? void 0 : _a.data; + return getDataByLocale(fileData, "Obj", filterLocales)?.data; } export { AbstractComp, AbstractNode, CachedNode, CodeNode, CompActionTypes, FetchCheckNode, FunctionNode, MultiBaseComp, RecordNode, RelaxedJsonParser, SimpleAbstractComp, SimpleComp, SimpleNode, Translator, ValueAndMsg, WrapContextNodeV2, WrapNode, changeChildAction, changeDependName, changeEditDSLAction, changeValueAction, clearMockWindow, clearStyleEval, customAction, deferAction, deleteCompAction, dependingNodeMapEquals, evalFunc, evalFunctionResult, evalNodeOrMinor, evalPerfUtil, evalScript, evalStyle, executeQueryAction, fromRecord, fromUnevaledValue, fromValue, fromValueWithCache, getDynamicStringSegments, getI18nObjects, getValueByLocale, i18n, isBroadcastAction, isChildAction, isCustomAction, isDynamicSegment, isFetching, isMyCustomAction, mergeExtra, multiChangeAction, nodeIsRecord, onlyEvalAction, relaxedJSONToJSON, renameAction, replaceCompAction, routeByNameAction, transformWrapper, triggerModuleEventAction, unwrapChildAction, updateActionContextAction, updateNodesV2Action, withFunction, wrapActionExtraInfo, wrapChildAction, wrapContext, wrapDispatch }; diff --git a/client/packages/lowcoder-design/src/components/CustomModal.tsx b/client/packages/lowcoder-design/src/components/CustomModal.tsx index 990931e90..e7101cefb 100644 --- a/client/packages/lowcoder-design/src/components/CustomModal.tsx +++ b/client/packages/lowcoder-design/src/components/CustomModal.tsx @@ -1,7 +1,7 @@ import { ButtonProps } from "antd/es/button"; import { default as AntdModal, ModalFuncProps, ModalProps as AntdModalProps } from "antd/es/modal"; import { ReactComponent as PackUpIcon } from "icons/v1/icon-Pack-up.svg"; -import React, { ReactNode, useState } from "react"; +import React, { ReactNode, useRef, useState } from "react"; import styled from "styled-components"; import { TacoButtonType, TacoButton } from "components/button"; import Draggable from "react-draggable"; @@ -221,9 +221,12 @@ const DEFAULT_PROPS = { } as const; function CustomModalRender(props: Omit & { width?: string | number }) { + const draggableRef = useRef(null); + return ( - - + diff --git a/client/packages/lowcoder-design/src/components/Drawer.tsx b/client/packages/lowcoder-design/src/components/Drawer.tsx index 89b152a64..7ccbce09f 100644 --- a/client/packages/lowcoder-design/src/components/Drawer.tsx +++ b/client/packages/lowcoder-design/src/components/Drawer.tsx @@ -1,13 +1,32 @@ import { default as AntdDrawer, DrawerProps as AntdDrawerProps } from "antd/es/drawer"; import Handle from "./Modal/handler"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, useCallback, useRef } from "react"; import { Resizable, ResizeHandle } from "react-resizable"; import { useResizeDetector } from "react-resize-detector"; import styled from "styled-components"; const StyledDrawer = styled(AntdDrawer)` & .ant-drawer-content-wrapper { - transition-duration: 0s; + transition: transform 0.3s cubic-bezier(0.7, 0.3, 0.1, 1) !important; + will-change: transform; + transform: translate3d(0, 0, 0); + } + + & .ant-drawer-content { + transition: none !important; + } + + & .ant-drawer-mask { + transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1) !important; + will-change: opacity; + } + + & .ant-drawer-header { + transition: none !important; + } + + & .ant-drawer-body { + transition: none !important; } `; @@ -53,19 +72,58 @@ export function Drawer(props: DrawerProps) { () => (resizable ? [getResizeHandle(placement)] : []), [placement, resizable] ); - const isTopBom = ["top", "bottom"].includes(placement); + const isTopBom = useMemo(() => ["top", "bottom"].includes(placement), [placement]); const [width, setWidth] = useState(); const [height, setHeight] = useState(); + const mountedRef = useRef(true); + + // Combined effect for width and height cleanup useEffect(() => { - setWidth(undefined); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [drawerWidth]); + if (drawerWidth !== undefined) { + setWidth(undefined); + } + if (drawerHeight !== undefined) { + setHeight(undefined); + } + }, [drawerWidth, drawerHeight]); + + // Cleanup on unmount useEffect(() => { - setHeight(undefined); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [drawerHeight]); - const { width: detectWidth, height: detectHeight, ref } = useResizeDetector(); - // log.info("Drawer. drawerWidth: ", drawerWidth, " width: ", width, "detectWidth: ", detectWidth); + return () => { + mountedRef.current = false; + }; + }, []); + + const { width: detectWidth, height: detectHeight, ref } = useResizeDetector({ + onResize: () => { + // Only update if component is still mounted + if (!mountedRef.current) return; + } + }); + + const handleResizeStart = useCallback( + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + props.onResizeStart?.(event, node, size, handle); + }, + [props.onResizeStart] + ); + + const handleResize = useCallback( + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + if (!mountedRef.current) return; + isTopBom ? setHeight(size.height) : setWidth(size.width); + props.onResize?.(event, node, size, handle); + }, + [isTopBom, props.onResize] + ); + + const handleResizeStop = useCallback( + (event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + props.onResizeStop?.(event, node, size, handle); + }, + [props.onResizeStop] + ); + return ( - props.onResizeStart?.(event, node, size, handle) - } - onResize={(event, { node, size, handle }) => { - isTopBom ? setHeight(size.height) : setWidth(size.width); - props.onResize?.(event, node, size, handle); - }} - onResizeStop={(event, { node, size, handle }) => - props.onResizeStop?.(event, node, size, handle) - } + onResizeStart={handleResizeStart} + onResize={handleResize} + onResizeStop={handleResizeStop} > {children} diff --git a/client/packages/lowcoder-design/src/components/Modal/handler.tsx b/client/packages/lowcoder-design/src/components/Modal/handler.tsx index c5c293ca3..c51a6858f 100644 --- a/client/packages/lowcoder-design/src/components/Modal/handler.tsx +++ b/client/packages/lowcoder-design/src/components/Modal/handler.tsx @@ -1,4 +1,5 @@ import styled, { css } from "styled-components"; +import { memo, useMemo } from "react"; type ResizeHandleAxis = "s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne"; type ReactRef = { @@ -83,8 +84,11 @@ const ResizeHandle = styled.div<{ $axis: string }>` ${(props) => (["sw", "nw", "se", "ne"].indexOf(props.$axis) >= 0 ? CornerHandle : "")}; `; -const Handle = (axis: ResizeHandleAxis, ref: ReactRef) => { - return ; -}; +// Memoize Handle component +const Handle = memo((axis: ResizeHandleAxis, ref: ReactRef) => { + return ; +}); + +Handle.displayName = 'Handle'; export default Handle; diff --git a/client/packages/lowcoder-design/src/components/Modal/index.tsx b/client/packages/lowcoder-design/src/components/Modal/index.tsx index 1677bcb9b..0c506b0e9 100644 --- a/client/packages/lowcoder-design/src/components/Modal/index.tsx +++ b/client/packages/lowcoder-design/src/components/Modal/index.tsx @@ -1,5 +1,5 @@ import { default as AntdModal, ModalProps as AntdModalProps } from "antd/es/modal"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { Resizable, ResizeHandle } from "react-resizable"; import { useResizeDetector } from "react-resize-detector"; import Handle from "./handler"; @@ -39,45 +39,60 @@ export function Modal(props: ModalProps) { const [width, setWidth] = useState(); const [height, setHeight] = useState(); + + // Memoize style object + const modalStyles = useMemo(() => ({ + body: { + height: height ?? modalHeight, + ...styles?.body, + } + }), [height, modalHeight, styles?.body]); + + // Memoize event handlers + const handleResizeStart = useCallback((event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + props.onResizeStart?.(event, node, size, handle); + }, [props.onResizeStart]); + + const handleResize = useCallback((event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + setWidth(size.width); + setHeight(size.height); + props.onResize?.(event, node, size, handle); + }, [props.onResize]); + + const handleResizeStop = useCallback((event: React.SyntheticEvent, { node, size, handle }: { node: HTMLElement; size: { width: number; height: number }; handle: ResizeHandle }) => { + props.onResizeStop?.(event, node, size, handle); + }, [props.onResizeStop]); + useEffect(() => { setWidth(undefined); // eslint-disable-next-line react-hooks/exhaustive-deps }, [modalWidth]); + useEffect(() => { setHeight(undefined); // eslint-disable-next-line react-hooks/exhaustive-deps }, [modalHeight]); const { width: detectWidth, height: detectHeight, ref } = useResizeDetector(); - // log.info("Modal. modalWidth: ", modalWidth, " width: ", size?.w, " detectWidth: ", detectWidth); + + // Memoize Resizable props + const resizableProps = useMemo(() => ({ + width: width ?? detectWidth ?? 0, + height: height ?? detectHeight ?? 0, + resizeHandles, + handle: Handle, + onResizeStart: handleResizeStart, + onResize: handleResize, + onResizeStop: handleResizeStop + }), [width, detectWidth, height, detectHeight, resizeHandles, handleResizeStart, handleResize, handleResizeStop]); + return ( - - props.onResizeStart?.(event, node, size, handle) - } - onResize={(event, { node, size, handle }) => { - setWidth(size.width); - setHeight(size.height); - props.onResize?.(event, node, size, handle); - }} - onResizeStop={(event, { node, size, handle }) => - props.onResizeStop?.(event, node, size, handle) - } - > + {children} diff --git a/client/packages/lowcoder-design/src/components/ScrollBar.tsx b/client/packages/lowcoder-design/src/components/ScrollBar.tsx index 9443d38a2..049980f2e 100644 --- a/client/packages/lowcoder-design/src/components/ScrollBar.tsx +++ b/client/packages/lowcoder-design/src/components/ScrollBar.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useCallback, useMemo } from "react"; import SimpleBar from "simplebar-react"; import styled from "styled-components"; import { DebouncedFunc } from 'lodash'; // Assuming you're using lodash's DebouncedFunc type @@ -57,7 +57,7 @@ interface IProps { children: React.ReactNode; className?: string; height?: string; - overflow?:string, + overflow?: string, style?: React.CSSProperties; // Add this line to include a style prop scrollableNodeProps?: { onScroll: DebouncedFunc<(e: any) => void>; @@ -68,7 +68,7 @@ interface IProps { suffixNode?: React.ReactNode; } -export const ScrollBar = ({ +export const ScrollBar = React.memo(({ className, children, style, @@ -80,31 +80,46 @@ export const ScrollBar = ({ suffixNode, ...otherProps }: IProps) => { - const height = style?.height ?? '100%'; - // You can now use the style prop directly or pass it to SimpleBar - const combinedStyle = { ...style, height }; // Example of combining height with passed style + // Memoize the combined style to prevent unnecessary re-renders + const combinedStyle = useMemo(() => { + const height = style?.height ?? '100%'; + return { ...style, height }; + }, [style]); + + // Memoize the render function to prevent recreation on every render + const renderContent = useCallback(({ scrollableNodeProps, contentNodeProps }: any) => ( + + {prefixNode} + + {children} + + {suffixNode} + + ), [prefixNode, children, suffixNode]); return hideScrollbar ? ( - + {prefixNode} {children} {suffixNode} ) : ( - - - {({ scrollableNodeProps, contentNodeProps }) => { - return ( - - {prefixNode} - - {children} - - {suffixNode} - - ); - }} + + + {renderContent} ); -}; +}); diff --git a/client/packages/lowcoder-design/src/components/Section.tsx b/client/packages/lowcoder-design/src/components/Section.tsx index 1c654f637..46d346f23 100644 --- a/client/packages/lowcoder-design/src/components/Section.tsx +++ b/client/packages/lowcoder-design/src/components/Section.tsx @@ -1,5 +1,5 @@ import { trans } from "i18n/design"; -import React, { ReactNode, useContext } from "react"; +import React, { ReactNode, useContext, useCallback, useMemo } from "react"; import styled from "styled-components"; import { ReactComponent as Packup } from "icons/v1/icon-Pack-up.svg"; import { labelCss } from "./Label"; @@ -14,6 +14,7 @@ const SectionItem = styled.div<{ $width?: number }>` border-bottom: none; } `; + const SectionLabel = styled.div` ${labelCss}; flex-grow: 1; @@ -64,6 +65,10 @@ const SectionLabelDiv = styled.div` } `; +const ButtonContainer = styled.div` + display: flex; +`; + const ShowChildren = styled.div<{ $show?: string; $noMargin?: boolean }>` display: ${(props) => props.$show || "none"}; flex-direction: column; @@ -80,6 +85,7 @@ const TooltipWrapper = styled.span` white-space: pre-wrap; color:#fff; `; + interface ISectionConfig { name?: string; open?: boolean; @@ -109,47 +115,51 @@ export const PropertySectionContext = React.createContext) => { - const { name,hasTooltip } = props; +const TOOLTIP_CONTENT = ( + + Here you can enter the animation type codes. Like bounce, swing or + tada. Read more about all possible codes at:{" "} + https://animate.style + +); + +export const BaseSection = React.memo((props: ISectionConfig) => { + const { name, hasTooltip } = props; const { compName, state, toggle } = useContext(PropertySectionContext); const open = props.open !== undefined ? props.open : name ? state[compName]?.[name] !== false : true; - // console.log("open", open, props.open); - - const handleToggle = () => { + const handleToggle = useCallback(() => { if (!name) { return; } toggle(compName, name); - }; + }, [name, compName, toggle]); + + const tooltipContent = useMemo(() => hasTooltip ? TOOLTIP_CONTENT : null, [hasTooltip]); + + const getPopupContainer = useCallback((node: HTMLElement) => { + return (node.closest('.react-grid-item') as HTMLElement) || document.body; + }, []); return ( {props.name && ( {props.name} - + {open && props.additionalButton} - + )} - Here you can enter the animation type codes. Like bounce, swing or - tada. Read more about all possible codes at:{" "} - https://animate.style - - ) - } + title={tooltipContent} arrow={{ pointAtCenter: true, }} placement="top" color="#2c2c2c" - getPopupContainer={(node: any) => node.closest('.react-grid-item')} + getPopupContainer={getPopupContainer} > {props.children} @@ -157,11 +167,15 @@ export const BaseSection = (props: ISectionConfig) => { ); -}; +}); -export function Section(props: ISectionConfig) { +BaseSection.displayName = 'BaseSection'; + +export const Section = React.memo((props: ISectionConfig) => { return controlItem({ filterText: props.name, searchChild: true }, ); -} +}); + +Section.displayName = 'Section'; // common section names export const sectionNames = { diff --git a/client/packages/lowcoder-design/src/components/Tab.tsx b/client/packages/lowcoder-design/src/components/Tab.tsx index 0c9df3216..3828b7e27 100644 --- a/client/packages/lowcoder-design/src/components/Tab.tsx +++ b/client/packages/lowcoder-design/src/components/Tab.tsx @@ -1,5 +1,5 @@ import styled, { css } from "styled-components"; -import React from "react"; +import React, { useCallback, useMemo } from "react"; const HeaderDiv = styled.div` width: 312px; @@ -79,26 +79,42 @@ interface ITabs { activeKey: string; } -const Tabs = (props: ITabs) => { +const Tabs = React.memo((props: ITabs) => { const { onChange, tabsConfig, activeKey } = props; - const activeTab = tabsConfig.find((c) => c.key === activeKey) || tabsConfig[0]; + + const activeTab = useMemo(() => + tabsConfig.find((c) => c.key === activeKey) || tabsConfig[0], + [tabsConfig, activeKey] + ); + + const handleTabClick = useCallback((key: string) => { + onChange(key); + }, [onChange]); + + const renderTab = useCallback((tab: ITabsConfig) => { + const isActive = activeTab.key === tab.key; + return ( + handleTabClick(tab.key)} + $isActive={isActive} + > + {tab.icon} + {tab.title} + + ); + }, [activeTab.key, handleTabClick]); return ( <> - {props.tabsConfig.map((tab) => { - const isActive = activeTab.key === tab.key; - return ( - onChange(tab.key)} $isActive={isActive}> - {tab.icon} - {tab.title} - - ); - })} + {tabsConfig.map(renderTab)} {activeTab.content} > ); -}; +}); + +Tabs.displayName = 'Tabs'; export { Tabs }; diff --git a/client/packages/lowcoder-design/src/components/container.tsx b/client/packages/lowcoder-design/src/components/container.tsx index c8054c921..aba8898ce 100644 --- a/client/packages/lowcoder-design/src/components/container.tsx +++ b/client/packages/lowcoder-design/src/components/container.tsx @@ -1,7 +1,8 @@ import { trans } from "i18n/design"; -import { ReactNode } from "react"; +import { ReactNode, useMemo } from "react"; import styled from "styled-components"; import { ReactComponent as ContainerDrag } from "icons/v1/icon-container-drag.svg"; +import React from "react"; type ContainerPlaceholderProps = { children?: ReactNode; @@ -13,25 +14,31 @@ const HintText = styled.span` text-align: center; `; -export function ContainerPlaceholder(props: ContainerPlaceholderProps) { +const ContainerWrapper = styled.div` + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +`; + +const StyledContainerDrag = styled(ContainerDrag)` + vertical-align: bottom; + margin-right: 8px; +`; + +export const ContainerPlaceholder = React.memo(function ContainerPlaceholder(props: ContainerPlaceholderProps) { return ( - + - + {props.children} - + ); -} +}); +// Create a memoized version of the placeholder export const HintPlaceHolder = ( {trans("container.hintPlaceHolder")} ); diff --git a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx index 23c73b200..d5ef7c4d0 100644 --- a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx +++ b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx @@ -152,6 +152,7 @@ class Icon { else return ( @@ -256,6 +257,7 @@ const IconPopup = (props: { searchKeywords?: Record; IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { + const draggableRef = useRef(null); const [searchText, setSearchText] = useState(""); const [allIcons, setAllIcons] = useState>({}); const searchResults = useMemo( @@ -289,6 +291,7 @@ const IconPopup = (props: { title={icon.title + ", Key: " + key} placement="bottom" align={{ offset: [0, -7, 0, 0] }} + getPopupContainer={(node: any) => node.parentNode} destroyTooltipOnHide > - + + {trans("iconSelect.title")} diff --git a/client/packages/lowcoder-design/src/components/markdown.tsx b/client/packages/lowcoder-design/src/components/markdown.tsx index 1260f007f..f20b2c0a3 100644 --- a/client/packages/lowcoder-design/src/components/markdown.tsx +++ b/client/packages/lowcoder-design/src/components/markdown.tsx @@ -1,10 +1,11 @@ import { css } from "styled-components"; -import { lazy } from "react"; +import { lazy, Suspense, memo, useMemo } from "react"; // import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; import remarkGfm from "remark-gfm"; -import type { Options as ReactMarkdownOptions } from "react-markdown/lib"; +import type { Options as ReactMarkdownOptions, Components } from "react-markdown/lib"; +import type { Pluggable } from "unified"; const ReactMarkdown = lazy(() => import('react-markdown')); @@ -38,44 +39,69 @@ interface TacoMarkDownProps extends ReactMarkdownOptions { children: string; } -const components = { - a: (props: any) => { - const { node, children, ...otherProps } = props; - return ( - - {children} - - ); +interface AnchorProps { + node?: any; + children: React.ReactNode; + href?: string; + className?: string; + style?: React.CSSProperties; +} + +// Memoize the anchor component to prevent unnecessary re-renders +const Anchor = memo((props: AnchorProps) => { + const { node, children, ...otherProps } = props; + return ( + + {children} + + ); +}); + +Anchor.displayName = 'Anchor'; + +// Memoize the components object with proper typing +const components: Components = { + a: Anchor as any, // Type assertion needed due to react-markdown's type definitions +}; + +// Memoize the sanitize schema +const sanitizeSchema = { + ...defaultSchema, + attributes: { + ...defaultSchema.attributes, + "*": [ + ...((defaultSchema.attributes && defaultSchema.attributes["*"]) || []), + "style", + "className", + ], }, }; -export const TacoMarkDown = (props: TacoMarkDownProps) => { +// Memoize the rehype plugins array with proper typing +const rehypePlugins: Pluggable[] = [ + [rehypeRaw] as Pluggable, + [rehypeSanitize, sanitizeSchema] as Pluggable, +]; + +export const TacoMarkDown = memo((props: TacoMarkDownProps) => { const { children, ...otherProps } = props; + + // Memoize the remark plugins array with proper typing + const remarkPlugins = useMemo(() => [remarkGfm] as Pluggable[], []); + return ( - - {children} - + Loading...}> + + {children} + + ); -}; +}); + +TacoMarkDown.displayName = 'TacoMarkDown'; diff --git a/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx b/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx index 060945977..a777c4628 100644 --- a/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx +++ b/client/packages/lowcoder-design/src/components/shapeSelect/index.tsx @@ -328,6 +328,7 @@ const IconPopup = (props: { searchKeywords?: Record; IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { + const draggableRef = useRef(null); const [allIcons, setAllIcons] = useState>({}); const onChangeRef = useRef(props.onChange); onChangeRef.current = props.onChange; @@ -374,8 +375,8 @@ const IconPopup = (props: { // [searchResults, allIcons, onChangeIcon] // ); return ( - - + + {trans("shapeSelect.title")} diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json index 4c4689031..c10fe0b5b 100644 --- a/client/packages/lowcoder/package.json +++ b/client/packages/lowcoder/package.json @@ -6,7 +6,7 @@ "main": "src/index.sdk.ts", "types": "src/index.sdk.ts", "dependencies": { - "@ant-design/icons": "^5.3.0", + "@ant-design/icons": "^6.0.0", "@codemirror/autocomplete": "^6.11.1", "@codemirror/commands": "^6.3.2", "@codemirror/lang-css": "^6.2.1", @@ -26,20 +26,20 @@ "@fortawesome/react-fontawesome": "latest", "@lottiefiles/dotlottie-react": "^0.13.0", "@manaflair/redux-batch": "^1.0.0", - "@rjsf/antd": "^5.21.2", - "@rjsf/core": "^5.21.2", - "@rjsf/utils": "^5.21.2", - "@rjsf/validator-ajv8": "^5.21.2", + "@rjsf/antd": "^5.24.9", + "@rjsf/core": "^5.24.9", + "@rjsf/utils": "^5.24.9", + "@rjsf/validator-ajv8": "^5.24.9", "@types/lodash": "^4.14.194", "@types/node": "^16.7.13", - "@types/react": "^18.2.45", - "@types/react-dom": "^18.2.18", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", "@types/react-signature-canvas": "^1.0.2", "@types/react-test-renderer": "^18.0.0", "@types/react-virtualized": "^9.21.21", - "alasql": "^4.6.2", + "alasql": "^4.6.6", "animate.css": "^4.1.1", - "antd": "^5.23.4", + "antd": "^5.24.8", "axios": "^1.7.7", "buffer": "^6.0.3", "clsx": "^2.0.0", @@ -65,28 +65,27 @@ "papaparse": "^5.3.2", "qrcode.react": "^3.1.0", "rc-trigger": "^5.3.1", - "react": "^18.2.0", + "react": "^19.1.0", "react-best-gradient-color-picker": "^3.0.10", "react-colorful": "^5.5.1", "react-device-mockup": "^1.0.0", "react-documents": "^1.2.1", - "react-dom": "^18.2.0", - "react-draggable": "^4.4.4", + "react-dom": "^19.1.0", + "react-draggable": "^4.4.6", "react-error-boundary": "^4.0.13", "react-grid-layout": "^1.3.0", "react-helmet": "^6.1.0", - "react-joyride": "^2.4.0", + "react-joyride": "next", "react-json-view": "^1.21.3", "react-markdown": "^9.0.1", "react-qr-barcode-scanner": "^1.0.6", - "react-quill": "^2.0.0", + "react-quill-new": "^3.4.6", "react-redux": "^7.2.6", "react-resizable": "^3.0.4", - "react-resize-detector": "^7.0.0", + "react-resize-detector": "^12.0.2", "react-router": "^5.2.1", "react-router-dom": "^5.3.0", "react-signature-canvas": "^1.0.6", - "react-sortable-hoc": "^2.0.0", "react-test-renderer": "^18.1.0", "react-use": "^17.3.2", "react-webcam": "^7.2.0", diff --git a/client/packages/lowcoder/src/components/CompName.tsx b/client/packages/lowcoder/src/components/CompName.tsx index 0e11d10e2..69fd8c781 100644 --- a/client/packages/lowcoder/src/components/CompName.tsx +++ b/client/packages/lowcoder/src/components/CompName.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useState, useCallback, useMemo } from "react"; import styled from "styled-components"; import { PointIcon, SearchOutlinedIcon } from "lowcoder-design/src/icons"; import type { EditPopoverItemType } from 'lowcoder-design/src/components/popover'; @@ -72,89 +72,100 @@ interface Iprops { search?: { searchText: string; setSearchText: (t: string) => void }; } -export const CompName = (props: Iprops) => { +export const CompName = React.memo((props: Iprops) => { const [error, setError] = useState(undefined); const [editing, setEditing] = useState(false); const [upgrading, setUpgrading] = useState(false); + const [showSearch, setShowSearch] = useState(false); + const editorState = useContext(EditorContext); - const selectedComp = values(editorState.selectedComps())[0]; - const compType = selectedComp.children.compType.getView() as UICompType; - const compInfo = parseCompType(compType); - const docUrl = getComponentDocUrl(compType); - const playgroundUrl = getComponentPlaygroundUrl(compType); - - const items: EditPopoverItemType[] = []; - - // Falk: TODO - Implement upgrade for individual Version functionality - const handleUpgrade = async () => { - if (upgrading) { - return; - } + const selectedComp = useMemo(() => values(editorState.selectedComps())[0], [editorState]); + const compType = useMemo(() => selectedComp.children.compType.getView() as UICompType, [selectedComp]); + const compInfo = useMemo(() => parseCompType(compType), [compType]); + const docUrl = useMemo(() => getComponentDocUrl(compType), [compType]); + const playgroundUrl = useMemo(() => getComponentPlaygroundUrl(compType), [compType]); + + // Cleanup on unmount + useEffect(() => { + return () => { + setError(undefined); + setEditing(false); + setUpgrading(false); + setShowSearch(false); + }; + }, []); + + // Reset search when name changes + useEffect(() => { + setShowSearch(false); + }, [props.name]); + + const handleUpgrade = useCallback(async () => { + if (upgrading) return; setUpgrading(true); - await GridCompOperator.upgradeCurrentComp(editorState); - setUpgrading(false); - }; - - if (docUrl) { - items.push({ - text: trans("comp.menuViewDocs"), - onClick: () => { - window.open(docUrl, "_blank"); - }, - }); - } + try { + await GridCompOperator.upgradeCurrentComp(editorState); + } finally { + setUpgrading(false); + } + }, [upgrading, editorState]); - if (playgroundUrl) { - items.push({ - text: trans("comp.menuViewPlayground"), - onClick: () => { - window.open(playgroundUrl, "_blank"); - }, - }); - } + const handleRename = useCallback((value: string) => { + if (editorState.rename(props.name, value)) { + editorState.setSelectedCompNames(new Set([value])); + setError(undefined); + } + }, [editorState, props.name]); + const handleSearchChange = useCallback((e: React.ChangeEvent) => { + props.search?.setSearchText(e.target.value); + }, [props.search]); - if (compInfo.isRemote) { - // Falk: Displaying the current version of the component - items.push({ - text: trans("history.currentVersion") + ": " + compInfo.packageVersion, - onClick: () => { - }, - }); - // items.push({ - // text: trans("history.currentVersion") + ": " + compInfo.packageVersion, - // onClick: () => { - - // }, - // }); - - items.push({ - text: trans("comp.menuUpgradeToLatest"), - onClick: () => { - handleUpgrade(); - }, - - }); - } + const handleSearchToggle = useCallback(() => { + setShowSearch(prev => !prev); + props.search?.setSearchText(""); + }, [props.search]); - const [showSearch, setShowSearch] = useState(false); - const { search } = props; - useEffect(() => { - setShowSearch(false); - }, [props.name]); - const compName = ( - + const items = useMemo(() => { + const menuItems: EditPopoverItemType[] = []; + + if (docUrl) { + menuItems.push({ + text: trans("comp.menuViewDocs"), + onClick: () => window.open(docUrl, "_blank"), + }); + } + + if (playgroundUrl) { + menuItems.push({ + text: trans("comp.menuViewPlayground"), + onClick: () => window.open(playgroundUrl, "_blank"), + }); + } + + if (compInfo.isRemote) { + menuItems.push({ + text: trans("history.currentVersion") + ": " + compInfo.packageVersion, + onClick: () => {}, + }); + + menuItems.push({ + text: trans("comp.menuUpgradeToLatest"), + onClick: handleUpgrade, + }); + } + + return menuItems; + }, [docUrl, playgroundUrl, compInfo, handleUpgrade]); + + const compName = useMemo(() => ( + { - if (editorState.rename(props.name, value)) { - editorState.setSelectedCompNames(new Set([value])); - setError(undefined); - } - }} + onFinish={handleRename} onChange={(value) => setError(editorState.checkRename(props.name, value))} - onEditStateChange={(editing) => setEditing(editing)} + onEditStateChange={setEditing} /> { hasError={!!error} /> - {!!search && ( + {!!props.search && ( { - setShowSearch(!showSearch); - search?.setSearchText(""); - }} + onClick={handleSearchToggle} style={{ color: showSearch ? "#315EFB" : "#8B8FA3" }} /> )} - { compType === "module" ? ( + {compType === "module" ? ( GridCompOperator.editComp(editorState)} @@ -189,19 +197,32 @@ export const CompName = (props: Iprops) => { )} - ); + ), [ + props.width, + props.search, + props.name, + showSearch, + error, + editing, + compType, + items, + editorState, + handleRename, + handleSearchToggle + ]); + return ( {compName} - {search && showSearch && ( + {props.search && showSearch && ( search.setSearchText(e.target.value)} + value={props.search.searchText} + onChange={handleSearchChange} allowClear={true} style={{ padding: "0 16px", margin: "0 0 4px 0" }} /> )} ); -}; +}); diff --git a/client/packages/lowcoder/src/components/Tabs.tsx b/client/packages/lowcoder/src/components/Tabs.tsx index 1735a838c..6ec03515c 100644 --- a/client/packages/lowcoder/src/components/Tabs.tsx +++ b/client/packages/lowcoder/src/components/Tabs.tsx @@ -1,6 +1,6 @@ import { default as AntdTabs } from "antd/es/tabs"; import { GreyTextColor, TabActiveColor } from "constants/style"; -import { ReactNode } from "react"; +import { ReactNode, memo } from "react"; import styled from "styled-components"; export const Tabs = styled(AntdTabs)` @@ -52,7 +52,7 @@ interface TabTitleProps { text: ReactNode; } -export const TabTitle = function TabTitle(props: TabTitleProps) { +export const TabTitle = memo(function TabTitle(props: TabTitleProps) { const { icon, text } = props; return ( @@ -60,4 +60,6 @@ export const TabTitle = function TabTitle(props: TabTitleProps) { {text} ); -}; +}); + +TabTitle.displayName = 'TabTitle'; diff --git a/client/packages/lowcoder/src/components/resultPanel/BottomResultPanel.tsx b/client/packages/lowcoder/src/components/resultPanel/BottomResultPanel.tsx index 36e0a64fd..7f4f82c23 100644 --- a/client/packages/lowcoder/src/components/resultPanel/BottomResultPanel.tsx +++ b/client/packages/lowcoder/src/components/resultPanel/BottomResultPanel.tsx @@ -55,6 +55,7 @@ export const BottomResultPanel = (props: BottomResultPanelProps) => { return ( = (props: { value: T; onChange: (value: T) => void; onChangeEnd: () => void; + otherProps?: Record; }) => ReactNode; const BorderDiv = styled.div` @@ -84,7 +93,7 @@ interface EditableCellProps extends CellProps { changeValue?: T | null; } -export function EditableCell(props: EditableCellProps) { +function EditableCellComp(props: EditableCellProps) { const { dispatch, normalView, @@ -96,47 +105,73 @@ export function EditableCell(props: EditableCellProps) { candidateStatus, editMode, onTableEvent, + tableSize, + textOverflow, + cellTooltip, + ...otherProps } = props; + const status = _.isNil(changeValue) ? "normal" : "toSave"; const editable = editViewFn ? props.editable : false; const { isEditing, setIsEditing } = useContext(TableCellContext); const value = changeValue ?? baseValue!; const [tmpValue, setTmpValue] = useState(value); - const singleClickEdit = editMode === 'single'; + const singleClickEdit = editMode === 'single'; + + // Use refs to track previous values for comparison + const prevValueRef = useRef(value); useEffect(() => { - setTmpValue(value); - }, [JSON.stringify(value)]); + console.log("rendered EditableCellComp"); + }, []); + + // Update tmpValue when value changes + useEffect(() => { + if (!_.isEqual(value, prevValueRef.current)) { + setTmpValue(value); + prevValueRef.current = value; + } + }, [value]); const onChange = useCallback( (value: T) => { setTmpValue(value); }, - [setTmpValue] + [] ); const onChangeEnd = useCallback(() => { setIsEditing(false); + const newValue = _.isNil(tmpValue) || _.isEqual(tmpValue, baseValue) ? null : tmpValue; dispatch( changeChildAction( "changeValue", - _.isNil(tmpValue) || _.isEqual(tmpValue, baseValue) ? null : tmpValue, + newValue, false ) ); if(!_.isEqual(tmpValue, value)) { onTableEvent?.('columnEdited'); } - }, [dispatch, JSON.stringify(baseValue), JSON.stringify(tmpValue)]); + }, [dispatch, tmpValue, baseValue, value, onTableEvent]); const editView = useMemo( - () => editViewFn?.({ value, onChange, onChangeEnd }) ?? <>>, - [editViewFn, JSON.stringify(value), onChange, onChangeEnd] + () => editViewFn?.({ value, onChange, onChangeEnd, otherProps }) ?? <>>, + [editViewFn, value, onChange, onChangeEnd, otherProps] ); const enterEditFn = useCallback(() => { if (editable) setIsEditing(true); - }, [editable]); + }, [editable, setIsEditing]); + + // Cleanup function + useEffect(() => { + return () => { + // Reset state on unmount + setTmpValue(null); + setIsEditing(false); + }; + }, [setIsEditing]); if (isEditing) { return ( @@ -157,9 +192,10 @@ export function EditableCell(props: EditableCellProps) { - {status === "toSave" && !isEditing && } + {status === "toSave" && !isEditing && } @@ -169,20 +205,15 @@ export function EditableCell(props: EditableCellProps) { {/* overlay on normal view to handle double click for editing */} {editable && ( - - - + + )} ); } + +export const EditableCell = React.memo(EditableCellComp) as typeof EditableCellComp; \ No newline at end of file diff --git a/client/packages/lowcoder/src/components/table/columnTypeView.tsx b/client/packages/lowcoder/src/components/table/columnTypeView.tsx index 87216d833..6ad6b1767 100644 --- a/client/packages/lowcoder/src/components/table/columnTypeView.tsx +++ b/client/packages/lowcoder/src/components/table/columnTypeView.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; const ColumnTypeViewWrapper = styled.div<{ @@ -73,13 +73,12 @@ function childIsOverflow(nodes: HTMLCollection): boolean { return false; } -export default function ColumnTypeView(props: { +function ColumnTypeView(props: { children: React.ReactNode, textOverflow?: boolean, }) { - - const wrapperRef = useRef(null); - const hoverViewRef = useRef(null); + const wrapperRef = useRef(null); + const hoverViewRef = useRef(null); const [isHover, setIsHover] = useState(false); const [hasOverflow, setHasOverflow] = useState(false); const [adjustedPosition, setAdjustedPosition] = useState<{ @@ -89,39 +88,71 @@ export default function ColumnTypeView(props: { height?: number; width?: number; }>({ done: false }); - const [delayHandler, setDelayHandler] = useState(); - const delayMouseEnter = useMemo(() => { - return () => - setDelayHandler( - setTimeout(() => { - setIsHover(true); - }, 300) - ); + + // Use ref for timeout to avoid state updates + const timeoutRef = useRef(); + const mountedRef = useRef(true); + + const delayMouseEnter = useCallback(() => { + // Clear any existing timeout + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + timeoutRef.current = setTimeout(() => { + if (mountedRef.current) { + setIsHover(true); + } + }, 300); + }, []); + + const handleMouseLeave = useCallback(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + if (mountedRef.current) { + setIsHover(false); + } + }, []); + + const handleMouseEnter = useCallback(() => { + if (mountedRef.current) { + setIsHover(true); + } }, []); + // Check for overflow useEffect(() => { const wrapperEle = wrapperRef.current; if (!isHover || !wrapperEle) { return; } + const overflow = wrapperEle.clientHeight < wrapperEle.scrollHeight || wrapperEle.clientWidth < wrapperEle.scrollWidth; + if (overflow || childIsOverflow(wrapperEle.children)) { - !hasOverflow && setHasOverflow(true); - } else { - hasOverflow && setHasOverflow(false); + if (mountedRef.current && !hasOverflow) { + setHasOverflow(true); + } + } else if (mountedRef.current && hasOverflow) { + setHasOverflow(false); } - }, [isHover]); + }, [isHover, hasOverflow]); + // Adjust position useEffect(() => { const wrapperEle = wrapperRef.current; const hoverEle = hoverViewRef.current; + if (!isHover || !hasOverflow) { if (wrapperEle?.parentElement) { wrapperEle.parentElement.style.zIndex = ""; } - setAdjustedPosition({ done: false }); + if (mountedRef.current) { + setAdjustedPosition({ done: false }); + } return; } // Get the position of the outer table @@ -129,6 +160,7 @@ export default function ColumnTypeView(props: { if (!hoverEle || !tableEle || !wrapperEle) { return; } + if (wrapperEle.parentElement) { // change parent z-index, fix bug when column sticky wrapperEle.parentElement.style.zIndex = "999"; @@ -150,6 +182,7 @@ export default function ColumnTypeView(props: { tableEle.getBoundingClientRect().x + tableEle.offsetWidth - (hoverEle.getBoundingClientRect().x + width); + if (leftOverflow > 0) { left = leftOverflow; } else if (rightOverflow < 0) { @@ -163,27 +196,34 @@ export default function ColumnTypeView(props: { (hoverEle.getBoundingClientRect().y + height); // Adjust the hover position according to the table position - setAdjustedPosition({ - left: left, - top: bottomOverflow < 0 ? bottomOverflow : undefined, - height: height, - width: width, - done: true, - }); + if (mountedRef.current) { + setAdjustedPosition({ + left, + top: bottomOverflow < 0 ? bottomOverflow : undefined, + height, + width, + done: true, + }); + } }, [isHover, hasOverflow]); + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + return ( <> { - delayMouseEnter(); - }} - onMouseLeave={() => { - clearTimeout(delayHandler); - setIsHover(false); - }} + onMouseEnter={delayMouseEnter} + onMouseLeave={handleMouseLeave} > {props.children} @@ -197,10 +237,8 @@ export default function ColumnTypeView(props: { $adjustLeft={adjustedPosition.left} $adjustTop={adjustedPosition.top} $padding={`${wrapperRef.current.offsetTop}px ${wrapperRef.current.offsetLeft}px`} - onMouseEnter={() => { - setIsHover(true); - }} - onMouseLeave={() => setIsHover(false)} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} > {props.children} @@ -208,3 +246,5 @@ export default function ColumnTypeView(props: { > ); } + +export default React.memo(ColumnTypeView); \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx index 8b03c0492..5d2ab8c10 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx @@ -4,7 +4,7 @@ import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { IconControl } from "comps/controls/iconControl"; import { CompNameContext, EditorContext, EditorState } from "comps/editorState"; import { withDefault } from "comps/generators"; -import { UICompBuilder } from "comps/generators/uiCompBuilder"; +import { NewChildren, UICompBuilder } from "comps/generators/uiCompBuilder"; import { disabledPropertyView, hiddenPropertyView, @@ -24,9 +24,11 @@ import { } from "./buttonCompConstants"; import { RefControl } from "comps/controls/refControl"; -import React, { useContext, useEffect } from "react"; +import React, { useContext, useEffect, useCallback } from "react"; import { AnimationStyle } from "@lowcoder-ee/comps/controls/styleControlConstants"; import { styleControl } from "@lowcoder-ee/comps/controls/styleControl"; +import { RecordConstructorToComp } from "lowcoder-core"; +import { ToViewReturn } from "@lowcoder-ee/comps/generators/multi"; const FormLabel = styled(CommonBlueLabel)` font-size: 13px; @@ -120,83 +122,100 @@ function submitForm(editorState: EditorState, formName: string) { } } -const ButtonTmpComp = (function () { - const childrenMap = { - text: withDefault(StringControl, trans("button.button")), - type: dropdownControl(typeOptions, ""), - onEvent: ButtonEventHandlerControl, - disabled: BoolCodeControl, - loading: BoolCodeControl, - form: SelectFormControl, - prefixIcon: IconControl, - suffixIcon: IconControl, - style: ButtonStyleControl, - animationStyle: styleControl(AnimationStyle, 'animationStyle'), - viewRef: RefControl, - }; - return new UICompBuilder(childrenMap, (props) => { - return( - - - {(editorState) => ( - - isDefault(props.type) ? props.onEvent("click") : submitForm(editorState, props.form) - } - > - {props.prefixIcon && {props.prefixIcon}} - { - props.text || (props.prefixIcon || props.suffixIcon ? undefined : " ") // Avoid button disappearing - } - {props.suffixIcon && {props.suffixIcon}} - - )} - - - ); - }) - .setPropertyViewFn((children) => ( - <> - - {children.text.propertyView({ label: trans("text") })} - - - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( - <> - {children.type.propertyView({ label: trans("prop.type"), radioButton: true })} - {isDefault(children.type.getView()) - ? [ - children.onEvent.getPropertyView(), - disabledPropertyView(children), - hiddenPropertyView(children), - loadingPropertyView(children), - ] - : children.form.getPropertyView()} - - > - )} +const childrenMap = { + text: withDefault(StringControl, trans("button.button")), + type: dropdownControl(typeOptions, ""), + onEvent: ButtonEventHandlerControl, + disabled: BoolCodeControl, + loading: BoolCodeControl, + form: SelectFormControl, + prefixIcon: IconControl, + suffixIcon: IconControl, + style: ButtonStyleControl, + animationStyle: styleControl(AnimationStyle, 'animationStyle'), + viewRef: RefControl, +}; - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( - <> - - {children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })} - {children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })} - - {children.style.getPropertyView()} - > +type ChildrenType = NewChildren>; + +const ButtonPropertyView = React.memo((props: { + children: ChildrenType +}) => { + const { editorModeStatus } = useContext(EditorContext); + return ( + <> + + {props.children.text.propertyView({ label: trans("text") })} + + + {(editorModeStatus === "logic" || editorModeStatus === "both") && ( + <> + {props.children.type.propertyView({ label: trans("prop.type"), radioButton: true })} + {isDefault(props.children.type.getView()) + ? [ + props.children.onEvent.getPropertyView(), + disabledPropertyView(props.children), + hiddenPropertyView(props.children), + loadingPropertyView(props.children), + ] + : props.children.form.getPropertyView()} + + > + )} + + {(editorModeStatus === "layout" || editorModeStatus === "both") && ( + <> + + {props.children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })} + {props.children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })} + + {props.children.style.getPropertyView()} + > + )} + > + ); +}); + +const ButtonView = React.memo((props: ToViewReturn) => { + const editorState = useContext(EditorContext); + + const handleClick = useCallback(() => { + isDefault(props.type) ? props.onEvent("click") : submitForm(editorState, props.form); + }, [props.type, props.onEvent, props.form, editorState]); + + return ( + + + {(editorState) => ( + + {props.prefixIcon && {props.prefixIcon}} + { + props.text || (props.prefixIcon || props.suffixIcon ? undefined : " ") // Avoid button disappearing + } + {props.suffixIcon && {props.suffixIcon}} + )} - > - )) - .setExposeMethodConfigs(buttonRefMethods) - .build(); -})(); + + + ) +}); + +const buttonViewFn = (props: ToViewReturn) => +const buttonPropertyViewFn = (children: ChildrenType) => + +const ButtonTmpComp = new UICompBuilder(childrenMap, buttonViewFn) + .setPropertyViewFn(buttonPropertyViewFn) + .setExposeMethodConfigs(buttonRefMethods) + .build(); export const ButtonComp = withExposingConfigs(ButtonTmpComp, [ new NameConfig("text", trans("button.textDesc")), diff --git a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx index e4b5f26d0..5d3f6d77b 100644 --- a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx @@ -10,7 +10,7 @@ import { ChangeEventHandlerControl } from "comps/controls/eventHandlerControl"; import { formDataChildren, FormDataPropertyView } from "./formComp/formDataConstants"; import { PositionControl } from "comps/controls/dropdownControl"; import { useEffect, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { ArrayStringControl } from "comps/controls/codeControl"; import { styleControl } from "comps/controls/styleControl"; import { AnimationStyle, AnimationStyleType, CarouselStyle } from "comps/controls/styleControlConstants"; @@ -56,26 +56,30 @@ let CarouselBasicComp = (function () { setHeight(containerRef.current.clientHeight); } }; + + useResizeDetector({ + targetRef: containerRef, + onResize, + }); + return ( - - props.onEvent("change")} - > - {props.data.map((url, index) => ( - - - - ))} - - + props.onEvent("change")} + > + {props.data.map((url, index) => ( + + + + ))} + ); }) diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx index f30a3f808..c457ba4c0 100644 --- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx @@ -42,6 +42,7 @@ import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUt import { DisabledContext } from "comps/generators/uiCompBuilder"; import { SliderControl } from "@lowcoder-ee/comps/controls/sliderControl"; import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils"; +import React from "react"; const ContainWrapper = styled.div<{ $style: ContainerStyleType & { @@ -212,6 +213,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => { const containerProps = containers[id].children; const noOfColumns = columns.length; return ( + { /> + ) }) } diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx index c7e1201bc..55ac388ca 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx @@ -1,4 +1,4 @@ -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing"; import { Section, sectionNames } from "lowcoder-design"; import { TriContainer } from "../triContainerComp/triContainer"; @@ -216,51 +216,54 @@ export const ContainerBaseComp = (function () { setWidth(container?.clientWidth ?? 0); setHeight(container?.clientHeight ?? 0); }; + + useResizeDetector({ + targetRef: conRef, + onResize, + }); + return ( - - props.onEvent('focus')} - onMouseLeave={() => props.onEvent('blur')} - onClick={() => props.onEvent('click')} - > - { props.onEvent('clickExtra')}>{props.extraTitle}} + props.onEvent('focus')} + onMouseLeave={() => props.onEvent('blur')} + onClick={() => props.onEvent('click')} + > + props.onEvent('clickExtra')}>{props.extraTitle}} - // 内容 - cover={props.cardType == 'common' && props.CoverImg && } - actions={props.cardType == 'common' && props.showActionIcon ? - props.actionOptions.filter(item => !item.hidden).map(item => { - return ( - item.onEvent('click')} - disabled={item.disabled} - $style={props.style} - > - {item.icon} - ) - } - ) : [] + // 内容 + cover={props.cardType == 'common' && props.CoverImg && } + actions={props.cardType == 'common' && props.showActionIcon ? + props.actionOptions.filter(item => !item.hidden).map(item => { + return ( + item.onEvent('click')} + disabled={item.disabled} + $style={props.style} + > + {item.icon} + ) } - > - {props.cardType == 'common' && props.showMeta && } - {props.cardType == 'custom' && - } - + ) : [] } - - + > + {props.cardType == 'common' && props.showMeta && } + {props.cardType == 'custom' && + } + + ); }) .setPropertyViewFn((children) => { diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index 55e287a6f..6e227b971 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -54,7 +54,7 @@ import React, { useRef, useState, } from "react"; -import { useResizeDetector } from "react-resize-detector"; +import { ResizePayload, useResizeDetector } from "react-resize-detector"; import styled from "styled-components"; import { checkIsMobile } from "util/commonUtils"; import { ExternalEditorContext } from "util/context/ExternalEditorContext"; @@ -397,8 +397,18 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { ); const dispatchPositionParamsTimerRef = useRef(0); + + // Add cleanup for timeout + useEffect(() => { + return () => { + if (dispatchPositionParamsTimerRef.current) { + window.clearTimeout(dispatchPositionParamsTimerRef.current); + } + }; + }, []); + const onResize = useCallback( - (width?: number, height?: number) => { + ({width, height}: ResizePayload) => { if(!width || !height) return; if (width !== positionParams.containerWidth) { @@ -444,13 +454,8 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { props.dispatch, ] ); - const setSelectedNames = useCallback( - (names: Set) => { - editorState?.setSelectedCompNames(names); - }, - [editorState?.setSelectedCompNames] - ); + // Cleanup resize detector const { width, ref } = useResizeDetector({ onResize, handleHeight: isRowCountLocked, @@ -458,6 +463,26 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { refreshRate: 100, }); + const setSelectedNames = useCallback( + (names: Set) => { + editorState?.setSelectedCompNames(names); + }, + [editorState?.setSelectedCompNames] + ); + + // Cleanup item references when items are removed + useEffect(() => { + const currentKeys = new Set(Object.keys(props.items)); + const refKeys = new Set(Object.keys(itemViewRef.current)); + + // Remove references to items that no longer exist + refKeys.forEach(key => { + if (!currentKeys.has(key)) { + delete itemViewRef.current[key]; + } + }); + }, [props.items]); + const itemViewRef = useRef({}); const itemViews = useMemo(() => { const newView: GirdItemViewRecord = {}; @@ -497,7 +522,7 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { return ( } className={props.className} style={props.style} scrollContainerRef={props.scrollContainerRef} diff --git a/client/packages/lowcoder/src/comps/comps/formComp/createForm.tsx b/client/packages/lowcoder/src/comps/comps/formComp/createForm.tsx index 864e000d3..70982cda6 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/createForm.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/createForm.tsx @@ -12,7 +12,7 @@ import { TacoButton, } from "lowcoder-design"; import _ from "lodash"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "redux/reducers"; import { fetchDatasourceStructure } from "redux/reduxActions/datasourceActions"; @@ -23,12 +23,14 @@ import { getDataSourceTypeConfig } from "./generate"; import { DataSourceTypeConfig, TableColumn } from "./generate/dataSourceCommon"; import { CompConfig } from "./generate/comp"; import { uiCompRegistry } from "comps/uiCompRegistry"; -import { arrayMove, SortableContainer, SortableElement, SortableHandle } from "react-sortable-hoc"; import { trans } from "i18n"; import log from "loglevel"; import { Datasource } from "@lowcoder-ee/constants/datasourceConstants"; import DataSourceIcon from "components/DataSourceIcon"; import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"; +import { DndContext } from "@dnd-kit/core"; +import { SortableContext, useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; const OpenDialogButton = styled.span` &:hover { @@ -421,20 +423,26 @@ const CustomEditText = (props: { ); }; -const DragHandle = SortableHandle(() => ); - -const SortableItem = SortableElement<{ - item: RowItem, - form: FormInstance, -}>((props: { item: RowItem; form: FormInstance }) => { +const SortableItem = (props: { item: RowItem; form: FormInstance; index: number }) => { const { item, form } = props; const { columnName, columnType, compItems } = item; const disabled = !Form.useWatch(["columns", columnName, "enabled"], form); + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ + id: String(props.index), + }); + return ( - + - + ); -}); +}; -const SortableBody = SortableContainer<{ - items: RowItem[], - form: FormInstance, -}>((props: { items: RowItem[]; form: FormInstance }) => { +const SortableBody = (props: { items: RowItem[]; form: FormInstance }) => { return ( {props.items.map((t, index) => { @@ -498,7 +503,7 @@ const SortableBody = SortableContainer<{ })} ); -}); +}; function getEmptyText(dataSourceNum: number, tableNum: number, columnNum: number): string { if (dataSourceNum === 0) { @@ -547,13 +552,22 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => { const dataSourceId: string | undefined = Form.useWatch("dataSourceId", form); const dataSourceItems = useDataSourceItems(); const dataSourceItem = dataSourceItems.find((t) => t.dataSource.id === dataSourceId); + + // Cleanup form on unmount + useEffect(() => { + return () => { + form.resetFields(); + }; + }, [form]); + // default to the first item useEffect(() => { if (!dataSourceItem) { const id = dataSourceItems.length > 0 ? dataSourceItems[0].dataSource.id : undefined; form.setFieldsValue({ dataSourceId: id }); } - }, [dataSourceItems]); + }, [dataSourceItems, dataSourceItem, form]); + // Refetch when changed const dispatch = useDispatch(); useEffect(() => { @@ -565,23 +579,45 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => { const tableName: string | undefined = Form.useWatch("tableName", form); const tableStructures = useTableStructures(dataSourceId); const tableStructure = tableStructures.find((t) => t.name === tableName); + // default to the first one useEffect(() => { if (!tableStructure) { const name = tableStructures.length > 0 ? tableStructures[0].name : undefined; form.setFieldsValue({ tableName: name }); } - }, [tableStructures]); + }, [tableStructures, tableStructure, form]); + // Columns of the data table, saved to support drag and drop const [items, setItems] = useState([]); const dataSourceTypeConfig = dataSourceItem?.typeConfig; + useEffect(() => { const { initItems, initColumns } = getInitItemsAndColumns(dataSourceTypeConfig, tableStructure); // Set the initial value by the method. Because if another table has the same column name, setting via initialValue is invalid. form.setFieldsValue({ columns: initColumns }); setItems(initItems); - }, [dataSourceTypeConfig, tableStructure]); + }, [dataSourceTypeConfig, tableStructure, form]); + + const handleDragEnd = useCallback((e: { active: { id: string }; over: { id: string } | null }) => { + if (!e.over) { + return; + } + const fromIndex = Number(e.active.id); + const toIndex = Number(e.over.id); + if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) { + return; + } + + const newData = [...items]; + const [movedItem] = newData.splice(fromIndex, 1); + newData.splice(toIndex, 0, movedItem); + + setItems(newData); + }, [items]); + const emptyText = getEmptyText(dataSourceItems.length, tableStructures.length, items.length); + return ( <> @@ -633,16 +669,18 @@ const CreateFormBody = (props: { onCreate: CreateHandler }) => { {trans("formComp.compType")} {trans("formComp.required")} - { - if (oldIndex !== newIndex) { - setItems(arrayMove(items, oldIndex, newIndex)); - } - }} - /> + + String(itemIdx))} + > + + + { export const CreateForm = (props: { onCreate: CreateHandler }) => { const [visible, setVisible] = useState(false); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + setVisible(true); + e.stopPropagation(); + }, []); + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + e.stopPropagation(); + }, []); + + const handleClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + }, []); + + const handleCancel = useCallback(() => { + setVisible(false); + }, []); + return ( <> - { - setVisible(true); - e.stopPropagation(); - }} - > + {trans("formComp.openDialogButton")} e.stopPropagation()} - onMouseDown={(e) => e.stopPropagation()} - onClick={(e) => e.stopPropagation()} + onKeyDown={handleKeyDown} + onMouseDown={handleMouseDown} + onClick={handleClick} > setVisible(false)} + onCancel={handleCancel} width="600px" children={} styles={{ body: {padding: 0} }} diff --git a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx index 7ee8106b3..e0fce300c 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx @@ -169,14 +169,18 @@ function onCreate( const BodyPlaceholder = (props: FormProps) => { const editorState = useContext(EditorContext); const formName = useContext(CompNameContext); + + const handleCreate = (data: CreateData) => { + const result = onCreate(data, props, editorState, formName); + return Promise.resolve(result); + }; + return ( {trans("formComp.containerPlaceholder")} - Promise.resolve(onCreate(data, props, editorState, formName)) - } + onCreate={handleCreate} /> ); diff --git a/client/packages/lowcoder/src/comps/comps/formComp/formDataConstants.tsx b/client/packages/lowcoder/src/comps/comps/formComp/formDataConstants.tsx index fae531ae0..dbc23e845 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/formDataConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/formDataConstants.tsx @@ -2,7 +2,7 @@ import { RecordConstructorToComp } from "lowcoder-core"; import { StringControl } from "comps/controls/codeControl"; import { CompNameContext, EditorContext } from "comps/editorState"; import { Section } from "lowcoder-design"; -import { ReactNode } from "react"; +import { ReactNode, useContext, useMemo } from "react"; import { trans } from "i18n"; export interface IForm { @@ -17,24 +17,26 @@ export const formDataChildren = { type FormDataComp = RecordConstructorToComp; -export const FormDataPropertyView = (children: FormDataComp) => ( - - {(editorState) => ( - - {(name) => ( - <> - {editorState?.findUIParentContainer(name, "form") && ( - - {children.formDataKey.propertyView({ - label: trans("formComp.name"), - placeholder: name, - tooltip: trans("formComp.nameTooltip"), - })} - - )} - > - )} - - )} - -); +export const FormDataPropertyView = (children: FormDataComp) => { + const editorState = useContext(EditorContext); + const name = useContext(CompNameContext); + + const isFormParent = useMemo(() => + editorState?.findUIParentContainer(name, "form"), + [editorState, name] + ); + + if (!isFormParent) { + return null; + } + + return ( + + {children.formDataKey.propertyView({ + label: trans("formComp.name"), + placeholder: name, + tooltip: trans("formComp.nameTooltip"), + })} + + ); +}; diff --git a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx index 4b0f769aa..efe3245b1 100644 --- a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx +++ b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx @@ -1,6 +1,6 @@ import { EditorContext } from "comps/editorState"; import { EditorContainer } from "pages/common/styledComponent"; -import React, { Profiler, useContext, useMemo, useRef, useState } from "react"; +import React, { Profiler, useContext, useMemo, useRef, useState, useEffect, useCallback } from "react"; import styled from "styled-components"; import { profilerCallback } from "util/cacheUtils"; import { @@ -97,11 +97,44 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => { const isDefaultTheme = useContext(ThemeContext)?.themeId === 'default-theme-id'; const isPreviewTheme = useContext(ThemeContext)?.themeId === 'preview-theme'; const editorState = useContext(EditorContext); - const [dragSelectedComps, setDragSelectedComp] = useState(EmptySet); - const scrollContainerRef = useRef(null); + const [dragSelectedComps, setDragSelectedComp] = useState>(new Set()); + const scrollContainerRef = useRef(null); + const mountedRef = useRef(true); const appSettings = editorState.getAppSettings(); const maxWidthFromHook = useMaxWidth(); + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setDragSelectedComp(new Set()); + }; + }, []); + + // Memoized drag selection handler + const handleDragSelection = useCallback((checkSelectFunc?: CheckSelectFn) => { + if (!mountedRef.current) return new Set(); + + const selectedComps = new Set(); + if (checkSelectFunc) { + Object.values(props.layout).forEach((layoutItem) => { + const key = layoutItem.i; + if (props.items.hasOwnProperty(key)) { + const item = props.items[key]; + const name = item.name; + const element = document.getElementById(key); + if (element) { + checkSelectFunc( + element as HTMLDivElement, + (result) => (result ? selectedComps.add(name) : selectedComps.delete(name)) + ); + } + } + }); + } + return selectedComps; + }, [props.items, props.layout]); + const maxWidth = useMemo( () => appSettings.maxWidth ?? maxWidthFromHook, [appSettings, maxWidthFromHook] @@ -244,6 +277,25 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => { rowHeight: parseInt(defaultRowHeight), }), [props.positionParams, defaultGrid, defaultRowHeight]); + // Memoized mouse event handlers + const handleMouseDown = useCallback(() => { + setDragSelectedComp(new Set()); + }, []); + + const handleMouseUp = useCallback(() => { + if (mountedRef.current) { + editorState.setSelectedCompNames(dragSelectedComps); + setDragSelectedComp(new Set()); + } + }, [editorState, dragSelectedComps]); + + const handleMouseMove = useCallback((checkSelectFunc: CheckSelectFn) => { + if (mountedRef.current) { + const selectedName = handleDragSelection(checkSelectFunc); + setDragSelectedComp(selectedName); + } + }, [handleDragSelection]); + if (readOnly) { return ( { $bgImagePosition={bgImagePosition} > { - setDragSelectedComp(EmptySet); - }} - onMouseUp={() => { - editorState.setSelectedCompNames(dragSelectedComps); - setDragSelectedComp(EmptySet); - }} - onMouseMove={(checkSelectFunc) => { - const selectedName = getDragSelectedNames(props.items, props.layout, checkSelectFunc); - setDragSelectedComp(selectedName); - }} + onMouseDown={handleMouseDown} + onMouseUp={handleMouseUp} + onMouseMove={handleMouseMove} > { positionParams={positionParams} emptyRows={defaultRowCount} minHeight={defaultMinHeight} - extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined } + extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined} /> diff --git a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/dragSelector.tsx b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/dragSelector.tsx index 79e017302..bbbd8c26b 100644 --- a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/dragSelector.tsx +++ b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/dragSelector.tsx @@ -1,5 +1,5 @@ import { Layers } from "constants/Layers"; -import React, { ReactNode } from "react"; +import React, { ReactNode, useCallback, useRef, useState, useEffect } from "react"; export type CheckSelectFn = ( item?: HTMLDivElement | null, @@ -29,154 +29,154 @@ interface SectionState { mouseDown: boolean; selectionBox?: Rect; startPoint?: Point; + appendMode: boolean; } -const InitialState = { +const createInitialState = (): SectionState => ({ mouseDown: false, appendMode: false, selectionBox: undefined, startPoint: undefined, -}; - -class DragSelectorComp extends React.Component { - private readonly selectAreaRef: React.RefObject; - - constructor(props: SectionProps) { - super(props); - this.selectAreaRef = React.createRef(); - this.state = InitialState; - this._onMouseMove = this._onMouseMove.bind(this); - this._onMouseUp = this._onMouseUp.bind(this); - } - - _onMouseDown(e: React.MouseEvent) { - if (e.button === 2 || e.nativeEvent.which === 2) { - return; - } - let nextState: SectionState = { mouseDown: false }; - nextState.mouseDown = true; - nextState.startPoint = { - x: e.pageX - (this.selectAreaRef.current?.getBoundingClientRect().left ?? 0), - y: e.pageY - (this.selectAreaRef.current?.getBoundingClientRect().top ?? 0), +}); + +export const DragSelector = React.memo((props: SectionProps) => { + const selectAreaRef = useRef(null); + const [state, setState] = useState(createInitialState()); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + // Clean up any remaining event listeners + window.document.removeEventListener("mousemove", handleMouseMove); + window.document.removeEventListener("mouseup", handleMouseUp); + }; + }, []); + + const handleMouseMove = useCallback((e: MouseEvent) => { + if (!mountedRef.current || !state.mouseDown) return; + + const endPoint = { + x: e.pageX - (selectAreaRef.current?.getBoundingClientRect().left ?? 0), + y: e.pageY - (selectAreaRef.current?.getBoundingClientRect().top ?? 0), }; - this.setState(nextState); - window.document.addEventListener("mousemove", this._onMouseMove); - window.document.addEventListener("mouseup", this._onMouseUp); - this.props.onMouseDown(); - } - - _onMouseUp() { - window.document.removeEventListener("mousemove", this._onMouseMove); - window.document.removeEventListener("mouseup", this._onMouseUp); - this.props.onMouseUp(); - this.setState(InitialState); - } - - _onMouseMove(e: MouseEvent) { - if (this.state.mouseDown) { - let endPoint = { - x: e.pageX - (this.selectAreaRef.current?.getBoundingClientRect().left ?? 0), - y: e.pageY - (this.selectAreaRef.current?.getBoundingClientRect().top ?? 0), - }; - this.setState({ - selectionBox: this._calculateSelectionBox(this.state.startPoint, endPoint), - }); + + setState(prevState => ({ + ...prevState, + selectionBox: calculateSelectionBox(prevState.startPoint, endPoint), + })); + + // Clean up selection properly + const selection = window.getSelection(); + if (selection) { + selection.removeAllRanges(); } - // Disable selection of text during mouse movement - var selection = window.getSelection(); - selection!.removeAllRanges(); - selection = null; - this.props.onMouseMove(this.childrenViewCheckFunc); - } - - rectIntersect = ( + + props.onMouseMove(childrenViewCheckFunc); + }, [state.mouseDown, state.startPoint, props.onMouseMove]); + + const handleMouseUp = useCallback(() => { + if (!mountedRef.current) return; + + window.document.removeEventListener("mousemove", handleMouseMove); + window.document.removeEventListener("mouseup", handleMouseUp); + props.onMouseUp(); + setState(createInitialState()); + }, [handleMouseMove, props.onMouseUp]); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + if (!mountedRef.current || e.button === 2 || e.nativeEvent.which === 2) return; + + const startPoint = { + x: e.pageX - (selectAreaRef.current?.getBoundingClientRect().left ?? 0), + y: e.pageY - (selectAreaRef.current?.getBoundingClientRect().top ?? 0), + }; + + setState({ + mouseDown: true, + startPoint, + selectionBox: undefined, + appendMode: false, + }); + + window.document.addEventListener("mousemove", handleMouseMove); + window.document.addEventListener("mouseup", handleMouseUp); + props.onMouseDown(); + }, [handleMouseMove, handleMouseUp, props.onMouseDown]); + + const rectIntersect = useCallback(( selectionBox: Rect | undefined, item: HTMLElement | null | undefined ): boolean => { - if (!selectionBox || !item) { - return false; - } + if (!selectionBox || !item || !selectAreaRef.current) return false; + + const containerRect = selectAreaRef.current.getBoundingClientRect(); const itemBox = { - top: - item.getBoundingClientRect().top - - (this.selectAreaRef.current?.getBoundingClientRect().top ?? 0), - left: - item.getBoundingClientRect().left - - (this.selectAreaRef.current?.getBoundingClientRect().left ?? 0), + top: item.getBoundingClientRect().top - containerRect.top, + left: item.getBoundingClientRect().left - containerRect.left, width: item.getBoundingClientRect().width, height: item.getBoundingClientRect().height, }; + return ( selectionBox.left <= itemBox.left + itemBox.width && selectionBox.left + selectionBox.width >= itemBox.left && selectionBox.top <= itemBox.top + itemBox.height && selectionBox.top + selectionBox.height >= itemBox.top ); - }; + }, []); - childrenViewCheckFunc = ( + const childrenViewCheckFunc = useCallback(( item?: HTMLDivElement | null, afterCheck?: (checkResult: boolean) => void ) => { - const result = this.rectIntersect(this.state.selectionBox, item); - if (!!afterCheck) { + const result = rectIntersect(state.selectionBox, item); + if (afterCheck) { afterCheck(result); } return result; - }; + }, [state.selectionBox, rectIntersect]); - render() { - return ( - - {this.props.children} - {this.renderSelectionBox()} - - ); - } - - renderSelectionBox() { - if ( - !this.state.mouseDown || - !this.state.startPoint || - !this.state.selectionBox || - !this.selectAreaRef.current - ) { + const calculateSelectionBox = useCallback((startPoint: Point | undefined, endPoint: Point) => { + if (!state.mouseDown || !startPoint || !endPoint) return undefined; + + return { + left: Math.min(startPoint.x, endPoint.x), + top: Math.min(startPoint.y, endPoint.y), + width: Math.abs(startPoint.x - endPoint.x), + height: Math.abs(startPoint.y - endPoint.y), + }; + }, [state.mouseDown]); + + const renderSelectionBox = useCallback(() => { + if (!state.mouseDown || !state.startPoint || !state.selectionBox || !selectAreaRef.current) { return null; } + return ( ); - } - - _calculateSelectionBox(startPoint: Point | undefined, endPoint: Point) { - if (!this.state.mouseDown || !startPoint || !endPoint) { - return undefined; - } - let left = Math.min(startPoint.x, endPoint.x); - let top = Math.min(startPoint.y, endPoint.y); - let width = Math.abs(startPoint.x - endPoint.x); - let height = Math.abs(startPoint.y - endPoint.y); - return { - left: left, - top: top, - width: width, - height: height, - }; - } -} - -export const DragSelector = React.memo(DragSelectorComp); + }, [state.mouseDown, state.startPoint, state.selectionBox]); + + return ( + + {props.children} + {renderSelectionBox()} + + ); +}); diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx index 9a8eb1790..8e57b9307 100644 --- a/client/packages/lowcoder/src/comps/comps/iconComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx @@ -22,7 +22,7 @@ import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps import { trans } from "i18n"; import { NumberControl } from "comps/controls/codeControl"; import { IconControl } from "comps/controls/iconControl"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { AutoHeightControl } from "../controls/autoHeightControl"; import { clickEvent, @@ -96,30 +96,29 @@ const IconView = (props: RecordConstructorToView) => { setHeight(container?.clientHeight ?? 0); }; + useResizeDetector({ + targetRef: conRef, + onResize, + }); + return ( - ( - props.onEvent("click")} - > - { props.sourceMode === 'standard' - ? props.icon - : - } - - )} + props.onEvent("click")} > - + { props.sourceMode === 'standard' + ? (props.icon || '') + : + } + ); }; diff --git a/client/packages/lowcoder/src/comps/comps/imageComp.tsx b/client/packages/lowcoder/src/comps/comps/imageComp.tsx index 1806399e2..0bc53a519 100644 --- a/client/packages/lowcoder/src/comps/comps/imageComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/imageComp.tsx @@ -14,7 +14,7 @@ import { import { RecordConstructorToView } from "lowcoder-core"; import { ReactElement, useEffect, useRef, useState } from "react"; import _ from "lodash"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { styleControl } from "comps/controls/styleControl"; import { AnimationStyle, @@ -177,45 +177,44 @@ const ContainerImg = (props: RecordConstructorToView) => { } }; + useResizeDetector({ + targetRef: conRef, + onResize, + }); + return ( - ( - - - props.onEvent("click")} - /> - - - )} + - + + props.onEvent("click")} + /> + + ); }; diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx index 26805d998..73e33ae2d 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx @@ -5,7 +5,7 @@ import _, { findIndex } from "lodash"; import { ConstructorToView, deferAction } from "lowcoder-core"; import { DragIcon, HintPlaceHolder, ScrollBar, pageItemRender } from "lowcoder-design"; import { RefObject, useContext, createContext, useMemo, useRef, useEffect } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { ResizePayload, useResizeDetector } from "react-resize-detector"; import styled from "styled-components"; import { checkIsMobile } from "util/commonUtils"; import { useDelayState } from "util/hooks"; @@ -372,41 +372,41 @@ export function ListView(props: Props) { children.onEvent.getView()('sortChange'); }; + useResizeDetector({ + targetRef: ref, + onResize: ({width, height}: ResizePayload) => { + if (height) setListHeight(height); + }, + observerOptions: { box: "border-box" }, + }); + // log.debug("renders: ", renders); + return ( - { - if (height) setListHeight(height); - }} - observerOptions={{ box: "border-box" }} - render={() => ( - 1} - $autoHeight={autoHeight} - > - {!enableSorting - ? renders - : ( - - String(colIdx)) - } - > - {renders} - - - ) - } - - )} + 1} + $autoHeight={autoHeight} > - + {!enableSorting + ? renders + : ( + + String(colIdx)) + } + > + {renders} + + + ) + } + diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx index 0e31deb50..132e1715e 100644 --- a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx +++ b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx @@ -36,7 +36,7 @@ import { widthCalculator, } from "comps/controls/styleControlConstants"; import { useEffect, useRef, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import { useResizeDetector } from "react-resize-detector"; import { useContext } from "react"; import { AssetType, IconscoutControl } from "@lowcoder-ee/comps/controls/iconscoutControl"; @@ -241,69 +241,68 @@ let ButtonTmpComp = (function () { setStyle(container?.clientHeight + "px", container?.clientWidth + "px"); }; + useResizeDetector({ + targetRef: conRef, + onResize, + }); + return ( {(editorState) => ( - ( - - - + + - isDefault(props.type) - ? props.onEvent("click") - : submitForm(editorState, props.form) + : { + aspectRatio: props.aspectRatio, + borderRadius: props.style.radius, } + } + disabled={ + props.disabled || + (!isDefault(props.type) && + getForm(editorState, props.form)?.disableSubmit()) + } + onClick={() => + isDefault(props.type) + ? props.onEvent("click") + : submitForm(editorState, props.form) + } + > + {props.sourceMode === 'standard' && props.prefixIcon && ( + - {props.sourceMode === 'standard' && props.prefixIcon && ( - - {props.prefixIcon} - - )} - {props.sourceMode === 'asset-library' && props.iconScoutAsset && ( - - - - )} - - - - )} - > - + {props.prefixIcon} + + )} + {props.sourceMode === 'asset-library' && props.iconScoutAsset && ( + + {Boolean(props.iconScoutAsset.value) && } + + )} + + + )} ); diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx index 68ca7d370..8718b1dc3 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx @@ -21,7 +21,7 @@ import { withExposingConfigs, } from "comps/generators/withExposing"; import { Section, sectionNames, ValueFromOption } from "lowcoder-design"; -import { ReactNode, useEffect, useRef, useState } from "react"; +import { ReactNode, useEffect, useRef, useState, useCallback } from "react"; import styled, { css } from "styled-components"; import { RecordConstructorToView } from "lowcoder-core"; import { InputEventHandlerControl } from "../../controls/eventHandlerControl"; @@ -280,8 +280,18 @@ const childrenMap = { const CustomInputNumber = (props: RecordConstructorToView) => { const ref = useRef(null); const defaultValue = props.defaultValue.value; + const mountedRef = useRef(true); + // Cleanup on unmount useEffect(() => { + return () => { + mountedRef.current = false; + ref.current = null; + }; + }, []); + + useEffect(() => { + if (!mountedRef.current) return; let value = 0; if (defaultValue === 'null' && props.allowNull) { value = NaN; @@ -296,16 +306,18 @@ const CustomInputNumber = (props: RecordConstructorToView) = const [tmpValue, setTmpValue] = useState(formatFn(props.value.value)); - const handleFinish = () => { + const handleFinish = useCallback(() => { + if (!mountedRef.current) return; const oldValue = props.value.value; const newValue = parseNumber(tmpValue, props.allowNull); props.value.onChange(newValue); if((oldValue !== newValue)) { props.onEvent("change"); } - }; + }, [tmpValue, props.allowNull, props.value, props.onEvent]); useEffect(() => { + if (!mountedRef.current) return; setTmpValue(formatFn(props.value.value)); }, [ props.value.value, @@ -315,9 +327,49 @@ const CustomInputNumber = (props: RecordConstructorToView) = props.thousandsSeparator, ]); + const handleChangeCapture = useCallback((e: any) => { + if (!mountedRef.current) return; + setTmpValue((e.target.value?.toString() ?? "").replace("。", ".")); + }, []); + + const handleStep = useCallback((_: any, info: any) => { + if (!mountedRef.current) return; + const v = NP.plus( + parseNumber(tmpValue), + NP.times(info.type === "up" ? 1 : -1, Number(info.offset)) + ); + props.value.onChange(v); + props.onEvent("change"); + }, [tmpValue, props.value, props.onEvent]); + + const handleKeyPress = useCallback((event: React.KeyboardEvent) => { + if (!mountedRef.current) return; + const value = tmpValue; + const cursor = ref.current?.selectionStart; + if (/\d/.test(event.key)) { + return; + } + if (cursor === 0 && event.key === "-" && !/-/.test(value)) { + return; + } + if (cursor !== 0 && props.thousandsSeparator && event.key === ",") { + return; + } + if ( + cursor !== 0 && + props.precision > 0 && + (event.key === "." || event.key === "。") && + !/[.]/.test(value) + ) { + return; + } + event.preventDefault(); + }, [tmpValue, props.thousandsSeparator, props.precision]); + return ( { + if (!mountedRef.current) return; props.viewRef(input); ref.current = input; }} @@ -336,49 +388,17 @@ const CustomInputNumber = (props: RecordConstructorToView) = handleFinish(); props.onEvent("submit"); }} - onChangeCapture={(e: any) => { - // eslint-disable-next-line only-ascii/only-ascii - setTmpValue((e.target.value?.toString() ?? "").replace("。", ".")); - }} - onStep={(_, info) => { - // since percentage mode needs to be handled manually - const v = NP.plus( - parseNumber(tmpValue), - NP.times(info.type === "up" ? 1 : -1, Number(info.offset)) - ); - props.value.onChange(v); - props.onEvent("change"); - }} + onChangeCapture={handleChangeCapture} + onStep={handleStep} onFocus={() => { + if (!mountedRef.current) return; props.onEvent("focus"); }} onBlur={() => { handleFinish(); props.onEvent("blur"); }} - onKeyPress={(event) => { - const value = tmpValue; - const cursor = ref.current?.selectionStart; - if (/\d/.test(event.key)) { - return; - } - if (cursor === 0 && event.key === "-" && !/-/.test(value)) { - return; - } - if (cursor !== 0 && props.thousandsSeparator && event.key === ",") { - return; - } - if ( - cursor !== 0 && - props.precision > 0 && - // eslint-disable-next-line only-ascii/only-ascii - (event.key === "." || event.key === "。") && - !/[.]/.test(value) - ) { - return; - } - event.preventDefault(); - }} + onKeyPress={handleKeyPress} /> ); }; diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx index cbc921508..b2b42ec20 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx @@ -7,6 +7,7 @@ import { SliderChildren, SliderPropertyView, SliderStyled, SliderWrapper } from import { hasIcon } from "comps/utils"; import { BoolControl } from "comps/controls/boolControl"; import { NumberControl } from "comps/controls/codeControl"; +import { useCallback, useRef, useEffect } from "react"; const RangeSliderBasicComp = (function () { const childrenMap = { @@ -16,19 +17,47 @@ const RangeSliderBasicComp = (function () { vertical: BoolControl, tabIndex: NumberControl, }; + return new UICompBuilder(childrenMap, (props, dispatch) => { + const mountedRef = useRef(true); + const startValueRef = useRef(props.start.value); + const endValueRef = useRef(props.end.value); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + startValueRef.current = 0; + endValueRef.current = 0; + }; + }, []); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + return false; + }, []); + + const handleChange = useCallback((value: number | number[]) => { + if (!mountedRef.current) return; + if (Array.isArray(value)) { + const [start, end] = value; + startValueRef.current = start; + endValueRef.current = end; + props.start.onChange(start); + props.end.onChange(end); + props.onEvent("change"); + } + }, [props.start, props.end, props.onEvent]); + return props.label({ style: props.style, labelStyle: props.labelStyle, - inputFieldStyle:props.inputFieldStyle, - animationStyle:props.animationStyle, + inputFieldStyle: props.inputFieldStyle, + animationStyle: props.animationStyle, children: ( { - e.stopPropagation(); - return false; - }} + onMouseDown={handleMouseDown} > {hasIcon(props.prefixIcon) && props.prefixIcon} { - props.start.onChange(start); - props.end.onChange(end); - props.onEvent("change"); - }} + onChange={handleChange} /> {hasIcon(props.suffixIcon) && props.suffixIcon} diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx index acadbb733..01a7d93b4 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx @@ -8,6 +8,7 @@ import { SliderChildren, SliderPropertyView, SliderStyled, SliderWrapper } from import { hasIcon } from "comps/utils"; import { BoolControl } from "comps/controls/boolControl"; import { NumberControl } from "comps/controls/codeControl"; +import { useCallback } from "react"; const SliderBasicComp = (function () { /** @@ -21,18 +22,24 @@ const SliderBasicComp = (function () { ...formDataChildren, }; return new UICompBuilder(childrenMap, (props) => { + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + }, []); + + const handleChange = useCallback((value: number) => { + props.value.onChange(value); + props.onEvent("change"); + }, [props.value, props.onEvent]); + return props.label({ style: props.style, labelStyle: props.labelStyle, - inputFieldStyle:props.inputFieldStyle, - animationStyle:props.animationStyle, + inputFieldStyle: props.inputFieldStyle, + animationStyle: props.animationStyle, children: ( { - e.stopPropagation(); - return false; - }} + onMouseDown={handleMouseDown} > {hasIcon(props.prefixIcon) && props.prefixIcon} { - props.value.onChange(e); - props.onEvent("change"); - }} + onChange={handleChange} /> {hasIcon(props.suffixIcon) && props.suffixIcon} diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx index 8750d2e0c..03b3c8673 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx @@ -12,8 +12,7 @@ import { darkenColor, fadeColor } from "lowcoder-design"; import { disabledPropertyView, hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils"; import { IconControl } from "comps/controls/iconControl"; import { trans } from "i18n"; - -import { useContext } from "react"; +import { memo, useCallback, useContext } from "react"; import { EditorContext } from "comps/editorState"; const getStyle = (style: SliderStyleType, vertical: boolean) => { @@ -92,42 +91,60 @@ export const SliderChildren = { animationStyle: styleControl(AnimationStyle, 'animationStyle') }; -export const SliderPropertyView = ( - children: RecordConstructorToComp -) => ( - <> +const InteractionSection = memo(({ children }: { children: RecordConstructorToComp }) => { + const editorModeStatus = useContext(EditorContext).editorModeStatus; + + if (!["logic", "both"].includes(editorModeStatus)) { + return null; + } - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( - - {children.onEvent.getPropertyView()} - {disabledPropertyView(children)} - {hiddenPropertyView(children)} - {showDataLoadingIndicatorsPropertyView(children as any)} - - )} + return ( + + {children.onEvent.getPropertyView()} + {disabledPropertyView(children)} + {hiddenPropertyView(children)} + {showDataLoadingIndicatorsPropertyView(children as any)} + + ); +}); - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( - children.label.getPropertyView() - )} +const LayoutSection = memo(({ children }: { children: RecordConstructorToComp }) => { + const editorModeStatus = useContext(EditorContext).editorModeStatus; + + if (!["layout", "both"].includes(editorModeStatus)) { + return null; + } - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( - <> - {children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })} - {children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })} - - - {children.style.getPropertyView()} - - - {children.labelStyle.getPropertyView()} - - - {children.inputFieldStyle.getPropertyView()} - - - {children.animationStyle.getPropertyView()} - - > - )} + return ( + <> + {children.label.getPropertyView()} + + {children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })} + {children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })} + + + {children.style.getPropertyView()} + + + {children.labelStyle.getPropertyView()} + + + {children.inputFieldStyle.getPropertyView()} + + + {children.animationStyle.getPropertyView()} + + > + ); +}); + +export const SliderPropertyView = memo(( + children: RecordConstructorToComp +) => ( + <> + + > -); +)); + +SliderPropertyView.displayName = 'SliderPropertyView'; diff --git a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx index f549dadf3..64ec62a95 100644 --- a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx @@ -8,7 +8,7 @@ import { UICompBuilder, withDefault } from "comps/generators"; import { NameConfig, NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing"; import { Section, sectionNames } from "lowcoder-design"; import React, { ChangeEvent, Suspense, useEffect, useRef, useState } from "react"; -import type ReactQuill from "react-quill"; +import type ReactQuill from "react-quill-new"; import { useDebounce } from "react-use"; import styled, { css } from "styled-components"; import { formDataChildren, FormDataPropertyView } from "./formComp/formDataConstants"; @@ -200,7 +200,7 @@ interface IProps { tabIndex?: number; } -const ReactQuillEditor = React.lazy(() => import("react-quill")); +const ReactQuillEditor = React.lazy(() => import("react-quill-new")); function RichTextEditor(props: IProps) { const [key, setKey] = useState(0); diff --git a/client/packages/lowcoder/src/comps/comps/rootComp.tsx b/client/packages/lowcoder/src/comps/comps/rootComp.tsx index edf1f3a34..58ef58d15 100644 --- a/client/packages/lowcoder/src/comps/comps/rootComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/rootComp.tsx @@ -8,7 +8,7 @@ import { HookListComp } from "comps/hooks/hookListComp"; import { QueryListComp } from "comps/queries/queryComp"; import { NameAndExposingInfo } from "comps/utils/exposingTypes"; import { handlePromiseAndDispatch } from "util/promiseUtils"; -import { HTMLAttributes, Suspense, lazy, useContext, useEffect, useMemo, useState } from "react"; +import { HTMLAttributes, Suspense, lazy, useContext, useEffect, useMemo, useRef, useState } from "react"; import { setFieldsNoTypeCheck } from "util/objectUtils"; import { AppSettingsComp } from "./appSettingsComp"; import { PreloadComp } from "./preLoadComp"; @@ -36,6 +36,8 @@ import React from "react"; import { isEqual } from "lodash"; import {LoadingBarHideTrigger} from "@lowcoder-ee/util/hideLoading"; import clsx from "clsx"; +import { useUnmount } from "react-use"; + const EditorView = lazy( () => import("pages/editor/editorView"), ); @@ -65,6 +67,10 @@ const RootView = React.memo((props: RootViewProps) => { const [propertySectionState, setPropertySectionState] = useState({}); const { readOnly } = useContext(ExternalEditorContext); const isUserViewMode = useUserViewMode(); + const mountedRef = useRef(true); + const editorStateRef = useRef(); + const prevCompRef = useRef(comp); + const appThemeId = comp.children.settings.getView().themeId; const { orgCommonSettings } = getGlobalSettings(); const themeList = orgCommonSettings?.themeList || []; @@ -80,27 +86,54 @@ const RootView = React.memo((props: RootViewProps) => { ); useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + useEffect(() => { + if (!mountedRef.current) return; + const newEditorState = new EditorState(comp, (changeEditorStateFn) => { - setEditorState((oldState) => (oldState ? changeEditorStateFn(oldState) : undefined)); + if (mountedRef.current) { + setEditorState((oldState) => { + return (oldState ? changeEditorStateFn(oldState) : undefined) + }); + } }); + editorStateRef.current = newEditorState; setEditorState(newEditorState); + + return () => { + if (editorStateRef.current) { + editorStateRef.current = undefined; + } + }; }, []); useEffect(() => { - if (editorState != null) { + if (!mountedRef.current || !editorState) return; + + if (prevCompRef.current !== comp) { editorState.setComp(() => comp); + prevCompRef.current = comp; } - }, [comp]); + }, [comp, editorState]); + + useUnmount(() => { + setEditorState(undefined); + setPropertySectionState({}); + if (editorStateRef.current) { + editorStateRef.current = undefined; + } + }); - /** - * ensure memo, otherwise all component caches will be invalid - */ const themeContextValue = useMemo( () => ({ theme, themeId, }), - [theme] + [theme, themeId] ); const propertySectionContextValue = useMemo(() => { @@ -109,6 +142,8 @@ const RootView = React.memo((props: RootViewProps) => { compName, state: propertySectionState, toggle: (compName: string, sectionName: string) => { + if (!mountedRef.current) return; + setPropertySectionState((oldState) => { const nextSectionState: PropertySectionState = { ...oldState }; const compState = nextSectionState[compName] || {}; @@ -164,6 +199,7 @@ export class RootComp extends RootCompBase { preloaded = false; preloadId = ""; isModuleRoot = false; + private editorStateRef?: EditorState; getView() { if (!this.preloaded) { @@ -174,6 +210,7 @@ export class RootComp extends RootCompBase { clearPreload() { this.children.preload.clear(); + this.editorStateRef = undefined; } setModuleRoot(moduleRoot: boolean) { @@ -184,8 +221,13 @@ export class RootComp extends RootCompBase { if (this.preloaded) { return this; } - await this.children.preload.run(id); - return setFieldsNoTypeCheck(this, { preloaded: true, preloadId: id }); + try { + await this.children.preload.run(id); + return setFieldsNoTypeCheck(this, { preloaded: true, preloadId: id }); + } catch (error) { + this.clearPreload(); + throw error; + } } private findInternalComp(name: string) { diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx index d6eb38737..72cae1493 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/checkboxComp.tsx @@ -24,6 +24,7 @@ import { trans } from "i18n"; import { RefControl } from "comps/controls/refControl"; import { migrateOldData } from "comps/generators/simpleGenerators"; import { fixOldInputCompData } from "../textInputComp/textInputConstants"; +import { useCallback, useRef, useEffect, memo } from "react"; export const getStyle = (style: CheckboxStyleType) => { return css` @@ -140,6 +141,54 @@ const CheckboxGroup = styled(AntdCheckboxGroup) <{ }} `; +const MemoizedCheckboxGroup = memo(({ + value, + disabled, + style, + layout, + options, + onChange, + viewRef, + tabIndex +}: { + value: string[]; + disabled: boolean; + style: CheckboxStyleType; + layout: ValueFromOption; + options: Array<{ label: string; value: string; disabled?: boolean }>; + onChange: (values: string[]) => void; + viewRef: React.Ref; + tabIndex?: number +}) => { + const mountedRef = useRef(true); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((values: unknown[]) => { + if (!mountedRef.current) return; + onChange(values as string[]); + }, [onChange]); + + return ( + + ); +}); + +MemoizedCheckboxGroup.displayName = 'MemoizedCheckboxGroup'; + let CheckboxBasicComp = (function () { const childrenMap = { defaultValue: arrayStringExposingStateControl("defaultValue"), @@ -161,35 +210,48 @@ let CheckboxBasicComp = (function () { ...SelectInputValidationChildren, ...formDataChildren, }; + return new UICompBuilder(childrenMap, (props) => { - const [ - validateState, - handleChange, - ] = useSelectInputValidate(props); + const mountedRef = useRef(true); + const [validateState, handleChange] = useSelectInputValidate(props); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleValidateChange = useCallback((values: string[]) => { + if (!mountedRef.current) return; + handleChange(values); + }, [handleChange]); + + const filteredOptions = useCallback(() => { + return props.options + .filter((option) => option.value !== undefined && !option.hidden) + .map((option) => ({ + label: option.label, + value: option.value, + disabled: option.disabled, + })); + }, [props.options]); + return props.label({ required: props.required, style: props.style, labelStyle: props.labelStyle, - inputFieldStyle:props.inputFieldStyle, - animationStyle:props.animationStyle, + inputFieldStyle: props.inputFieldStyle, + animationStyle: props.animationStyle, children: ( - option.value !== undefined && !option.hidden) - .map((option) => ({ - label: option.label, - value: option.value, - disabled: option.disabled, - }))} + disabled={props.disabled} + style={props.inputFieldStyle} + layout={props.layout} + options={filteredOptions()} + onChange={handleValidateChange} + viewRef={props.viewRef} tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} - onChange={(values) => { - handleChange(values as string[]); - }} /> ), ...validateState, diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx index 742dedb35..97a490fe9 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioComp.tsx @@ -13,7 +13,7 @@ import { EllipsisTextCss, ValueFromOption } from "lowcoder-design"; import { trans } from "i18n"; import { fixOldInputCompData } from "../textInputComp/textInputConstants"; import { migrateOldData } from "comps/generators/simpleGenerators"; -import { useEffect, useRef } from "react"; +import { useCallback, useRef, useEffect, memo } from "react"; const getStyle = (style: RadioStyleType, inputFieldStyle?:RadioStyleType ) => { return css` @@ -97,16 +97,72 @@ const Radio = styled(AntdRadioGroup)<{ }} `; +const MemoizedRadio = memo(({ + value, + disabled, + style, + inputFieldStyle, + layout, + options, + onChange, + viewRef +}: { + value: string; + disabled: boolean; + style: RadioStyleType; + inputFieldStyle: RadioStyleType; + layout: ValueFromOption; + options: Array<{ label: string; value: string; disabled?: boolean; hidden?: boolean }>; + onChange: (value: string) => void; + viewRef: React.Ref; +}) => { + const mountedRef = useRef(true); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((e: any) => { + if (!mountedRef.current) return; + onChange(e.target.value); + }, [onChange]); + + const filteredOptions = useCallback(() => { + return options + .filter((option) => option.value !== undefined && !option.hidden) + .map((option) => ({ + label: option.label, + value: option.value, + disabled: option.disabled, + })); + }, [options]); + + return ( + + ); +}); + +MemoizedRadio.displayName = 'MemoizedRadio'; + let RadioBasicComp = (function () { return new UICompBuilder(RadioChildrenMap, (props) => { - const [ - validateState, - handleChange, - ] = useSelectInputValidate(props); - + const mountedRef = useRef(true); const radioRef = useRef(null); - + const [validateState, handleChange] = useSelectInputValidate(props); + useEffect(() => { + if (!mountedRef.current) return; if (radioRef.current && typeof props.tabIndex === 'number') { const firstRadioInput = radioRef.current.querySelector('input[type="radio"]'); if (firstRadioInput) { @@ -114,36 +170,40 @@ let RadioBasicComp = (function () { } } }, [props.tabIndex, props.options]); - + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleValidateChange = useCallback((value: string) => { + if (!mountedRef.current) return; + handleChange(value); + }, [handleChange]); + return props.label({ required: props.required, style: props.style, labelStyle: props.labelStyle, - inputFieldStyle:props.inputFieldStyle, - animationStyle:props.animationStyle, + inputFieldStyle: props.inputFieldStyle, + animationStyle: props.animationStyle, children: ( - { + { + if (!mountedRef.current) return; if (el) { props.viewRef(el); radioRef.current = el; } }} - disabled={props.disabled} - value={props.value.value} - $style={props.style} - $inputFieldStyle={props.inputFieldStyle} - $layout={props.layout} - onChange={(e) => { - handleChange(e.target.value); - }} - options={props.options - .filter((option) => option.value !== undefined && !option.hidden) - .map((option) => ({ - label: option.label, - value: option.value, - disabled: option.disabled, - }))} /> ), ...validateState, diff --git a/client/packages/lowcoder/src/comps/comps/signatureComp.tsx b/client/packages/lowcoder/src/comps/comps/signatureComp.tsx index 6ba13fe0e..7dd560d50 100644 --- a/client/packages/lowcoder/src/comps/comps/signatureComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/signatureComp.tsx @@ -19,8 +19,8 @@ import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps import { trans } from "i18n"; import { changeValueAction, multiChangeAction } from "lowcoder-core"; import { Section, sectionNames, UndoIcon } from "lowcoder-design"; -import React, { Suspense, useEffect, useState } from "react"; -import ReactResizeDetector from "react-resize-detector"; +import React, { Suspense, useEffect, useRef, useState } from "react"; +import { ResizePayload, useResizeDetector } from "react-resize-detector"; import type SignatureCanvasType from "react-signature-canvas"; import styled from "styled-components"; import { UICompBuilder } from "../generators"; @@ -115,6 +115,7 @@ let SignatureTmpComp = (function () { let canvas: SignatureCanvasType | null = null; const [isBegin, setIsBegin] = useState(false); const [canvasSize, setCanvasSize] = useState([0, 0]); + const conRef = useRef(null); const updateValue = (isClear: boolean = false) => { const clear = isClear || canvas?.toData().length === 0; @@ -127,76 +128,79 @@ let SignatureTmpComp = (function () { ); } }; + + useResizeDetector({ + targetRef: conRef, + onResize: ({width, height}: ResizePayload) => { + width && height && setCanvasSize([width, height]); + updateValue(true); + }, + }); + return props.label({ style: props.style, labelStyle: props.labelStyle, inputFieldStyle:props.inputFieldStyle, children: ( - { - width && height && setCanvasSize([width, height]); - updateValue(true); + { + e.preventDefault(); }} + $style={props.inputFieldStyle} + $isEmpty={!props.value && !isBegin} > - { - e.preventDefault(); - }} - $style={props.inputFieldStyle} - $isEmpty={!props.value && !isBegin} - > - - }> - { - canvas = ref; - }} - penColor={props.inputFieldStyle.pen} - clearOnResize={false} - canvasProps={{ - className: "sigCanvas", - width: canvasSize[0], - height: canvasSize[1], - }} - onEnd={() => { - updateValue(); - setIsBegin(false); + + }> + { + canvas = ref; + }} + penColor={props.inputFieldStyle.pen} + clearOnResize={false} + canvasProps={{ + className: "sigCanvas", + width: canvasSize[0], + height: canvasSize[1], + }} + onEnd={() => { + updateValue(); + setIsBegin(false); + props.onEvent("change"); + }} + onBegin={() => setIsBegin(true)} + /> + + + {(props.showClear || props.showUndo) && ( + + {props.showUndo && ( + + { + const data = canvas?.toData(); + if (data) { + data?.pop(); + canvas?.fromData(data); + updateValue(); + props.onEvent("change"); + } + }} + /> + + )} + {props.showClear && ( + { + updateValue(true); props.onEvent("change"); }} - onBegin={() => setIsBegin(true)} /> - + )} - {(props.showClear || props.showUndo) && ( - - {props.showUndo && ( - - { - const data = canvas?.toData(); - if (data) { - data?.pop(); - canvas?.fromData(data); - updateValue(); - props.onEvent("change"); - } - }} - /> - - )} - {props.showClear && ( - { - updateValue(true); - props.onEvent("change"); - }} - /> - )} - - )} - {!(isBegin || props.value) && {props.tips}} - - + )} + {!(isBegin || props.value) && {props.tips}} + ), }); }) diff --git a/client/packages/lowcoder/src/comps/comps/switchComp.tsx b/client/packages/lowcoder/src/comps/comps/switchComp.tsx index c4745cc48..40bada394 100644 --- a/client/packages/lowcoder/src/comps/comps/switchComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/switchComp.tsx @@ -18,7 +18,7 @@ import { refMethods } from "comps/generators/withMethodExposing"; import { blurMethod, clickMethod, focusWithOptions } from "comps/utils/methodUtils"; import { fixOldInputCompData } from "./textInputComp/textInputConstants"; -import { useContext, useEffect } from "react"; +import { useCallback, useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; const EventOptions = [ @@ -115,6 +115,12 @@ let SwitchTmpComp = (function () { props.value.onChange(defaultValue); }, [defaultValue]); + const handleChange = useCallback((checked: boolean) => { + props.value.onChange(checked); + props.onEvent("change"); + props.onEvent(checked ? "true" : "false"); + }, [props.value, props.onEvent]); + return props.label({ style: props.style, labelStyle: props.labelStyle, @@ -126,18 +132,18 @@ let SwitchTmpComp = (function () { checked={value} disabled={props.disabled} ref={props.viewRef} + onChange={handleChange} tabIndex={typeof props.tabIndex === 'number' ? props.tabIndex : undefined} - onChange={(checked) => { - props.value.onChange(checked); - props.onEvent("change"); - props.onEvent(checked ? "true" : "false"); - }} /> ), }); }) .setPropertyViewFn((children) => { + const editorModeStatus = useContext(EditorContext).editorModeStatus; + const isLogicMode = ["logic", "both"].includes(editorModeStatus); + const isLayoutMode = ["layout", "both"].includes(editorModeStatus); + return ( <> @@ -146,7 +152,7 @@ let SwitchTmpComp = (function () { - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {isLogicMode && ( {children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -156,11 +162,11 @@ let SwitchTmpComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {isLayoutMode && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {isLayoutMode && ( <> {children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeCompBuilder.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeCompBuilder.tsx index 98d570378..b401761d2 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeCompBuilder.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeCompBuilder.tsx @@ -54,6 +54,7 @@ export class ColumnTypeCompBuilder< RecordConstructorToComp> >; private editViewFn?: EditViewFn; + private cleanupFunctions: (() => void)[] = []; constructor( childrenMap: ChildrenCtorMap, @@ -93,22 +94,57 @@ export class ColumnTypeCompBuilder< if (!this.propertyViewFn) { throw new Error("need property view fn"); } + + // Memoize the props processing + const memoizedViewFn = _.memoize( + (props: any, dispatch: any) => { + const baseValue = this.baseValueFn?.(props, dispatch); + const normalView = this.viewFn(props, dispatch); + return ( + + {...props} + normalView={normalView} + dispatch={dispatch} + baseValue={baseValue} + changeValue={props.changeValue as any} + editViewFn={this.editViewFn} + /> + ); + }, + (props) => { + let safeOptions = []; + let safeAvatars = []; + if(props.options) { + safeOptions = props.options.map((option: Record) => { + const {prefixIcon, suffixIcon, ...safeOption} = option; + return safeOption; + }) + } + if(props.avatars) { + safeAvatars = props.avatars.map((avatar: Record) => { + const {AvatarIcon, ...safeAvatar} = avatar; + return safeAvatar; + }) + } + const { + prefixIcon, + suffixIcon, + iconFalse, + iconTrue, + iconNull, + tagColors, + options, + avatars, + ...safeProps + } = props; + return safeProps; + } + ); + const viewFn: ColumnTypeViewFn = (props, dispatch): CellViewReturn => - (cellProps) => { - const baseValue = this.baseValueFn?.(props, dispatch); - const normalView = this.viewFn(props, dispatch); - return ( - - {...cellProps} - normalView={normalView} - dispatch={dispatch} - baseValue={baseValue} - changeValue={props.changeValue as any} - editViewFn={this.editViewFn} - /> - ); - }; + (cellProps) => memoizedViewFn({ ...props, ...cellProps } as any, dispatch); + const ColumnTypeCompTmp = new MultiCompBuilder( this.childrenMap as ToConstructor< RecordConstructorToComp> @@ -117,12 +153,21 @@ export class ColumnTypeCompBuilder< ) .setPropertyViewFn(this.propertyViewFn) .build(); + const displayValueFn = this.displayValueFn; const editViewFn = this.editViewFn; return class extends ColumnTypeCompTmp { // table cell data - readonly displayValue: JSONValue = null; + private _displayValue: JSONValue = null; + private cleanupFunctions: (() => void)[] = []; + constructor(props: any) { + super(props); + this.cleanupFunctions.push(() => { + this._displayValue = null; + memoizedViewFn.cache.clear?.(); + }); + } override extraNode() { return { @@ -134,7 +179,8 @@ export class ColumnTypeCompBuilder< }, updateNodeFields: (value: any) => { const displayValueFunc = value[__COLUMN_DISPLAY_VALUE_FN]; - return { displayValue: displayValueFunc(value) }; + this._displayValue = displayValueFunc(value); + return { displayValue: this._displayValue }; }, }; } @@ -143,12 +189,24 @@ export class ColumnTypeCompBuilder< * Get the data actually displayed by the table cell */ getDisplayValue() { - return this.displayValue; + return this._displayValue; } static canBeEditable() { return !_.isNil(editViewFn); } + + componentWillUnmount() { + // Cleanup all registered cleanup functions + this.cleanupFunctions.forEach(cleanup => cleanup()); + this.cleanupFunctions = []; + } }; } + + // Cleanup method to be called when the builder is no longer needed + cleanup() { + this.cleanupFunctions.forEach(cleanup => cleanup()); + this.cleanupFunctions = []; + } } diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx index 8c7ea937d..f221b547d 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/ColumnNumberComp.tsx @@ -1,4 +1,5 @@ - import { default as InputNumber } from "antd/es/input-number"; +import React, { useState, useRef, useEffect, useCallback, useMemo, ReactNode } from "react"; +import { default as InputNumber } from "antd/es/input-number"; import { NumberControl, RangeControl, StringControl } from "comps/controls/codeControl"; import { BoolControl } from "comps/controls/boolControl"; import { trans } from "i18n"; @@ -35,59 +36,123 @@ const childrenMap = { suffix: StringControl, }; -let float = false; -let step = 1; -let precision = 0; +const getBaseValue: ColumnTypeViewFn = (props) => props.text; -const getBaseValue: ColumnTypeViewFn = ( - props -) => { - return props.text +type NumberViewProps = { + value: number; + prefix: string; + suffix: string; + prefixIcon: ReactNode; + suffixIcon: ReactNode; + float: boolean; + precision: number; }; +type NumberEditProps = { + value: number; + onChange: (value: number) => void; + onChangeEnd: () => void; + step: number; + precision: number; + float: boolean; +}; + +const ColumnNumberView = React.memo((props: NumberViewProps) => { + const formattedValue = useMemo(() => { + let result = !props.float ? Math.floor(props.value) : props.value; + if (props.float) { + result = Number(result.toFixed(props.precision + 1)); + } + return result; + }, [props.value, props.float, props.precision]); + + return ( + <> + {hasIcon(props.prefixIcon) && ( + {props.prefixIcon} + )} + {props.prefix + formattedValue + props.suffix} + {hasIcon(props.suffixIcon) && ( + {props.suffixIcon} + )} + > + ); +}); + +ColumnNumberView.displayName = 'ColumnNumberView'; + + +const ColumnNumberEdit = React.memo((props: NumberEditProps) => { + const [currentValue, setCurrentValue] = useState(props.value); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setCurrentValue(0); + }; + }, []); + + const handleChange = useCallback((value: string | number | null) => { + if (!mountedRef.current) return; + const newValue = typeof value === 'number' ? value : 0; + const finalValue = !props.float ? Math.floor(newValue) : newValue; + props.onChange(finalValue); + setCurrentValue(finalValue); + }, [props.onChange, props.float]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + const handlePressEnter = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + return ( + + + + ); +}); + +ColumnNumberEdit.displayName = 'NumberEdit'; + export const ColumnNumberComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { - float = props.float; - step = props.step; - precision = props.precision; const value = props.changeValue ?? getBaseValue(props, dispatch); - let formattedValue: string | number = !float ? Math.floor(value) : value; - if(float) { - formattedValue = formattedValue.toFixed(precision + 1); - } - return ( - <>{hasIcon(props.prefixIcon) && ( - {props.prefixIcon} - )} - {props.prefix + formattedValue + props.suffix} - {hasIcon(props.suffixIcon) && ( - {props.suffixIcon} - )} > - ); + return ; }, (nodeValue) => nodeValue.text.value, - getBaseValue, + getBaseValue ) .setEditViewFn((props) => { + const { value, onChange, onChangeEnd, otherProps } = props; return ( - - { - value = value ?? 0; - props.onChange(!float ? Math.floor(value) : value); - }} - precision={float ? precision : 0} - onBlur={props.onChangeEnd} - onPressEnter={props.onChangeEnd} + - - )}) + ); + }) .setPropertyViewFn((children) => { return ( <> @@ -99,7 +164,7 @@ export const ColumnNumberComp = (function () { label: trans("table.numberStep"), tooltip: trans("table.numberStepTooltip"), onFocus: (focused) => { - if(!focused) { + if (!focused) { const value = children.step.getView(); const isFloat = children.float.getView(); const newValue = !isFloat ? Math.floor(value) : value; @@ -107,7 +172,7 @@ export const ColumnNumberComp = (function () { } } })} - {float && ( + {children.float.getView() && ( children.precision.propertyView({ label: trans("table.precision"), }) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx index 0b159b08d..c34b6dfbb 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnAvatarsComp.tsx @@ -10,7 +10,7 @@ import { AlignCenter, AlignLeft, AlignRight } from "lowcoder-design"; import { NumberControl } from "comps/controls/codeControl"; import { Avatar, Tooltip } from "antd"; import { clickEvent, eventHandlerControl, refreshEvent } from "comps/controls/eventHandlerControl"; -import { ReactElement, useContext, useEffect } from "react"; +import React, { ReactElement, useCallback, useEffect, useRef } from "react"; import { IconControl } from "comps/controls/iconControl"; import { ColorControl } from "comps/controls/colorControl"; import { optionsControl } from "comps/controls/optionsControl"; @@ -18,16 +18,6 @@ import { BoolControl } from "comps/controls/boolControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { JSONObject } from "util/jsonTypes"; -const MenuLinkWrapper = styled.div` - > a { - color: ${PrimaryColor} !important; - - &:hover { - color: ${LightActiveTextColor} !important; - } - } -`; - const MacaroneList = [ '#fde68a', '#eecff3', @@ -86,6 +76,100 @@ export const alignOptions = [ { label: , value: "flex-end" }, ] as const; +// Memoized Avatar component +const MemoizedAvatar = React.memo(({ + item, + index, + style, + autoColor, + avatarSize, + onEvent +}: { + item: any; + index: number; + style: any; + autoColor: boolean; + avatarSize: number; + onEvent: (event: string) => void; +}) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleClick = useCallback(() => { + if (!mountedRef.current) return; + onEvent("click"); + }, [onEvent]); + + return ( + + + {item.label} + + + ); +}); + +MemoizedAvatar.displayName = 'MemoizedAvatar'; + +// Memoized Avatar Group component +const MemoizedAvatarGroup = React.memo(({ + avatars, + maxCount, + avatarSize, + style, + autoColor, + onEvent +}: { + avatars: any[]; + maxCount: number; + avatarSize: number; + style: any; + autoColor: boolean; + onEvent: (event: string) => void; +}) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + return ( + + {avatars.map((item, index) => ( + + ))} + + ); +}); + +MemoizedAvatarGroup.displayName = 'MemoizedAvatarGroup'; + export const ColumnAvatarsComp = (function () { const childrenMap = { style: styleControl(avatarGroupStyle), @@ -107,42 +191,22 @@ export const ColumnAvatarsComp = (function () { return new ColumnTypeCompBuilder( childrenMap, - (props , dispatch) => { + (props, dispatch) => { return ( - { - - { - props.avatars.map((item, index) => { - return ( - - { - props.onEvent("click") - // Falk: TODO: Implement dispatch function to set the currentAvatar - // dispatch(changeChildAction("currentAvatar", item as JSONObject, false)); - }} - > - {item.label} - - - ) - }) - } - - } + - ) + ); }, () => "" ) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx index 8c53650a8..d1d530eb6 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnBooleanComp.tsx @@ -1,6 +1,7 @@ +import React, { useCallback, useRef, useEffect, useMemo } from "react"; import { BoolCodeControl } from "comps/controls/codeControl"; import { trans } from "i18n"; -import { default as Checkbox } from "antd/es/checkbox"; +import { default as Checkbox, CheckboxChangeEvent } from "antd/es/checkbox"; import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder"; import { ColumnValueTooltip } from "../simpleColumnTypeComps"; import { getStyle } from "comps/comps/selectInputComp/checkboxComp"; @@ -65,44 +66,101 @@ type CheckBoxEditPropsType = { onChangeEnd: () => void; }; -const CheckBoxEdit = (props: CheckBoxEditPropsType) => { +// Memoized checkbox edit component +const CheckBoxEdit = React.memo((props: CheckBoxEditPropsType) => { + const mountedRef = useRef(true); const style = useStyle(CheckboxStyle); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (!mountedRef.current) return; + if (e.key === "Enter") { + props.onChangeEnd(); + } + }, [props.onChangeEnd]); + + const handleChange = useCallback((e: CheckboxChangeEvent) => { + if (!mountedRef.current) return; + props.onChange(e.target.checked); + }, [props.onChange]); + return ( props.onChangeEnd()} - onKeyDown={(e) => { - if (e.key === "Enter") { - props.onChangeEnd(); - } - }} + onBlur={handleBlur} + onKeyDown={handleKeyDown} > props.onChange(e.target.checked)} + onChange={handleChange} /> ); -}; +}); + +CheckBoxEdit.displayName = 'CheckBoxEdit'; + +// Memoized checkbox view component +const CheckBoxView = React.memo(({ + value, + iconTrue, + iconFalse, + iconNull, + falseValues +}: { + value: boolean; + iconTrue: React.ReactNode; + iconFalse: React.ReactNode; + iconNull: React.ReactNode; + falseValues: string; +}) => { + const style = useStyle(CheckboxStyle); + + const content = useMemo(() => { + if (value === true) { + return hasIcon(iconTrue) ? iconTrue : ; + } else if (value === false) { + return hasIcon(iconFalse) ? iconFalse : (falseValues === "x" ? : falseValues); + } else { + return hasIcon(iconNull) ? iconNull : "No Value"; + } + }, [value, iconTrue, iconFalse, iconNull, falseValues]); + + return ( + + {content} + + ); +}); + +CheckBoxView.displayName = 'CheckBoxView'; export const BooleanComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { const value = props.changeValue ?? getBaseValue(props, dispatch); - const CheckBoxComp = () => { - const style = useStyle(CheckboxStyle); - return ( - - {value === true ? ( hasIcon(props.iconTrue) ? props.iconTrue : ) - : value === false ? ( hasIcon(props.iconFalse) ? props.iconFalse : ( props.falseValues === "x" ? : props.falseValues ) - ) : ( hasIcon(props.iconNull) ? props.iconNull : "No Value" - )} - - ); - }; - return ; + return ( + + ); }, (nodeValue) => nodeValue.text.value, getBaseValue diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateComp.tsx index a71734f46..99bee383e 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateComp.tsx @@ -9,10 +9,10 @@ import { withDefault } from "comps/generators"; import { formatPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { isNumber } from "lodash"; -import dayjs from "dayjs"; +import dayjs, { Dayjs } from "dayjs"; import utc from "dayjs/plugin/utc"; import { CalendarCompIconSmall, PrevIcon, SuperPrevIcon } from "lowcoder-design"; -import { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import styled from "styled-components"; import { DateParser, DATE_FORMAT } from "util/dateTimeUtils"; @@ -49,9 +49,7 @@ const DatePickerStyled = styled(DatePicker)<{ $open: boolean }>` top: 0.5px; display: flex; align-items: center; - // background: #fff; padding: 0 3px; - // border-left: 1px solid #d7d9e0; } `; @@ -117,6 +115,12 @@ const StylePanel = styled.div` } `; +const DatePickerPopup = styled.div` + border-radius: 8px; + box-shadow: 0 0 10px 0 rgba(0,0,0,0.10); + overflow: hidden; +`; + const Wrapper = styled.div` background: transparent !important; `; @@ -127,7 +131,6 @@ export function formatDate(date: string, format: string) { mom = dayjs(Number(date)); } if (!mom.isValid()) { - // mom = dayjs.utc(date, DateParser).local(); mom = dayjs.utc(date).local(); } @@ -140,8 +143,6 @@ const childrenMap = { inputFormat: withDefault(StringControl, DATE_FORMAT), }; -let inputFormat = DATE_FORMAT; - const getBaseValue: ColumnTypeViewFn = (props) => props.text; type DateEditProps = { @@ -152,32 +153,72 @@ type DateEditProps = { inputFormat: string; }; -export const DateEdit = (props: DateEditProps) => { +// Memoized DateEdit component +export const DateEdit = React.memo((props: DateEditProps) => { const pickerRef = useRef(); + const mountedRef = useRef(true); const [panelOpen, setPanelOpen] = useState(true); - let value = dayjs(props.value, DateParser); - if (!value.isValid()) { - value = dayjs(0, DateParser); - } + + // Initialize tempValue with proper validation + const [tempValue, setTempValue] = useState(() => { + const initialValue = dayjs(props.value, DateParser); + return initialValue.isValid() ? initialValue : dayjs(0, DateParser); + }); + + // Memoize event handlers + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (!mountedRef.current) return; + if (e.key === "Enter" && !panelOpen) { + props.onChangeEnd(); + } + }, [panelOpen, props.onChangeEnd]); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + if (!mountedRef.current) return; + e.stopPropagation(); + e.preventDefault(); + }, []); + + const handleOpenChange = useCallback((open: boolean) => { + if (!mountedRef.current) return; + setPanelOpen(open); + }, []); - const [tempValue, setTempValue] = useState(value); + const handleChange = useCallback((value: dayjs.Dayjs | null, dateString: string | string[]) => { + if (!mountedRef.current) return; + props.onChange(dateString as string); + }, [props.onChange]); + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + // Update tempValue when props.value changes useEffect(() => { - const value = props.value ? dayjs(props.value, DateParser) : null; - setTempValue(value); - }, [props.value]) + if (!mountedRef.current) return; + + const newValue = props.value ? dayjs(props.value, DateParser) : null; + if (newValue?.isValid()) { + setTempValue(newValue); + } + }, [props.value]); + + // Cleanup event listeners and state + useEffect(() => { + return () => { + mountedRef.current = false; + setTempValue(null); + if (pickerRef.current) { + pickerRef.current = null; + } + }; + }, []); return ( { - if (e.key === "Enter" && !panelOpen) { - props.onChangeEnd(); - } - }} - onMouseDown={(e) => { - e.stopPropagation(); - e.preventDefault(); - }} + onKeyDown={handleKeyDown} + onMouseDown={handleMouseDown} > { showTime={props.showTime} showNow={true} defaultOpen={true} - panelRender={(panelNode) => {panelNode}} - popupStyle={{ - borderRadius: "8px", - boxShadow: "0 0 10px 0 rgba(0,0,0,0.10)", - overflow: "hidden", - }} - onOpenChange={(open) => setPanelOpen(open)} - onChange={(value, dateString) => { - props.onChange(dateString as string) - }} - onBlur={() => props.onChangeEnd()} + panelRender={(panelNode) => ( + + {panelNode} + + )} + onOpenChange={handleOpenChange} + onChange={(date: unknown, dateString: string | string[]) => handleChange(date as Dayjs | null, dateString)} + onBlur={handleBlur} /> ); -}; +}); + +DateEdit.displayName = 'DateEdit'; export const DateComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { - inputFormat = props.inputFormat; const value = props.changeValue ?? getBaseValue(props, dispatch); return formatDate(value, props.format); }, @@ -228,7 +267,7 @@ export const DateComp = (function () { onChange={props.onChange} onChangeEnd={props.onChangeEnd} showTime={false} - inputFormat={inputFormat} + inputFormat={props.otherProps?.inputFormat} /> )) .setPropertyViewFn((children) => ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp.tsx index 7e243fdfb..181fccba7 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp.tsx @@ -9,6 +9,7 @@ import { formatPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { DATE_TIME_FORMAT } from "util/dateTimeUtils"; import { DateEdit, formatDate } from "./columnDateComp"; +import React, { useCallback, useEffect, useRef } from "react"; const childrenMap = { text: StringControl, @@ -16,15 +17,52 @@ const childrenMap = { inputFormat: withDefault(StringControl, DATE_TIME_FORMAT), }; -let inputFormat = DATE_TIME_FORMAT; - const getBaseValue: ColumnTypeViewFn = (props) => props.text; +// Memoized DateTimeEdit component +const DateTimeEdit = React.memo((props: { + value: string; + onChange: (value: string) => void; + onChangeEnd: () => void; + inputFormat: string; +}) => { + const mountedRef = useRef(true); + + // Memoize event handlers + const handleChange = useCallback((value: string) => { + if (!mountedRef.current) return; + props.onChange(value); + }, [props.onChange]); + + const handleChangeEnd = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + return ( + + ); +}); + +DateTimeEdit.displayName = 'DateTimeEdit'; + export const DateTimeComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { - inputFormat = props.inputFormat; const value = props.changeValue ?? getBaseValue(props, dispatch); return formatDate(value, props.format); }, @@ -32,12 +70,11 @@ export const DateTimeComp = (function () { getBaseValue ) .setEditViewFn((props) => ( - )) .setPropertyViewFn((children) => ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx index de554ac0e..b3e0e9d15 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnDropdownComp.tsx @@ -1,4 +1,4 @@ -import { ReactElement } from "react"; +import React, { ReactNode, useCallback, useRef, useEffect, useMemo, ReactElement } from "react"; import { DropdownOptionControl } from "comps/controls/optionsControl"; import { StringControl } from "comps/controls/codeControl"; import { trans } from "i18n"; @@ -13,6 +13,20 @@ import { ButtonTypeOptions } from "../simpleColumnTypeComps"; import { useStyle } from "comps/controls/styleControl"; import { ButtonStyle } from "comps/controls/styleControlConstants"; import { Button100 } from "comps/comps/buttonComp/buttonCompConstants"; +import styled from "styled-components"; +import { ButtonType } from "antd/es/button"; + +const StyledButton = styled(Button100)` + display: flex; + align-items: center; + gap: 0; + min-width: 30px; + width: auto; +`; + +const StyledIconWrapper = styled(IconWrapper)` + margin: 0; +`; const childrenMap = { buttonType: dropdownControl(ButtonTypeOptions, "primary"), @@ -24,88 +38,128 @@ const childrenMap = { const getBaseValue: ColumnTypeViewFn = (props) => props.label; +// Memoized dropdown menu component +const DropdownMenu = React.memo(({ items, options }: { items: any[]; options: any[] }) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleClick = useCallback(({ key }: { key: string }) => { + if (!mountedRef.current) return; + const item = items.find((o) => o.key === key); + const itemIndex = options.findIndex(option => option.label === item?.label); + item && options[itemIndex]?.onEvent("click"); + }, [items, options]); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + }, []); + + return ( + + ); +}); + +DropdownMenu.displayName = 'DropdownMenu'; + +const DropdownView = React.memo((props: { + buttonType: ButtonType; + label: string; + prefixIcon: ReactNode; + suffixIcon: ReactNode; + options: any[]; +}) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const hasOptionIcon = useMemo(() => + props.options.findIndex((option) => (option.prefixIcon as ReactElement)?.props.value) > -1, + [props.options] + ); + + const items = useMemo(() => + props.options + .filter((option) => !option.hidden) + .map((option, index) => ({ + title: option.label, + label: option.label, + key: option.label + " - " + index, + disabled: option.disabled, + icon: hasOptionIcon && {option.prefixIcon}, + index, + })), + [props.options, hasOptionIcon] + ); + + const hasPrefixIcon = useMemo(() => + (props.prefixIcon as ReactElement)?.props.value, + [props.prefixIcon] + ); + + const hasSuffixIcon = useMemo(() => + (props.suffixIcon as ReactElement)?.props.value, + [props.suffixIcon] + ); + + const buttonStyle = useStyle(ButtonStyle); + + const menu = useMemo(() => ( + + ), [items, props.options]); + + return ( + menu} + > + + {hasPrefixIcon && ( + + {props.prefixIcon} + + )} + {props.label || (hasPrefixIcon || hasSuffixIcon ? undefined : " ")} + {hasSuffixIcon && ( + + {props.suffixIcon} + + )} + + + ); +}); + +DropdownView.displayName = 'DropdownView'; + export const ColumnDropdownComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props) => { - const hasOptionIcon = props.options.findIndex((option) => (option.prefixIcon as ReactElement)?.props.value) > -1; - const items = props.options - .filter((option) => !option.hidden) - .map((option, index) => ({ - title: option.label, - label: option.label, - key: option.label + " - " + index, - disabled: option.disabled, - icon: hasOptionIcon && {option.prefixIcon}, - index, - })); - - const hasPrefixIcon = (props.prefixIcon as ReactElement)?.props.value; - const hasSuffixIcon = (props.suffixIcon as ReactElement)?.props.value; - const buttonStyle = useStyle(ButtonStyle); - - const menu = ( - { - const item = items.find((o) => o.key === key); - const itemIndex = props.options.findIndex(option => option.label === item?.label); - item && props.options[itemIndex]?.onEvent("click"); - }} - onMouseDown={(e) => { - e.stopPropagation(); - e.preventDefault(); - }} - /> - ); - - return ( - menu} - > - - { - hasPrefixIcon && ( - - {props.prefixIcon} - - ) - } - { - props.label || (hasPrefixIcon || hasSuffixIcon ? undefined : " ") // Avoid button disappearing - } - { - hasSuffixIcon && ( - - {props.suffixIcon} - - ) - } - - - ); + return ; }, (nodeValue) => nodeValue.label.value, getBaseValue, diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnImgComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnImgComp.tsx index 566da4aa9..b062f8fc4 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnImgComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnImgComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { default as Input } from "antd/es/input"; import { ColumnTypeCompBuilder, @@ -7,6 +8,8 @@ import { StringControl, NumberControl } from "comps/controls/codeControl"; import { trans } from "i18n"; import { withDefault } from "comps/generators"; import { TacoImage } from "lowcoder-design"; +import styled from "styled-components"; +import { DEFAULT_IMG_URL } from "@lowcoder-ee/util/stringUtils"; export const ColumnValueTooltip = trans("table.columnValueTooltip"); @@ -15,6 +18,77 @@ const childrenMap = { size: withDefault(NumberControl, "50"), }; +const StyledTacoImage = styled(TacoImage)` + pointer-events: auto; +`; + +// Memoized image component +const ImageView = React.memo(({ src, size }: { src: string; size: number }) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + return ( + + ); +}); + +ImageView.displayName = 'ImageView'; + +// Memoized edit component +const ImageEdit = React.memo(({ value, onChange, onChangeEnd }: { value: string; onChange: (value: string) => void; onChangeEnd: () => void }) => { + const mountedRef = useRef(true); + const [currentValue, setCurrentValue] = useState(value); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((e: React.ChangeEvent) => { + if (mountedRef.current) { + const newValue = e.target.value; + setCurrentValue(newValue); + onChange(newValue); + } + }, [onChange]); + + const handleBlur = useCallback(() => { + if (mountedRef.current) { + onChangeEnd(); + } + }, [onChangeEnd]); + + const handlePressEnter = useCallback(() => { + if (mountedRef.current) { + onChangeEnd(); + } + }, [onChangeEnd]); + + return ( + + ); +}); + +ImageEdit.displayName = 'ImageEdit'; + const getBaseValue: ColumnTypeViewFn = (props) => props.src; export const ImageComp = (function () { @@ -22,22 +96,16 @@ export const ImageComp = (function () { childrenMap, (props, dispatch) => { const value = props.changeValue ?? getBaseValue(props, dispatch); - return ; + return ; }, (nodeValue) => nodeValue.src.value, getBaseValue ) .setEditViewFn((props) => ( - { - const value = e.target.value; - props.onChange(value); - }} - onBlur={props.onChangeEnd} - onPressEnter={props.onChangeEnd} + )) .setPropertyViewFn((children) => { diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx index 9a28d1830..c82b7326a 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinkComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { default as Input } from "antd/es/input"; import { ColumnTypeCompBuilder, @@ -32,16 +33,71 @@ const StyledLink = styled.a<{ $disabled: boolean }>` ${(props) => props.$disabled && disableCss}; `; -export const ColumnLink = (props: { disabled: boolean; label: string; onClick?: () => void }) => ( - { - !props.disabled && props.onClick && props.onClick(); - }} - > - {props.label} - -); +// Memoized link component +export const ColumnLink = React.memo(({ disabled, label, onClick }: { disabled: boolean; label: string; onClick?: () => void }) => { + const handleClick = useCallback(() => { + if (!disabled && onClick) { + onClick(); + } + }, [disabled, onClick]); + + return ( + + {label} + + ); +}); + +ColumnLink.displayName = 'ColumnLink'; + +// Memoized edit component +const LinkEdit = React.memo(({ value, onChange, onChangeEnd }: { value: string; onChange: (value: string) => void; onChangeEnd: () => void }) => { + const mountedRef = useRef(true); + const [currentValue, setCurrentValue] = useState(value); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((e: React.ChangeEvent) => { + if (mountedRef.current) { + const newValue = e.target.value; + setCurrentValue(newValue); + onChange(newValue); + } + }, [onChange]); + + const handleBlur = useCallback(() => { + if (mountedRef.current) { + onChangeEnd(); + } + }, [onChangeEnd]); + + const handlePressEnter = useCallback(() => { + if (mountedRef.current) { + onChangeEnd(); + } + }, [onChangeEnd]); + + return ( + + ); +}); + +LinkEdit.displayName = 'LinkEdit'; const getBaseValue: ColumnTypeViewFn = (props) => props.text; @@ -56,16 +112,10 @@ export const LinkComp = (function () { getBaseValue ) .setEditViewFn((props) => ( - { - const value = e.target.value; - props.onChange(value); - }} - onBlur={props.onChangeEnd} - onPressEnter={props.onChangeEnd} + )) .setPropertyViewFn((children) => ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx index 06f7fcb38..4ecd308dd 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnLinksComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { default as Menu } from "antd/es/menu"; import { ColumnTypeCompBuilder } from "comps/comps/tableComp/column/columnTypeCompBuilder"; import { ActionSelectorControlInContext } from "comps/controls/actionSelector/actionSelectorControl"; @@ -36,6 +37,27 @@ const MenuWrapper = styled.div` } `; +// Memoized menu item component +const MenuItem = React.memo(({ option, index }: { option: any; index: number }) => { + const handleClick = useCallback(() => { + if (!option.disabled && option.onClick) { + option.onClick(); + } + }, [option.disabled, option.onClick]); + + return ( + + + + ); +}); + +MenuItem.displayName = 'MenuItem'; + const OptionItem = new MultiCompBuilder( { label: StringControl, @@ -62,6 +84,36 @@ const OptionItem = new MultiCompBuilder( }) .build(); +// Memoized menu component +const LinksMenu = React.memo(({ options }: { options: any[] }) => { + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const menuItems = useMemo(() => + options + .filter((o) => !o.hidden) + .map((option, index) => ({ + key: index, + label: + })), + [options] + ); + + return ( + + + + ); +}); + +LinksMenu.displayName = 'LinksMenu'; + export const ColumnLinksComp = (function () { const childrenMap = { options: manualOptionsControl(OptionItem, { @@ -71,28 +123,7 @@ export const ColumnLinksComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props) => { - const menuItems = props.options - .filter((o) => !o.hidden) - .map((option, index) => ( - { - key: index, - label: ( - - - - ) - } - )); - - return ( - - - - ) + return ; }, () => "" ) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnMarkdownComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnMarkdownComp.tsx index aa131c04e..e8fcd9a4b 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnMarkdownComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnMarkdownComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { default as Input } from "antd/es/input"; import { ColumnTypeCompBuilder, @@ -27,31 +28,80 @@ const childrenMap = { const getBaseValue: ColumnTypeViewFn = (props) => props.text; +// Memoized markdown view component +const MarkdownView = React.memo(({ value }: { value: string }) => { + return ( + + {value} + + ); +}); + +MarkdownView.displayName = 'MarkdownView'; + +// Memoized edit component with proper cleanup +const MarkdownEdit = React.memo((props: { + value: string; + onChange: (value: string) => void; + onChangeEnd: () => void; +}) => { + const [currentValue, setCurrentValue] = useState(props.value); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setCurrentValue(''); + }; + }, []); + + const handleChange = useCallback((e: React.ChangeEvent) => { + if (!mountedRef.current) return; + const value = e.target.value; + props.onChange(value); + setCurrentValue(value); + }, [props.onChange]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + const handlePressEnter = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + return ( + + ); +}); + +MarkdownEdit.displayName = 'MarkdownEdit'; + export const ColumnMarkdownComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { const value = props.changeValue ?? getBaseValue(props, dispatch); - return ( - - {value} - - ); + return ; }, (nodeValue) => nodeValue.text.value, getBaseValue ) .setEditViewFn((props) => ( - { - const value = e.target.value; - props.onChange(value); - }} - onBlur={props.onChangeEnd} - onPressEnter={props.onChangeEnd} + )) .setPropertyViewFn((children) => ( diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnProgressComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnProgressComp.tsx index 1c5e02814..7e06f4c8e 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnProgressComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnProgressComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { NumberControl } from "comps/controls/codeControl"; import { trans } from "i18n"; import { default as InputNumber } from "antd/es/input-number"; @@ -71,6 +72,58 @@ const childrenMap = { const getBaseValue: ColumnTypeViewFn = (props) => props.text; +type ProgressEditProps = { + value: number; + onChange: (value: number) => void; + onChangeEnd: () => void; +}; + +const ProgressEdit = React.memo((props: ProgressEditProps) => { + const [currentValue, setCurrentValue] = useState(props.value); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setCurrentValue(0); + }; + }, []); + + const handleChange = useCallback((value: string | number | null) => { + if (!mountedRef.current) return; + const newValue = typeof value === 'number' ? value : 0; + props.onChange(newValue); + setCurrentValue(newValue); + }, [props.onChange]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + const handlePressEnter = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + }, [props.onChangeEnd]); + + return ( + , downIcon: }} + onChange={handleChange} + onBlur={handleBlur} + onPressEnter={handlePressEnter} + /> + ); +}); + +ProgressEdit.displayName = 'ProgressEdit'; + export const ProgressComp = (function () { return new ColumnTypeCompBuilder( childrenMap, @@ -89,18 +142,10 @@ export const ProgressComp = (function () { ) .setEditViewFn((props) => { return ( - , downIcon: }} - onChange={(value) => { - props.onChange(Number(value)); - }} - onBlur={props.onChangeEnd} - onPressEnter={props.onChangeEnd} + ); }) diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnRatingComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnRatingComp.tsx index 8aca4df27..fc44cd936 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnRatingComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnRatingComp.tsx @@ -1,3 +1,4 @@ +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { NumberControl } from "comps/controls/codeControl"; import { trans } from "i18n"; import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder"; @@ -42,7 +43,7 @@ const RateStyled = styled(Rate)<{ isEdit?: boolean }>` const Wrapper = styled.div` background: transparent !important; padding: 0 8px; -` +`; const childrenMap = { text: NumberControl, @@ -50,6 +51,61 @@ const childrenMap = { const getBaseValue: ColumnTypeViewFn = (props) => props.text; +type RatingEditProps = { + value: number; + onChange: (value: number) => void; + onChangeEnd: () => void; +}; + +const RatingEdit = React.memo((props: RatingEditProps) => { + const [currentValue, setCurrentValue] = useState(props.value); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setCurrentValue(0); + }; + }, []); + + const handleChange = useCallback((value: number) => { + if (!mountedRef.current) return; + props.onChange(value); + setCurrentValue(value); + }, [props.onChange]); + + const handleBlur = useCallback((e: React.FocusEvent) => { + if (!mountedRef.current) return; + if (!e.currentTarget?.contains(e.relatedTarget)) { + props.onChangeEnd(); + } + }, [props.onChangeEnd]); + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (!mountedRef.current) return; + if (e.key === "Enter") { + props.onChangeEnd(); + } + }, [props.onChangeEnd]); + + return ( + + + + ); +}); + +RatingEdit.displayName = 'RatingEdit'; + export const RatingComp = (function () { return new ColumnTypeCompBuilder( childrenMap, @@ -62,25 +118,11 @@ export const RatingComp = (function () { ) .setEditViewFn((props) => { return ( - { - if (!e.currentTarget?.contains(e.relatedTarget)) { - props.onChangeEnd(); - } - }} - > - props.onChange(value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - props.onChangeEnd(); - } - }} - /> - + ); }) .setPropertyViewFn((children) => { diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx index a51b182e1..de76a4dd8 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSelectComp.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { SelectUIView } from "comps/comps/selectInputComp/selectCompConstants"; import { SelectOptionControl } from "comps/controls/optionsControl"; @@ -80,7 +80,6 @@ const childrenMap = { options: SelectOptionControl, }; -let options: any[] = [] const getBaseValue: ColumnTypeViewFn = (props) => props.text; type SelectEditProps = { @@ -90,38 +89,56 @@ type SelectEditProps = { options: any[]; }; -const defaultProps: any = {} -const SelectEdit = (props: SelectEditProps) => { +const SelectEdit = React.memo((props: SelectEditProps) => { const [currentValue, setCurrentValue] = useState(props.initialValue); + const mountedRef = useRef(true); + const defaultProps: any = {}; + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setCurrentValue(''); + }; + }, []); + + const handleChange = useCallback((val: string) => { + if (!mountedRef.current) return; + props.onChange(val); + setCurrentValue(val); + }, [props.onChange]); + + const handleEvent = useCallback(async (eventName: string) => { + if (!mountedRef.current) return [] as unknown[]; + if (eventName === "blur") { + props.onChangeEnd(); + } + return [] as unknown[]; + }, [props.onChangeEnd]); + + const memoizedOptions = useMemo(() => props.options, [props.options]); + return ( { - props.onChange(val); - setCurrentValue(val) - }} - onEvent={async (eventName) => { - if (eventName === "blur") { - props.onChangeEnd() - } - return [] - }} + options={memoizedOptions} + onChange={handleChange} + onEvent={handleEvent} // @ts-ignore style={{}} + {...defaultProps} /> ); -}; +}); +SelectEdit.displayName = 'SelectEdit'; export const ColumnSelectComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { - options = props.options; const value = props.changeValue ?? getBaseValue(props, dispatch); const option = props.options.find(x => x.value === value); return ( @@ -139,7 +156,7 @@ export const ColumnSelectComp = (function () { diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnStatusComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnStatusComp.tsx index b8181f91a..ba647cbc0 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnStatusComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnStatusComp.tsx @@ -6,7 +6,7 @@ import { import { trans } from "i18n"; import { StringControl, stringUnionControl } from "comps/controls/codeControl"; import { DropdownStyled, Wrapper } from "./columnTagsComp"; -import { ReactNode, useContext, useState } from "react"; +import React, { ReactNode, useContext, useState, useCallback, useRef, useEffect, useMemo } from "react"; import { StatusContext } from "components/table/EditableCell"; import { CustomSelect, PackUpIcon, ScrollBar } from "lowcoder-design"; import { PresetStatusColorType } from "antd/es/_util/colors"; @@ -44,11 +44,91 @@ type StatusEditPropsType = { onChangeEnd: () => void; }; -const StatusEdit = (props: StatusEditPropsType) => { +const StatusEdit = React.memo((props: StatusEditPropsType) => { const defaultStatus = useContext(StatusContext); - const [status, setStatus] = useState(defaultStatus); - const [allOptions, setAllOptions] = useState(BadgeStatusOptions); + const [status, setStatus] = useState>(() => { + const result: Array<{ text: string; status: StatusType }> = []; + defaultStatus.forEach((item) => { + if (item.text.includes(",")) { + item.text.split(",").forEach((tag) => result.push({ text: tag, status: "none" })); + } + result.push({ text: item.text, status: item.status }); + }); + return result; + }); const [open, setOpen] = useState(false); + const mountedRef = useRef(true); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setStatus([]); + setOpen(false); + }; + }, []); + + // Update status when defaultStatus changes + useEffect(() => { + if (!mountedRef.current) return; + + const result: Array<{ text: string; status: StatusType }> = []; + defaultStatus.forEach((item) => { + if (item.text.includes(",")) { + item.text.split(",").forEach((tag) => result.push({ text: tag, status: "none" })); + } + result.push({ text: item.text, status: item.status }); + }); + setStatus(result); + }, [defaultStatus]); + + const handleSearch = useCallback((value: string) => { + if (!mountedRef.current) return; + + if (defaultStatus.findIndex((item) => item.text.includes(value)) < 0) { + setStatus([...defaultStatus, { text: value, status: "none" }]); + } else { + setStatus(defaultStatus); + } + props.onChange({ + value, + status: status.find((item) => item.text === value)?.status || "none", + }); + }, [defaultStatus, status, props.onChange]); + + const handleChange = useCallback((value: string) => { + if (!mountedRef.current) return; + props.onChange({ + value, + status: status.find((item) => item.text === value)?.status || "none", + }); + setOpen(false); + }, [status, props.onChange]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + setOpen(false); + }, [props.onChangeEnd]); + + const handleFocus = useCallback(() => { + if (!mountedRef.current) return; + setOpen(true); + }, []); + + const handleClick = useCallback(() => { + if (!mountedRef.current) return; + setOpen(!open); + }, [open]); + + const memoizedOptions = useMemo(() => + BadgeStatusOptions.map((value, index) => ( + + {value === "none" ? value : } + + )), + [] + ); return ( @@ -62,58 +142,25 @@ const StatusEdit = (props: StatusEditPropsType) => { style={{ width: "100%" }} suffixIcon={} showSearch - onSearch={(value: string) => { - if (defaultStatus.findIndex((item) => item.text.includes(value)) < 0) { - setStatus([ - ...defaultStatus, - { - text: value, - status: "none", - }, - ]); - } else { - setStatus(defaultStatus); - } - props.onChange({ - value, - status: status.find((item) => item.text === value)?.status || "none", - }); - }} - onChange={(value: string) => { - props.onChange({ - value, - status: status.find((item) => item.text === value)?.status || "none", - }); - setOpen(false) - }} + onSearch={handleSearch} + onChange={handleChange} dropdownRender={(originNode: ReactNode) => ( {originNode} )} dropdownStyle={{ marginTop: "7px", padding: "8px 0 6px 0" }} - onBlur={() => { - props.onChangeEnd(); - setOpen(false); - }} - onFocus={() => { - setOpen(true); - }} - onClick={() => setOpen(!open)} + onBlur={handleBlur} + onFocus={handleFocus} + onClick={handleClick} > - {allOptions.map((value, index) => ( - - {value === "none" ? ( - value - ) : ( - - )} - - ))} + {memoizedOptions} ); -}; +}); + +StatusEdit.displayName = 'StatusEdit'; export const BadgeStatusComp = (function () { return new ColumnTypeCompBuilder( @@ -126,24 +173,20 @@ export const BadgeStatusComp = (function () { (nodeValue) => [nodeValue.status.value, nodeValue.text.value].filter((t) => t).join(" "), getBaseValue ) - .setEditViewFn((props) => { - return ( - - ); - }) - .setPropertyViewFn((children) => { - return ( - <> - {children.text.propertyView({ - label: trans("table.columnValue"), - tooltip: ColumnValueTooltip, - })} - {children.status.propertyView({ - label: trans("table.status"), - tooltip: trans("table.statusTooltip"), - })} - > - ); - }) + .setEditViewFn((props) => ( + + )) + .setPropertyViewFn((children) => ( + <> + {children.text.propertyView({ + label: trans("table.columnValue"), + tooltip: ColumnValueTooltip, + })} + {children.status.propertyView({ + label: trans("table.status"), + tooltip: trans("table.statusTooltip"), + })} + > + )) .build(); })(); diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx index 4d5f29243..0cdeee48a 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnSwitchComp.tsx @@ -1,23 +1,16 @@ import { BoolCodeControl } from "comps/controls/codeControl"; import { trans } from "i18n"; -import { default as Checkbox } from "antd/es/checkbox"; import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder"; import { ColumnValueTooltip } from "../simpleColumnTypeComps"; -import { SwitchStyle, SwitchStyleType, LabelStyle, InputFieldStyle } from "comps/controls/styleControlConstants"; -import styled, { css } from "styled-components"; -import { CheckboxStyle } from "comps/controls/styleControlConstants"; -import { useStyle } from "comps/controls/styleControl"; +import { InputFieldStyle } from "comps/controls/styleControlConstants"; +import styled from "styled-components"; import { default as Switch } from "antd/es/switch"; import { styleControl } from "comps/controls/styleControl"; import { RefControl } from "comps/controls/refControl"; import { booleanExposingStateControl } from "comps/controls/codeStateControl"; import { changeEvent, eventHandlerControl } from "comps/controls/eventHandlerControl"; import { disabledPropertyView } from "comps/utils/propertyUtils"; - -interface SwitchWrapperProps { - disabled: boolean; - $style?: SwitchStyleType; -} +import React, { useCallback, useRef, useEffect } from "react"; const EventOptions = [ changeEvent, @@ -33,26 +26,6 @@ const EventOptions = [ }, ] as const; -const getStyle = (style: SwitchStyleType) => { - return css` - .ant-switch-handle::before { - background-color: ${style.handle}; - } - button { - background-image: none; - background-color: ${style.unchecked}; - &.ant-switch-checked { - background-color: ${style.checked}; - } - } - `; -}; - -const SwitchWrapper = styled.div<{ disabled: boolean }>` - display: flex; - align-items: center; -`; - const Wrapper = styled.div` background: transparent !important; padding: 0 8px; @@ -64,34 +37,96 @@ const childrenMap = { onEvent: eventHandlerControl(EventOptions), disabled: BoolCodeControl, style: styleControl(InputFieldStyle), - viewRef: RefControl, + // viewRef: RefControl, }; const getBaseValue: ColumnTypeViewFn = (props) => props.switchState; -let onEvent: (eventName: string) => Promise; +const SwitchView = React.memo(({ value, disabled, onEvent, valueControl }: { + value: boolean; + disabled: boolean; + // viewRef: (viewRef: HTMLButtonElement | null) => void; + onEvent: (event: string) => void; + valueControl: { onChange: (value: boolean) => void }; +}) => { + const mountedRef = useRef(true); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((checked: boolean) => { + if (!mountedRef.current) return; + valueControl.onChange(checked); + onEvent("change"); + onEvent(checked ? "true" : "false"); + }, [valueControl, onEvent]); + + return ( + + ); +}); + +SwitchView.displayName = 'SwitchView'; + +const SwitchEdit = React.memo(({ value, onChange, onChangeEnd }: { + value: boolean; + onChange: (value: boolean) => void; + onChangeEnd: () => void; +}) => { + const mountedRef = useRef(true); + + useEffect(() => { + return () => { + mountedRef.current = false; + }; + }, []); + + const handleChange = useCallback((checked: boolean) => { + if (!mountedRef.current) return; + onChange(checked); + }, [onChange]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + onChangeEnd(); + }, [onChangeEnd]); + + return ( + + + + ); +}); + +SwitchEdit.displayName = 'SwitchEdit'; export const SwitchComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { - onEvent = props.onEvent; const value = props.changeValue ?? getBaseValue(props, dispatch); - const CheckBoxComp = () => { - return ( - { - props.value.onChange(checked); - props.onEvent("change"); - props.onEvent(checked ? "true" : "false"); - }} - /> - ); - }; - return ; + return ( + + ); }, (nodeValue) => nodeValue.switchState.value, getBaseValue @@ -109,8 +144,8 @@ export const SwitchComp = (function () { disabled={false} onChange={(checked, e) => { props.onChange(checked); - onEvent?.("change"); - onEvent?.(checked ? "true" : "false"); + props.otherProps?.onEvent?.("change"); + props.otherProps?.onEvent?.(checked ? "true" : "false"); }} /> diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx index 61ed6cb53..085751013 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx @@ -10,7 +10,7 @@ import { codeControl } from "comps/controls/codeControl"; import { trans } from "i18n"; import styled from "styled-components"; import _ from "lodash"; -import { ReactNode, useContext, useState } from "react"; +import React, { ReactNode, useContext, useState, useRef, useEffect, useCallback, useMemo } from "react"; import { toJson } from "really-relaxed-json"; import { hashToNum } from "util/stringUtils"; import { CustomSelect, PackUpIcon } from "lowcoder-design"; @@ -82,6 +82,7 @@ type TagEditPropsType = { value: string | string[]; onChange: (value: string | string[]) => void; onChangeEnd: () => void; + tagOptions: any[]; }; export const Wrapper = styled.div` @@ -185,9 +186,7 @@ export const TagStyled = styled(Tag)` } `; -let tagOptionsList: any[] = []; - -const TagEdit = (props: TagEditPropsType) => { +const TagEdit = React.memo((props: TagEditPropsType) => { const defaultTags = useContext(TagsContext); const [tags, setTags] = useState(() => { const result: string[] = []; @@ -200,6 +199,57 @@ const TagEdit = (props: TagEditPropsType) => { return result; }); const [open, setOpen] = useState(false); + const mountedRef = useRef(true); + + // Memoize tag options to prevent unnecessary re-renders + const memoizedTagOptions = useMemo(() => props.tagOptions || [], [props.tagOptions]); + + // Cleanup on unmount + useEffect(() => { + return () => { + mountedRef.current = false; + setTags([]); + setOpen(false); + }; + }, []); + + // Update tags when defaultTags changes + useEffect(() => { + if (!mountedRef.current) return; + + const result: string[] = []; + defaultTags.forEach((item) => { + if (item.split(",")[1]) { + item.split(",").forEach((tag) => result.push(tag)); + } + result.push(item); + }); + setTags(result); + }, [defaultTags]); + + const handleSearch = useCallback((value: string) => { + if (!mountedRef.current) return; + + if (defaultTags.findIndex((item) => item.includes(value)) < 0) { + setTags([...defaultTags, value]); + } else { + setTags(defaultTags); + } + props.onChange(value); + }, [defaultTags, props.onChange]); + + const handleChange = useCallback((value: string | string[]) => { + if (!mountedRef.current) return; + props.onChange(value); + setOpen(false); + }, [props.onChange]); + + const handleBlur = useCallback(() => { + if (!mountedRef.current) return; + props.onChangeEnd(); + setOpen(false); + }, [props.onChangeEnd]); + return ( { open={open} allowClear={true} suffixIcon={} - onSearch={(value: string) => { - if (defaultTags.findIndex((item) => item.includes(value)) < 0) { - setTags([...defaultTags, value]); - } else { - setTags(defaultTags); - } - props.onChange(value); - }} - onChange={(value: string | string[]) => { - props.onChange(value); - setOpen(false) - }} + onSearch={handleSearch} + onChange={handleChange} dropdownRender={(originNode: ReactNode) => ( {originNode} @@ -232,24 +272,27 @@ const TagEdit = (props: TagEditPropsType) => { )} dropdownStyle={{ marginTop: "7px", padding: "8px 0 6px 0" }} onFocus={() => { - setOpen(true); + if (mountedRef.current) { + setOpen(true); + } }} - onBlur={() => { - props.onChangeEnd(); - setOpen(false); + onBlur={handleBlur} + onClick={() => { + if (mountedRef.current) { + setOpen(!open); + } }} - onClick={() => setOpen(!open)} > {tags.map((value, index) => ( {value.split(",")[1] ? ( value.split(",").map((item, i) => ( - + {item} )) ) : ( - + {value} )} @@ -258,14 +301,15 @@ const TagEdit = (props: TagEditPropsType) => { ); -}; +}); + +TagEdit.displayName = 'TagEdit'; export const ColumnTagsComp = (function () { return new ColumnTypeCompBuilder( childrenMap, (props, dispatch) => { const tagOptions = props.tagColors; - tagOptionsList = props.tagColors; let value = props.changeValue ?? getBaseValue(props, dispatch); value = typeof value === "string" && value.split(",")[1] ? value.split(",") : value; const tags = _.isArray(value) ? value : (value.length ? [value] : []); @@ -273,7 +317,7 @@ export const ColumnTagsComp = (function () { // The actual eval value is of type number or boolean const tagText = String(tag); return ( - + {tagText} @@ -291,7 +335,12 @@ export const ColumnTagsComp = (function () { .setEditViewFn((props) => { const text = props.value; const value = _.isArray(text) ? text.join(",") : text; - return ; + return ; }) .setPropertyViewFn((children) => ( <> diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx index b4ad2d73d..f338f0f64 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -1,166 +1,193 @@ -import { default as TimePicker } from "antd/es/time-picker"; -import { - ColumnTypeCompBuilder, - ColumnTypeViewFn, -} from "comps/comps/tableComp/column/columnTypeCompBuilder"; -import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps"; -import { StringControl } from "comps/controls/codeControl"; -import { withDefault } from "comps/generators"; -import { formatPropertyView } from "comps/utils/propertyUtils"; -import { trans } from "i18n"; -import dayjs from "dayjs"; -import { useEffect, useRef, useState } from "react"; -import styled from "styled-components"; -import { TIME_FORMAT } from "util/dateTimeUtils"; -import { hasIcon } from "comps/utils"; -import { IconControl } from "comps/controls/iconControl"; - - -const TimePickerStyled = styled(TimePicker)<{ $open: boolean }>` - width: 100%; - height: 100%; - position: absolute; - top: 0; - padding: 0; - padding-left: 11px; - .ant-picker-input { - height: 100%; - } - input { - padding-right: 18px; - cursor: pointer; - } - &.ant-picker-focused .ant-picker-suffix svg g { - stroke: ${(props) => props.$open && "#315EFB"}; - } - .ant-picker-suffix { - height: calc(100% - 1px); - position: absolute; - right: 0; - top: 0.5px; - display: flex; - align-items: center; - padding: 0 3px; - } -`; - -const Wrapper = styled.div` - background: transparent !important; -`; - -export function formatTime(time: string, format: string) { - const parsedTime = dayjs(time, TIME_FORMAT); - return parsedTime.isValid() ? parsedTime.format(format) : ""; -} - -const childrenMap = { - text: StringControl, - prefixIcon: IconControl, - suffixIcon: IconControl, - format: withDefault(StringControl, TIME_FORMAT), - inputFormat: withDefault(StringControl, TIME_FORMAT), -}; - -let inputFormat = TIME_FORMAT; - -const getBaseValue: ColumnTypeViewFn = (props) => props.text; - -type TimeEditProps = { - value: string; - onChange: (value: string) => void; - onChangeEnd: () => void; - inputFormat: string; -}; - -export const TimeEdit = (props: TimeEditProps) => { - const pickerRef = useRef(); - const [panelOpen, setPanelOpen] = useState(true); - let value = dayjs(props.value, TIME_FORMAT); - if (!value.isValid()) { - value = dayjs("00:00:00", TIME_FORMAT); - } - - const [tempValue, setTempValue] = useState(value); - - useEffect(() => { - const value = props.value ? dayjs(props.value, TIME_FORMAT) : null; - setTempValue(value); - }, [props.value]); - - return ( - { - if (e.key === "Enter" && !panelOpen) { - props.onChangeEnd(); - } - }} - onMouseDown={(e) => { - e.stopPropagation(); - e.preventDefault(); - }} - > - setPanelOpen(open)} - onChange={(value, timeString) => { - props.onChange(timeString as string); - }} - onBlur={() => props.onChangeEnd()} - /> - - ); -}; - -export const TimeComp = (function () { - return new ColumnTypeCompBuilder( - childrenMap, - (props, dispatch) => { - inputFormat = props.inputFormat; - const value = props.changeValue ?? getBaseValue(props, dispatch); - return( - <> - {hasIcon(props.prefixIcon) && ( - {props.prefixIcon} - )} - {value} - {hasIcon(props.suffixIcon) && ( - {props.suffixIcon} - )} - > - ); - - }, - (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value), - getBaseValue - ) - .setEditViewFn((props) => ( - - )) - .setPropertyViewFn((children) => ( - <> - {children.text.propertyView({ - label: trans("table.columnValue"), - tooltip: ColumnValueTooltip, - })} - {children.prefixIcon.propertyView({ - label: trans("button.prefixIcon"), - })} - {children.suffixIcon.propertyView({ - label: trans("button.suffixIcon"), - })} - {formatPropertyView({ children, placeholder: TIME_FORMAT })} - > - )) - .build(); -})(); +import { default as TimePicker } from "antd/es/time-picker"; +import { + ColumnTypeCompBuilder, + ColumnTypeViewFn, +} from "comps/comps/tableComp/column/columnTypeCompBuilder"; +import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps"; +import { StringControl } from "comps/controls/codeControl"; +import { withDefault } from "comps/generators"; +import { formatPropertyView } from "comps/utils/propertyUtils"; +import { trans } from "i18n"; +import dayjs from "dayjs"; +import React, { useEffect, useRef, useState, useCallback } from "react"; +import styled from "styled-components"; +import { TIME_FORMAT } from "util/dateTimeUtils"; +import { hasIcon } from "comps/utils"; +import { IconControl } from "comps/controls/iconControl"; + +const TimePickerStyled = styled(TimePicker)<{ $open: boolean }>` + width: 100%; + height: 100%; + position: absolute; + top: 0; + padding: 0; + padding-left: 11px; + .ant-picker-input { + height: 100%; + } + input { + padding-right: 18px; + cursor: pointer; + } + &.ant-picker-focused .ant-picker-suffix svg g { + stroke: ${(props) => props.$open && "#315EFB"}; + } + .ant-picker-suffix { + height: calc(100% - 1px); + position: absolute; + right: 0; + top: 0.5px; + display: flex; + align-items: center; + padding: 0 3px; + } +`; + +const Wrapper = styled.div` + background: transparent !important; +`; + +export function formatTime(time: string, format: string) { + const parsedTime = dayjs(time, TIME_FORMAT); + return parsedTime.isValid() ? parsedTime.format(format) : ""; +} + +const childrenMap = { + text: StringControl, + prefixIcon: IconControl, + suffixIcon: IconControl, + format: withDefault(StringControl, TIME_FORMAT), + inputFormat: withDefault(StringControl, TIME_FORMAT), +}; + +const getBaseValue: ColumnTypeViewFn = (props) => props.text; + +type TimeEditProps = { + value: string; + onChange: (value: string) => void; + onChangeEnd: () => void; + inputFormat: string; +}; + +export const TimeEdit = React.memo((props: TimeEditProps) => { + const pickerRef = useRef(); + const [panelOpen, setPanelOpen] = useState(true); + const mountedRef = useRef(true); + + // Initialize tempValue with proper validation + const [tempValue, setTempValue] = useState(() => { + const initialValue = dayjs(props.value, TIME_FORMAT); + return initialValue.isValid() ? initialValue : dayjs("00:00:00", TIME_FORMAT); + }); + + // Memoize event handlers + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (e.key === "Enter" && !panelOpen) { + props.onChangeEnd(); + } + }, [panelOpen, props.onChangeEnd]); + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + }, []); + + const handleOpenChange = useCallback((open: boolean) => { + if (mountedRef.current) { + setPanelOpen(open); + } + }, []); + + const handleChange = useCallback((value: dayjs.Dayjs | null, dateString: string | string[]) => { + props.onChange(dateString as string); + }, [props.onChange]); + + // Update tempValue when props.value changes + useEffect(() => { + if (!mountedRef.current) return; + + const newValue = props.value ? dayjs(props.value, TIME_FORMAT) : null; + if (newValue?.isValid()) { + setTempValue(newValue); + } + }, [props.value]); + + // Cleanup event listeners and state + useEffect(() => { + return () => { + mountedRef.current = false; + setTempValue(null); + if (pickerRef.current) { + pickerRef.current = null; + } + }; + }, []); + + return ( + + + + ); +}); + +TimeEdit.displayName = 'TimeEdit'; + +export const TimeComp = (function () { + return new ColumnTypeCompBuilder( + childrenMap, + (props, dispatch) => { + const value = props.changeValue ?? getBaseValue(props, dispatch); + return ( + <> + {hasIcon(props.prefixIcon) && ( + {props.prefixIcon} + )} + {value} + {hasIcon(props.suffixIcon) && ( + {props.suffixIcon} + )} + > + ); + }, + (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value), + getBaseValue + ) + .setEditViewFn(({value, onChange, onChangeEnd, otherProps}) => ( + + )) + .setPropertyViewFn((children) => ( + <> + {children.text.propertyView({ + label: trans("table.columnValue"), + tooltip: ColumnValueTooltip, + })} + {children.prefixIcon.propertyView({ + label: trans("button.prefixIcon"), + })} + {children.suffixIcon.propertyView({ + label: trans("button.suffixIcon"), + })} + {formatPropertyView({ children, placeholder: TIME_FORMAT })} + > + )) + .build(); +})(); diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx index 04330e9c6..c0d5a6d30 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/simpleTextComp.tsx @@ -5,6 +5,8 @@ import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilde import { ColumnValueTooltip } from "../simpleColumnTypeComps"; import { IconControl } from "comps/controls/iconControl"; import { hasIcon } from "comps/utils"; +import React, { useCallback, useMemo } from "react"; +import { RecordConstructorToComp } from "lowcoder-core"; const childrenMap = { text: StringOrNumberControl, @@ -12,58 +14,85 @@ const childrenMap = { suffixIcon: IconControl, }; -const getBaseValue: ColumnTypeViewFn = ( - props -) => props.text + ""; +// Memoize the base value function to prevent unnecessary string creation +const getBaseValue: ColumnTypeViewFn = (props) => + typeof props.text === 'string' ? props.text : String(props.text); + +// Memoized icon components to prevent unnecessary re-renders +const IconWrapper = React.memo(({ icon }: { icon: React.ReactNode }) => ( +
{userName ?? ""}