From e92ace27ca7d6b6d4ba36dde2cebc77c181bb573 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Wed, 22 Jul 2026 23:59:02 +0500 Subject: [PATCH 1/3] add zustand editorStore for the selected comps --- client/packages/lowcoder/package.json | 3 +- .../lowcoder/src/components/CompName.tsx | 7 +- .../comps/containerComp/containerView.tsx | 10 ++- .../comps/containerComp/flowContainerView.tsx | 4 +- .../lowcoder/src/comps/comps/rootComp.tsx | 46 +++++----- .../lowcoder/src/comps/editorState.tsx | 44 +++++----- .../lowcoder/src/comps/editorStore.test.ts | 41 +++++++++ .../lowcoder/src/comps/editorStore.tsx | 84 +++++++++++++++++++ .../src/comps/generators/uiCompBuilder.tsx | 7 +- .../lowcoder/src/comps/hooks/hookComp.tsx | 14 +++- client/packages/lowcoder/src/index.sdk.ts | 1 + .../src/pages/ComponentDoc/common/Example.tsx | 8 +- .../src/pages/ComponentPlayground/index.tsx | 20 +++-- .../lowcoder/src/pages/editor/LeftContent.tsx | 6 +- .../src/pages/editor/LeftLayersContent.tsx | 6 +- .../lowcoder/src/pages/editor/editorView.tsx | 4 +- .../src/pages/editor/right/PropertyView.tsx | 5 +- client/yarn.lock | 22 +++++ 18 files changed, 264 insertions(+), 68 deletions(-) create mode 100644 client/packages/lowcoder/src/comps/editorStore.test.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore.tsx diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json index 542327bee7..88aa68589c 100644 --- a/client/packages/lowcoder/package.json +++ b/client/packages/lowcoder/package.json @@ -132,7 +132,8 @@ "y-protocols": "^1.0.6", "y-websocket": "^3.0.0", "yjs": "^13.6.27", - "zod": "^3.25.76" + "zod": "^3.25.76", + "zustand": "^5.0.14" }, "scripts": { "supportedBrowsers": "yarn dlx browserslist-useragent-regexp --allowHigherVersions '>0.2%,not dead,not op_mini all,chrome >=69'", diff --git a/client/packages/lowcoder/src/components/CompName.tsx b/client/packages/lowcoder/src/components/CompName.tsx index 69fd8c781e..66bd47643a 100644 --- a/client/packages/lowcoder/src/components/CompName.tsx +++ b/client/packages/lowcoder/src/components/CompName.tsx @@ -15,6 +15,7 @@ import { trans } from "i18n"; import { getComponentDocUrl } from "comps/utils/compDocUtil"; import { getComponentPlaygroundUrl } from "comps/utils/compDocUtil"; import { parseCompType } from "comps/utils/remote"; +import { useEditorStore } from "comps/editorStore"; const CompDiv = styled.div<{ $width?: number; $hasSearch?: boolean; $showSearch?: boolean }>` width: ${(props) => (props.$width ? props.$width : 312)}px; @@ -79,7 +80,11 @@ export const CompName = React.memo((props: Iprops) => { const [showSearch, setShowSearch] = useState(false); const editorState = useContext(EditorContext); - const selectedComp = useMemo(() => values(editorState.selectedComps())[0], [editorState]); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const selectedComp = useMemo( + () => values(editorState.selectedComps(selectedCompNames))[0], + [editorState, selectedCompNames] + ); const compType = useMemo(() => selectedComp.children.compType.getView() as UICompType, [selectedComp]); const compInfo = useMemo(() => parseCompType(compType), [compType]); const docUrl = useMemo(() => getComponentDocUrl(compType), [compType]); diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index dbf4d5d8f5..9cdb6a70ed 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -54,6 +54,7 @@ import React, { useRef, useState, } from "react"; +import { useEditorStore } from "comps/editorStore"; import { ResizePayload, useResizeDetector } from "react-resize-detector"; import styled from "styled-components"; import { checkIsMobile } from "util/commonUtils"; @@ -354,6 +355,7 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { const [currentRowCount, setRowCount] = useState(rowCount || Infinity); const [currentRowHeight, setRowHeight] = useState(positionParams.rowHeight || DEFAULT_ROW_HEIGHT); const editorState = useContext(EditorContext); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); const { readOnly } = useContext(ExternalEditorContext); const appSettingsComp = editorState?.getAppSettingsComp().getView(); @@ -377,10 +379,10 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { getExtraLayout( props.items, props.layout, - editorState?.selectedCompNames, + selectedCompNames, props.dragSelectedComps ), - [props.items, props.layout, editorState?.selectedCompNames, props.dragSelectedComps] + [props.items, props.layout, selectedCompNames, props.dragSelectedComps] ); const [containerSelectNames, setContainerSelectNames] = useState>(new Set([])); @@ -397,8 +399,8 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { }, [extraLayout, containerSelectNames]); const canAddSelect = useMemo( - () => _.size(containerSelectNames) === _.size(editorState?.selectedCompNames), - [containerSelectNames, editorState?.selectedCompNames] + () => _.size(containerSelectNames) === _.size(selectedCompNames), + [containerSelectNames, selectedCompNames] ); const dispatchPositionParamsTimerRef = useRef(0); diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/flowContainerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/flowContainerView.tsx index 71bb7b83d8..f84c61d967 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/flowContainerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/flowContainerView.tsx @@ -8,6 +8,7 @@ import { EditorContext } from "comps/editorState"; import { ThemeContext } from "comps/utils/themeContext"; import styled from "styled-components"; import { defaultTheme } from "@lowcoder-ee/constants/themeConstants"; +import { useEditorStore } from "comps/editorStore"; const FlowContainerWrapper = styled.div<{ $bgColor: string; $maxWidth?: number; $minHeight: string }>` background-color: ${(props) => props.$bgColor}; @@ -37,6 +38,7 @@ export function FlowContainerView( const layouts = props.layout; const { selectable, minHeight, maxWidth } = props; const editorState = useContext(EditorContext); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); const bgColor = (useContext(ThemeContext)?.theme || defaultTheme).canvas; return ( @@ -63,7 +65,7 @@ export function FlowContainerView( }} autoHeight={false} resizeHandles={[]} - isSelected={editorState.selectedCompNames.has(comp.name)} + isSelected={selectedCompNames.has(comp.name)} onClick={() => { editorState.setSelectedCompNames(new Set([comp.name])); }} diff --git a/client/packages/lowcoder/src/comps/comps/rootComp.tsx b/client/packages/lowcoder/src/comps/comps/rootComp.tsx index 3c663028d7..cebfa67df6 100644 --- a/client/packages/lowcoder/src/comps/comps/rootComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/rootComp.tsx @@ -38,6 +38,8 @@ import {LoadingBarHideTrigger} from "@lowcoder-ee/util/hideLoading"; import clsx from "clsx"; import { useUnmount } from "react-use"; import { AIHelperModal, AIHelperProvider } from "components/ai-helper"; +import { createEditorStore, EditorStoreProvider } from "comps/editorStore"; +import { useStore } from "zustand"; const EditorView = lazy( () => import("pages/editor/editorView"), @@ -65,6 +67,8 @@ const RootView = React.memo((props: RootViewProps) => { const previewTheme = useContext(ThemeContext); const { comp, isModuleRoot, ...divProps } = props; const [editorState, setEditorState] = useState(); + const [editorStore] = useState(createEditorStore); + const selectedCompNames = useStore(editorStore, (state) => state.selectedCompNames); const [propertySectionState, setPropertySectionState] = useState({}); const { readOnly } = useContext(ExternalEditorContext); const isUserViewMode = useUserViewMode(); @@ -101,7 +105,7 @@ const RootView = React.memo((props: RootViewProps) => { return (oldState ? changeEditorStateFn(oldState) : undefined) }); } - }, undefined, isModuleRoot); + }, undefined, isModuleRoot, editorStore); editorStateRef.current = newEditorState; setEditorState(newEditorState); @@ -110,7 +114,7 @@ const RootView = React.memo((props: RootViewProps) => { editorStateRef.current = undefined; } }; - }, [isModuleRoot]); + }, [isModuleRoot, editorStore]); useEffect(() => { if (!mountedRef.current || !editorState) return; @@ -138,7 +142,7 @@ const RootView = React.memo((props: RootViewProps) => { ); const propertySectionContextValue = useMemo(() => { - const compName = Object.keys(editorState?.selectedComps() || {})[0]; + const compName = Object.keys(editorState?.selectedComps(selectedCompNames) || {})[0]; return { compName, state: propertySectionState, @@ -154,7 +158,7 @@ const RootView = React.memo((props: RootViewProps) => { }); }, }; - }, [editorState, propertySectionState]); + }, [editorState, propertySectionState, selectedCompNames]); if (!editorState && !isUserViewMode && readOnly) { return ; @@ -172,22 +176,24 @@ const RootView = React.memo((props: RootViewProps) => { divProps.id, )} style={{height: '100%'}}> - - - - - {Object.keys(comp.children.queries.children).map((key) => ( -
{comp.children.queries.children[key].getView()}
- ))} - - - - - {!readOnly && !isUserViewMode && !isModuleRoot && } -
-
-
-
+ + + + + + {Object.keys(comp.children.queries.children).map((key) => ( +
{comp.children.queries.children[key].getView()}
+ ))} + + + + + {!readOnly && !isUserViewMode && !isModuleRoot && } +
+
+
+
+
); }, (prevProps, nextProps) => { diff --git a/client/packages/lowcoder/src/comps/editorState.tsx b/client/packages/lowcoder/src/comps/editorState.tsx index 243eefb7de..9fb08861e6 100644 --- a/client/packages/lowcoder/src/comps/editorState.tsx +++ b/client/packages/lowcoder/src/comps/editorState.tsx @@ -18,6 +18,11 @@ import { checkName } from "./utils/rename"; import { trans } from "i18n"; import type { UiLayoutType } from "./comps/uiComp"; import { getEditorModeStatus, saveCollisionStatus } from "util/localStorageUtil"; +import { + createEditorStore, + type EditorStoreApi, + type SelectSourceType, +} from "./editorStore"; type RootComp = InstanceType; @@ -33,8 +38,6 @@ export type CompInfo = { dataDesc: Record; }; -type SelectSourceType = "editor" | "leftPanel" | "addComp" | "rightPanel"; - export type DeviceType = "desktop" | "tablet" | "mobile"; export type DeviceOrientation = "landscape" | "portrait"; @@ -47,8 +50,6 @@ export type DeviceOrientation = "landscape" | "portrait"; */ export class EditorState { readonly rootComp: RootComp; - readonly showPropertyPane: boolean = false; - readonly selectedCompNames: Set = new Set(); readonly editorModeStatus: string = ""; readonly collisionStatus: boolean = false; readonly isDragging: boolean = false; @@ -58,7 +59,6 @@ export class EditorState { readonly selectedBottomResName: string = ""; readonly selectedBottomResType?: BottomResTypeEnum; readonly showResultCompName: string = ""; - readonly selectSource?: SelectSourceType; // the source of select type readonly deviceType: DeviceType = "desktop"; readonly deviceOrientation: DeviceOrientation = "portrait"; @@ -71,6 +71,7 @@ export class EditorState { setEditorState: (fn: (editorState: EditorState) => EditorState) => void, initialEditorModeStatus: string = getEditorModeStatus(), isModuleRoot: boolean = false, + private readonly editorStore: EditorStoreApi = createEditorStore(), ) { this.rootComp = rootComp; this.setEditorState = setEditorState; @@ -97,6 +98,18 @@ export class EditorState { this.changeStateFn(() => params); } + get showPropertyPane() { + return this.editorStore.getState().showPropertyPane; + } + + get selectedCompNames() { + return this.editorStore.getState().selectedCompNames; + } + + get selectSource() { + return this.editorStore.getState().selectSource; + } + getAllCompMap() { return { ...this.getAllHooksCompMap(), @@ -286,18 +299,18 @@ export class EditorState { /** * @deprecated */ - selectedComp(): OptionalComp { + selectedComp(selectedCompNames: Set = this.selectedCompNames): OptionalComp { // temporary glue code const compType = this.getUIComp().children.compType.getView(); if (compType !== "normal" && compType !== "module") { return this.getUIComp().children.comp; } const compMap = this.getAllCompMap(); - if (this.selectedCompNames.size > 1) { + if (selectedCompNames.size > 1) { return undefined; } return Object.values(compMap).find((item) => - this.selectedCompNames.has(item.children.name.getView()) + selectedCompNames.has(item.children.name.getView()) ); } @@ -306,10 +319,10 @@ export class EditorState { return this.getUIComp().getComp()?.getPositionParams(); } - selectedComps() { + selectedComps(selectedCompNames: Set = this.selectedCompNames) { const compMap = this.getAllCompMap(); const selectedComps = _.pickBy(compMap, (item) => - this.selectedCompNames.has(item.children.name.getView()) + selectedCompNames.has(item.children.name.getView()) ); return selectedComps; } @@ -402,7 +415,7 @@ export class EditorState { } setShowPropertyPane(showPropertyPane: boolean) { - this.changeState({ showPropertyPane: showPropertyPane }); + this.editorStore.getState().setShowPropertyPane(showPropertyPane); } setComp(compFn: (comp: RootComp) => RootComp) { @@ -415,14 +428,7 @@ export class EditorState { selectedCompNames: Set, selectSource?: SelectSourceType ) { - if (selectedCompNames.size === 0 && this.selectedCompNames.size === 0) { - return; - } - this.changeState({ - selectedCompNames: selectedCompNames, - showPropertyPane: selectedCompNames.size > 0, - selectSource: selectSource, - }); + this.editorStore.getState().setSelectedCompNames(selectedCompNames, selectSource); } setSelectedBottomRes(name: string, type?: BottomResTypeEnum) { diff --git a/client/packages/lowcoder/src/comps/editorStore.test.ts b/client/packages/lowcoder/src/comps/editorStore.test.ts new file mode 100644 index 0000000000..40756c7973 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore.test.ts @@ -0,0 +1,41 @@ +import { createEditorStore } from "./editorStore"; + +describe("editorStore", () => { + test("updates selection and property pane together", () => { + const store = createEditorStore(); + const selectedCompNames = new Set(["button1"]); + + store.getState().setSelectedCompNames(selectedCompNames, "leftPanel"); + + expect(store.getState().selectedCompNames).toEqual(selectedCompNames); + expect(store.getState().selectedCompNames).not.toBe(selectedCompNames); + expect(store.getState().selectSource).toBe("leftPanel"); + expect(store.getState().showPropertyPane).toBe(true); + + store.getState().setSelectedCompNames(new Set()); + expect(store.getState().selectedCompNames.size).toBe(0); + expect(store.getState().showPropertyPane).toBe(false); + }); + + test("does not notify subscribers for an unchanged selection", () => { + const store = createEditorStore(); + const subscriber = jest.fn(); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); + store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); + + expect(subscriber).toHaveBeenCalledTimes(1); + unsubscribe(); + }); + + test("creates isolated editor stores", () => { + const firstStore = createEditorStore(); + const secondStore = createEditorStore(); + + firstStore.getState().setSelectedCompNames(new Set(["button1"])); + + expect(firstStore.getState().selectedCompNames).toEqual(new Set(["button1"])); + expect(secondStore.getState().selectedCompNames.size).toBe(0); + }); +}); diff --git a/client/packages/lowcoder/src/comps/editorStore.tsx b/client/packages/lowcoder/src/comps/editorStore.tsx new file mode 100644 index 0000000000..50ea3d8ffb --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore.tsx @@ -0,0 +1,84 @@ +import React, { createContext, type PropsWithChildren, useContext } from "react"; +import { useStore } from "zustand"; +import { createStore, type StoreApi } from "zustand/vanilla"; + +export type SelectSourceType = + | "editor" + | "leftPanel" + | "addComp" + | "rightPanel" + | "alignComp" + | "layoutComp" + | "nestComp" + | "deleteComp" + | "moveComp" + | "renameComp" + | "resizeComp"; + +export type EditorStoreState = { + selectedCompNames: Set; + selectSource?: SelectSourceType; + showPropertyPane: boolean; + setSelectedCompNames: ( + selectedCompNames: Set, + selectSource?: SelectSourceType + ) => void; + setShowPropertyPane: (showPropertyPane: boolean) => void; +}; + +export type EditorStoreApi = StoreApi; + +function areSetsEqual(left: Set, right: Set) { + return left.size === right.size && Array.from(left).every((name) => right.has(name)); +} + +export function createEditorStore(): EditorStoreApi { + return createStore((set) => ({ + selectedCompNames: new Set(), + selectSource: undefined, + showPropertyPane: false, + setSelectedCompNames: (selectedCompNames, selectSource) => + set((state) => { + if (selectedCompNames.size === 0 && state.selectedCompNames.size === 0) { + return state; + } + + const showPropertyPane = selectedCompNames.size > 0; + if ( + areSetsEqual(state.selectedCompNames, selectedCompNames) && + state.selectSource === selectSource && + state.showPropertyPane === showPropertyPane + ) { + return state; + } + + return { + selectedCompNames: new Set(selectedCompNames), + selectSource, + showPropertyPane, + }; + }), + setShowPropertyPane: (showPropertyPane) => + set((state) => + state.showPropertyPane === showPropertyPane ? state : { showPropertyPane } + ), + })); +} + +const EditorStoreContext = createContext(undefined); + +type EditorStoreProviderProps = PropsWithChildren<{ + store: EditorStoreApi; +}>; + +export function EditorStoreProvider({ children, store }: EditorStoreProviderProps) { + return {children}; +} + +export function useEditorStore(selector: (state: EditorStoreState) => T): T { + const store = useContext(EditorStoreContext); + if (!store) { + throw new Error("useEditorStore must be used inside EditorStoreProvider"); + } + return useStore(store, selector); +} diff --git a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx index 7f6a0bdeaf..692c0778fc 100644 --- a/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx +++ b/client/packages/lowcoder/src/comps/generators/uiCompBuilder.tsx @@ -34,6 +34,7 @@ import { getNpmPackageMeta } from "../utils/remote"; import { compPluginsList } from "constants/compPluginConstants"; import Select from "antd/es/select"; import { useMergeCompStyles } from "@lowcoder-ee/util/hooks"; +import { useEditorStore } from "../editorStore"; export type NewChildren>> = ChildrenCompMap & { @@ -77,7 +78,11 @@ export const ExtendedPropertyView = React.memo(< const [compVersions, setCompVersions] = useState(['latest']); const [compName, setCompName] = useState(''); const editorState = useContext(EditorContext); - const selectedComp = values(editorState?.selectedComps())[0]; + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const selectedComp = useMemo( + () => values(editorState?.selectedComps(selectedCompNames))[0], + [editorState, selectedCompNames] + ); const compType = selectedComp?.children?.compType?.getView() as UICompType; const timeoutRef = useRef(); diff --git a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx index b3ce3126b9..c95bd51686 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx @@ -38,6 +38,7 @@ import UrlParamsHookComp from "./UrlParamsHookComp"; import { UtilsComp } from "./utilsComp"; import { ScreenInfoHookComp } from "./screenInfoComp"; import { ChatControllerComp } from "./chatControllerComp"; +import { useEditorStore } from "comps/editorStore"; window._ = _; window.dayjs = dayjs; @@ -133,7 +134,12 @@ function SelectHookView(props: { comp: ConstructorToComp; }) { const editorState = useContext(EditorContext); - const selectedComp = editorState.selectedComp(); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const selectSource = useEditorStore((state) => state.selectSource); + const selectedComp = useMemo( + () => editorState.selectedComp(selectedCompNames), + [editorState, selectedCompNames] + ); // Memoize the comp tree calculation const compTree = useMemo(() => { @@ -159,8 +165,8 @@ function SelectHookView(props: { props.compType !== "drawer" && props.compType !== "meeting") || !selectedComp || - (editorState.selectSource !== "addComp" && - editorState.selectSource !== "leftPanel") + (selectSource !== "addComp" && + selectSource !== "leftPanel") ) { return; } else if ( @@ -191,7 +197,7 @@ function SelectHookView(props: { ); } } - }, [selectedComp, editorState.selectSource, allChildComp]); + }, [selectedComp, selectSource, allChildComp]); return (
diff --git a/client/packages/lowcoder/src/index.sdk.ts b/client/packages/lowcoder/src/index.sdk.ts index 63a34b1e9a..6dbb7619cf 100644 --- a/client/packages/lowcoder/src/index.sdk.ts +++ b/client/packages/lowcoder/src/index.sdk.ts @@ -24,6 +24,7 @@ export * from "comps/utils/methodUtils"; export { useUserViewMode } from "util/hooks"; export * from "comps/editorState"; +export * from "comps/editorStore"; export * from "redux/store/store"; // util diff --git a/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx b/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx index 968fde3426..3138eb332a 100644 --- a/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx +++ b/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx @@ -16,6 +16,7 @@ import { ExampleContext } from "../ExampleContext"; import { trans } from "i18n"; import { EditorContext, EditorState } from "comps/editorState"; import { RootComp } from "comps/comps/rootComp"; +import { createEditorStore, EditorStoreProvider } from "comps/editorStore"; const Wrapper = styled.div` border: 1px solid #d7d9e0; @@ -191,7 +192,8 @@ const externalState: ExternalEditorContextState = { appType: AppTypeEnum.Application, }; -const editorState = new EditorState(new RootComp({ value: {} }), () => {}); +const editorStore = createEditorStore(); +const editorState = new EditorState(new RootComp({ value: {} }), () => {}, undefined, false, editorStore); export default function Example(props: IProps) { const { @@ -251,7 +253,9 @@ export default function Example(props: IProps) { - {view} + + {view} +
{!hideSettings && ( diff --git a/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx b/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx index 50bd0935aa..3e189018ff 100644 --- a/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx +++ b/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx @@ -9,6 +9,7 @@ import { RootComp } from "comps/comps/rootComp"; import { useMemo } from "react"; import { lazyLoadComp } from "@lowcoder-ee/comps/comps/lazyLoadComp/lazyLoadComp"; import { LoadingBarHideTrigger } from "@lowcoder-ee/util/hideLoading"; +import { createEditorStore, EditorStoreProvider } from "comps/editorStore"; type CompInfo = UICompManifest & { key: string }; const groups: Partial> = {}; @@ -50,7 +51,8 @@ const Wrapper = styled.div` } `; -const editorState = new EditorState(new RootComp({ value: {} }), () => {}); +const editorStore = createEditorStore(); +const editorState = new EditorState(new RootComp({ value: {} }), () => {}, undefined, false, editorStore); export default function ComponentPlayground() { window.__LOWCODER_ORG__ = {}; @@ -75,13 +77,15 @@ export default function ComponentPlayground() {
- - } - layoutInfo={compManifest.layoutInfo || { h: 5, w: 5 }} - /> - + + + } + layoutInfo={compManifest.layoutInfo || { h: 5, w: 5 }} + /> + +
); diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index 1b403d6823..3a18f0e135 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -32,6 +32,7 @@ import type { UICompType } from "comps/uiCompRegistry"; import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; import { DataNode, EventDataNode } from "antd/es/tree"; import { isAggregationApp } from "util/appUtils"; +import { useEditorStore } from "comps/editorStore"; import Modal from "antd/es/modal/Modal"; const CollapseTitleWrapper = styled.div` @@ -282,6 +283,7 @@ const LeftContentWrapper = styled.div` export const LeftContent = (props: LeftContentProps) => { const { uiComp } = props; const editorState = useContext(EditorContext); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); const [expandedKeys, setExpandedKeys] = useState>([]); const [showData, setShowData] = useState([]); @@ -483,8 +485,8 @@ export const LeftContent = (props: LeftContentProps) => { : editorState.getHooksComp().getUITree(); const explorerData: NodeItem[] = getTree(tree, []); let selectedKeys = []; - if (editorState.selectedCompNames.size === 1) { - const key = Object.keys(editorState.selectedComps())[0]; + if (selectedCompNames.size === 1) { + const key = Object.keys(editorState.selectedComps(selectedCompNames))[0]; const parentKeys = getParentNodeKeysByKey(explorerData, key); if (parentKeys && parentKeys.length) { let needSet = false; diff --git a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx index 907a78867e..ea7cfa90a8 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx @@ -29,6 +29,7 @@ import { default as Button } from "antd/es/button"; import { default as Divider } from "antd/es/divider"; import { default as Dropdown } from "antd/es/dropdown"; import { default as Flex } from "antd/es/flex"; +import { useEditorStore } from "comps/editorStore"; import { default as Input } from "antd/es/input"; import { default as Menu } from "antd/es/menu"; import { default as Space } from "antd/es/space"; @@ -152,6 +153,7 @@ const CustomDropdown = styled(Dropdown)` export const LeftLayersContent = (props: LeftLayersContentProps) => { const { uiComp } = props; const editorState = useContext(EditorContext); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); const [expandedKeys, setExpandedKeys] = useState>([]); const dispatch = useDispatch(); const applicationId = useApplicationId(); @@ -426,7 +428,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { const selectedComponentsOnCanvas: string[] = []; const compTree = editorState.getUIComp().getTree(); const explorerData: NodeItem[] = getTree(compTree, []); - for (let value of editorState.selectedCompNames) { + for (let value of selectedCompNames) { for (let key of explorerData) { if (key.title === value) { selectedComponentsOnCanvas.push(key.key); @@ -434,7 +436,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { } } setCheckedKeys(selectedComponentsOnCanvas); - }, [editorState]); + }, [editorState, selectedCompNames]); // make sure to handle the selectedActionKey for the changed input fields /* useEffect(() => { diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index 60edc7249f..ee749cd796 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -65,6 +65,7 @@ import { AppSettingContext, AppSettingType } from "@lowcoder-ee/comps/utils/appS import { getBrandingSetting } from "@lowcoder-ee/redux/selectors/enterpriseSelectors"; import Flex from "antd/es/flex"; import { PreviewContainerID } from "constants/domLocators"; +import { useEditorStore } from "comps/editorStore"; // import { BottomSkeleton } from "./bottom/BottomContent"; const Header = lazy( @@ -412,6 +413,7 @@ function EditorView(props: EditorViewProps) { const { uiComp } = props; const params = useParams(); const editorState = useContext(EditorContext); + const showPropertyPane = useEditorStore((state) => state.showPropertyPane); const { readOnly, hideHeader } = useContext(ExternalEditorContext); const application = useSelector(currentApplication); const isPublicApp = useSelector(isPublicApplication); @@ -781,7 +783,7 @@ function EditorView(props: EditorViewProps) { )} diff --git a/client/packages/lowcoder/src/pages/editor/right/PropertyView.tsx b/client/packages/lowcoder/src/pages/editor/right/PropertyView.tsx index f051a28983..4e878c4fcd 100644 --- a/client/packages/lowcoder/src/pages/editor/right/PropertyView.tsx +++ b/client/packages/lowcoder/src/pages/editor/right/PropertyView.tsx @@ -6,6 +6,7 @@ import { SelectedComps } from "lowcoder-design"; import { ScrollBar } from "lowcoder-design"; import { ReactNode, useContext } from "react"; import { trans } from "i18n"; +import { useEditorStore } from "comps/editorStore"; const ScrollWrapper = (props: { children: ReactNode }) => ( @@ -20,8 +21,8 @@ interface PropertyViewProps { export default function PropertyView(props: PropertyViewProps) { const { uiComp } = props; const editorState = useContext(EditorContext); - const selectedCompNames = editorState.selectedCompNames; - const selectedComp = editorState.selectedComp(); + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const selectedComp = editorState.selectedComp(selectedCompNames); const moduleLayoutComp = uiComp?.getModuleLayoutComp(); let propertyView; diff --git a/client/yarn.lock b/client/yarn.lock index 7e563bc56e..5b9e27e9bc 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -16885,6 +16885,7 @@ coolshapes-react@lowcoder-org/coolshapes-react: y-websocket: ^3.0.0 yjs: ^13.6.27 zod: ^3.25.76 + zustand: ^5.0.14 languageName: unknown linkType: soft @@ -25957,6 +25958,27 @@ coolshapes-react@lowcoder-org/coolshapes-react: languageName: node linkType: hard +"zustand@npm:^5.0.14": + version: 5.0.14 + resolution: "zustand@npm:5.0.14" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 9b660a4cd510f1c0512a71cecbe6a03ce0c353da93391c83dbc4d4904d7d1b45ecf44791e235bea514450ee50ff771cabf45a9a49edeec44a5886a5cdf1a7d3d + languageName: node + linkType: hard + "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4" From 9d7d4f805acf5e8785416b423505a4ceb225dc4e Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Fri, 24 Jul 2026 01:30:42 +0500 Subject: [PATCH 2/3] refactor hook comps architecture for the zustand --- .../comps/containerComp/containerView.tsx | 10 ++- .../lowcoder/src/comps/comps/rootComp.tsx | 33 +------ .../comps/editorPropertySectionContext.tsx | 53 +++++++++++ .../lowcoder/src/comps/hooks/hookComp.tsx | 89 ++----------------- .../lowcoder/src/comps/hooks/hookSelection.ts | 71 +++++++++++++++ .../src/comps/utils/hookCompOperator.ts | 17 ++-- .../lowcoder/src/pages/editor/LeftContent.tsx | 5 +- .../src/pages/editor/LeftLayersContent.tsx | 7 +- 8 files changed, 159 insertions(+), 126 deletions(-) create mode 100644 client/packages/lowcoder/src/comps/editorPropertySectionContext.tsx create mode 100644 client/packages/lowcoder/src/comps/hooks/hookSelection.ts diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index 9cdb6a70ed..10ffd92e24 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -2,6 +2,7 @@ import { EditorContext, EditorState } from "comps/editorState"; import { sameTypeMap, stateComp, valueComp } from "comps/generators"; import { addMapChildAction, addMapCompChildAction } from "comps/generators/sameTypeMap"; import { hookCompCategory, HookCompType } from "comps/hooks/hookCompTypes"; +import { shouldOpenOverlayOnCreate } from "comps/hooks/hookSelection"; import { UICompLayoutInfo, uiCompRegistry, UICompType } from "comps/uiCompRegistry"; import { genRandomKey } from "comps/utils/idGenerator"; import { parseCompType } from "comps/utils/remote"; @@ -216,13 +217,16 @@ const onDrop = async ( // log.debug("layout: onDrop start. layout: ", layout, " items: ", items, " compType: ", compType); if (hookCompCategory(compType) === "ui") { const compName = editorState.getNameGenerator().genItemName(compType); + const hookValue = { + name: compName, + compType: compType as HookCompType, + ...(shouldOpenOverlayOnCreate(compType) && { comp: { visible: true } }), + }; editorState .getHooksComp() .dispatch( wrapActionExtraInfo( - editorState - .getHooksComp() - .pushAction({ name: compName, compType: compType as HookCompType }), + editorState.getHooksComp().pushAction(hookValue), { compInfos: [{ compName: compName, compType: compType, type: "add" }] } ) ); diff --git a/client/packages/lowcoder/src/comps/comps/rootComp.tsx b/client/packages/lowcoder/src/comps/comps/rootComp.tsx index cebfa67df6..a0f4ead0b7 100644 --- a/client/packages/lowcoder/src/comps/comps/rootComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/rootComp.tsx @@ -24,11 +24,6 @@ import { getGlobalSettings } from "comps/utils/globalSettings"; import { getCurrentTheme } from "comps/utils/themeUtil"; import { DataChangeResponderListComp } from "./dataChangeResponderComp"; import { FolderListComp } from "./folderListComp"; -import { - PropertySectionContext, - PropertySectionContextType, - PropertySectionState, -} from "lowcoder-design"; import RefTreeComp from "./refTreeComp"; import { ExternalEditorContext } from "util/context/ExternalEditorContext"; import { useUserViewMode } from "util/hooks"; @@ -39,7 +34,7 @@ import clsx from "clsx"; import { useUnmount } from "react-use"; import { AIHelperModal, AIHelperProvider } from "components/ai-helper"; import { createEditorStore, EditorStoreProvider } from "comps/editorStore"; -import { useStore } from "zustand"; +import { EditorPropertySectionProvider } from "comps/editorPropertySectionContext"; const EditorView = lazy( () => import("pages/editor/editorView"), @@ -68,8 +63,6 @@ const RootView = React.memo((props: RootViewProps) => { const { comp, isModuleRoot, ...divProps } = props; const [editorState, setEditorState] = useState(); const [editorStore] = useState(createEditorStore); - const selectedCompNames = useStore(editorStore, (state) => state.selectedCompNames); - const [propertySectionState, setPropertySectionState] = useState({}); const { readOnly } = useContext(ExternalEditorContext); const isUserViewMode = useUserViewMode(); const mountedRef = useRef(true); @@ -127,7 +120,6 @@ const RootView = React.memo((props: RootViewProps) => { useUnmount(() => { setEditorState(undefined); - setPropertySectionState({}); if (editorStateRef.current) { editorStateRef.current = undefined; } @@ -141,25 +133,6 @@ const RootView = React.memo((props: RootViewProps) => { [theme, themeId] ); - const propertySectionContextValue = useMemo(() => { - const compName = Object.keys(editorState?.selectedComps(selectedCompNames) || {})[0]; - return { - compName, - state: propertySectionState, - toggle: (compName: string, sectionName: string) => { - if (!mountedRef.current) return; - - setPropertySectionState((oldState) => { - const nextSectionState: PropertySectionState = { ...oldState }; - const compState = nextSectionState[compName] || {}; - compState[sectionName] = compState[sectionName] === false; - nextSectionState[compName] = compState; - return nextSectionState; - }); - }, - }; - }, [editorState, propertySectionState, selectedCompNames]); - if (!editorState && !isUserViewMode && readOnly) { return ; } @@ -177,7 +150,7 @@ const RootView = React.memo((props: RootViewProps) => { )} style={{height: '100%'}}> - + @@ -192,7 +165,7 @@ const RootView = React.memo((props: RootViewProps) => { - + ); diff --git a/client/packages/lowcoder/src/comps/editorPropertySectionContext.tsx b/client/packages/lowcoder/src/comps/editorPropertySectionContext.tsx new file mode 100644 index 0000000000..8fa1562414 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorPropertySectionContext.tsx @@ -0,0 +1,53 @@ +import { + PropertySectionContext, + type PropertySectionContextType, + type PropertySectionState, +} from "lowcoder-design"; +import React, { type PropsWithChildren, useCallback, useMemo, useState } from "react"; +import type { EditorState } from "./editorState"; +import { useEditorStore } from "./editorStore"; + +type EditorPropertySectionProviderProps = PropsWithChildren<{ + editorState: EditorState; +}>; + +export function EditorPropertySectionProvider({ + children, + editorState, +}: EditorPropertySectionProviderProps) { + const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const [propertySectionState, setPropertySectionState] = useState({}); + + const compName = useMemo( + () => Object.keys(editorState.selectedComps(selectedCompNames))[0] ?? "", + [editorState, selectedCompNames] + ); + + const toggle = useCallback((targetCompName: string, sectionName: string) => { + setPropertySectionState((oldState) => { + const oldCompState = oldState[targetCompName] ?? {}; + return { + ...oldState, + [targetCompName]: { + ...oldCompState, + [sectionName]: oldCompState[sectionName] === false, + }, + }; + }); + }, []); + + const contextValue = useMemo( + () => ({ + compName, + state: propertySectionState, + toggle, + }), + [compName, propertySectionState, toggle] + ); + + return ( + + {children} + + ); +} diff --git a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx index c95bd51686..59bf72ba88 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx @@ -1,8 +1,6 @@ import { CompName } from "components/CompName"; -import { getAllCompItems } from "comps/comps/containerBase/utils"; import { SimpleNameComp } from "comps/comps/simpleNameComp"; import { StringControl } from "comps/controls/codeControl"; -import { EditorContext } from "comps/editorState"; import { RemoteCompInfo } from "types/remoteComp"; import { simpleMultiComp, @@ -17,17 +15,14 @@ import { DrawerComp } from "comps/hooks/drawerComp"; import { remoteComp } from "comps/comps/remoteComp/remoteComp"; import { - HookCompConstructor, HookCompMapRawType, - HookCompType, } from "comps/hooks/hookCompTypes"; import { ModalComp } from "comps/hooks/modalComp"; import { trans } from "i18n"; import _ from "lodash"; import dayjs from "dayjs"; -import { ConstructorToComp } from "lowcoder-core"; import { ScrollBar, Section, sectionNames } from "lowcoder-design"; -import React, { useContext, useEffect, useMemo, useCallback } from "react"; +import React, { useEffect, useMemo, useCallback } from "react"; import { useInterval, useTitle, useWindowSize } from "react-use"; import { useCurrentUser } from "util/currentUser"; import { LocalStorageComp } from "./localStorageComp"; @@ -127,83 +122,17 @@ export const HookTmpComp = withTypeAndChildren(HookMap, "title", { name: SimpleNameComp, }); -function SelectHookView(props: { +type SelectHookViewProps = { children: React.ReactNode; compName: string; - compType: HookCompType; - comp: ConstructorToComp; -}) { - const editorState = useContext(EditorContext); - const selectedCompNames = useEditorStore((state) => state.selectedCompNames); - const selectSource = useEditorStore((state) => state.selectSource); - const selectedComp = useMemo( - () => editorState.selectedComp(selectedCompNames), - [editorState, selectedCompNames] - ); - - // Memoize the comp tree calculation - const compTree = useMemo(() => { - if (!props.comp || !(props.comp as any).getCompTree) return null; - return (props.comp as any).getCompTree(); - }, [props.comp]); - - // Memoize the child components calculation - const allChildComp = useMemo(() => { - if (!compTree) return {}; - return getAllCompItems(compTree); - }, [compTree]); +}; - // Memoize the click handler +function SelectHookView(props: SelectHookViewProps) { + const setSelectedCompNames = useEditorStore((state) => state.setSelectedCompNames); const handleClick = useCallback(() => { - editorState.setSelectedCompNames(new Set([props.compName])); - }, [editorState, props.compName]); - - // Select the modal and its subcomponents on the left to display the modal - useEffect(() => { - if ( - (props.compType !== "modal" && - props.compType !== "drawer" && - props.compType !== "meeting") || - !selectedComp || - (selectSource !== "addComp" && - selectSource !== "leftPanel") - ) { - return; - } else if ( - (selectedComp as any).children.comp === props.comp - ) { - if ((selectedComp as any).children.comp?.remoteInfo?.isRemote){ - return; - } - - // Select the current modal to display the modal - !props.comp.children.visible.getView().value && - props.comp.children.visible.dispatch( - props.comp.children.visible.changeChildAction("value", true) - ); - } else { - // all child components of modal - const selectChildComp = Object.values(allChildComp).find( - (child) => child === selectedComp - ); - const visible = props.comp.children.visible.getView().value; - if (selectChildComp && !visible) { - props.comp.children.visible.dispatch( - props.comp.children.visible.changeChildAction("value", true) - ); - } else if (!selectChildComp && visible) { - props.comp.children.visible.dispatch( - props.comp.children.visible.changeChildAction("value", false) - ); - } - } - }, [selectedComp, selectSource, allChildComp]); - - return ( -
- {props.children} -
- ); + setSelectedCompNames(new Set([props.compName])); + }, [props.compName, setSelectedCompNames]); + return
{props.children}
; } export class HookComp extends HookTmpComp { @@ -220,8 +149,6 @@ export class HookComp extends HookTmpComp { return ( {view} diff --git a/client/packages/lowcoder/src/comps/hooks/hookSelection.ts b/client/packages/lowcoder/src/comps/hooks/hookSelection.ts new file mode 100644 index 0000000000..918bcf8843 --- /dev/null +++ b/client/packages/lowcoder/src/comps/hooks/hookSelection.ts @@ -0,0 +1,71 @@ +import type { CompTree } from "comps/comps/containerBase/utils"; +import type { EditorState } from "comps/editorState"; +import type { HookCompType } from "./hookCompTypes"; + +const overlayHookTypes = new Set(["drawer", "modal", "meeting"]); + +export function shouldOpenOverlayOnCreate(compType: string) { + return compType === "drawer" || compType === "modal"; +} + +function containsSelectedComp(compTree: CompTree, selectedCompName: string): boolean { + const selectedHere = Object.values(compTree.items).some( + (child) => child.children.name.getView() === selectedCompName + ); + + return ( + selectedHere || + Object.values(compTree.children).some((childTree) => + containsSelectedComp(childTree, selectedCompName) + ) + ); +} + +function syncOverlayVisibility(editorState: EditorState, selectedCompNames: Set) { + if (selectedCompNames.size !== 1) { + return; + } + + const selectedCompName = selectedCompNames.values().next().value; + if (!selectedCompName) { + return; + } + + Object.values(editorState.getHooksComp().children).forEach((hookComp) => { + const compType = hookComp.children.compType.getView(); + if (!overlayHookTypes.has(compType)) { + return; + } + + const comp: any = hookComp.children.comp; + const selectedOverlay = hookComp.children.name.getView() === selectedCompName; + if (selectedOverlay && comp.remoteInfo?.isRemote) { + return; + } + + const visibleControl = comp.children?.visible; + if (!visibleControl) { + return; + } + + const compTree = comp.getCompTree?.(); + const shouldBeVisible = + selectedOverlay || + (compTree ? containsSelectedComp(compTree, selectedCompName) : false); + const isVisible = visibleControl.getView().value; + + if (isVisible !== shouldBeVisible) { + visibleControl.dispatch( + visibleControl.changeChildAction("value", shouldBeVisible) + ); + } + }); +} + +export function selectCompsFromLeftPanel( + editorState: EditorState, + selectedCompNames: Set +) { + editorState.setSelectedCompNames(selectedCompNames, "leftPanel"); + syncOverlayVisibility(editorState, selectedCompNames); +} diff --git a/client/packages/lowcoder/src/comps/utils/hookCompOperator.ts b/client/packages/lowcoder/src/comps/utils/hookCompOperator.ts index dd7e5bf3b0..05d344c685 100644 --- a/client/packages/lowcoder/src/comps/utils/hookCompOperator.ts +++ b/client/packages/lowcoder/src/comps/utils/hookCompOperator.ts @@ -1,6 +1,7 @@ import { HookComp } from "comps/hooks/hookComp"; import { EditorState } from "comps/editorState"; -import { singletonHookComp } from "comps/hooks/hookCompTypes"; +import { singletonHookComp } from "comps/hooks/hookCompTypes"; +import { shouldOpenOverlayOnCreate } from "comps/hooks/hookSelection"; import { wrapActionExtraInfo } from "lowcoder-core"; import { writeHookOnlyToClipboard, @@ -53,12 +54,14 @@ export class HookCompOperator { payload.hookItems.forEach((item) => { const newName = nameGenerator.genItemName(item.compType); - const dispatchPayload = { - ...(item.fullValue || {}), - name: newName, - compType: item.compType, - comp: item.comp, - }; + const dispatchPayload = { + ...(item.fullValue || {}), + name: newName, + compType: item.compType, + comp: shouldOpenOverlayOnCreate(item.compType) + ? { ...item.comp, visible: true } + : item.comp, + }; hooksComp.dispatch( wrapActionExtraInfo( diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index 3a18f0e135..ff103b58fd 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -17,6 +17,7 @@ import { } from "lowcoder-design"; import React, { ReactNode, useCallback, useContext, useMemo, useState } from "react"; import { hookCompCategory } from "comps/hooks/hookCompTypes"; +import { selectCompsFromLeftPanel } from "comps/hooks/hookSelection"; import _ from "lodash"; import styled from "styled-components"; import { leftCompListClassName } from "pages/tutorials/tutorialsConstant"; @@ -353,7 +354,7 @@ export const LeftContent = (props: LeftContentProps) => { const uiCollapseClick = useCallback( (compName: string) => { - editorState.setSelectedCompNames(new Set([compName]), "leftPanel"); + selectCompsFromLeftPanel(editorState, new Set([compName])); }, [editorState] ); @@ -620,4 +621,4 @@ export const LeftContent = (props: LeftContentProps) => { ); -}; \ No newline at end of file +}; diff --git a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx index ea7cfa90a8..7c2b356e55 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx @@ -30,6 +30,7 @@ import { default as Divider } from "antd/es/divider"; import { default as Dropdown } from "antd/es/dropdown"; import { default as Flex } from "antd/es/flex"; import { useEditorStore } from "comps/editorStore"; +import { selectCompsFromLeftPanel } from "comps/hooks/hookSelection"; import { default as Input } from "antd/es/input"; import { default as Menu } from "antd/es/menu"; import { default as Space } from "antd/es/space"; @@ -214,7 +215,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { const uiCollapseClick = useCallback( (compName: string) => { - editorState.setSelectedCompNames(new Set([compName]), "leftPanel"); + selectCompsFromLeftPanel(editorState, new Set([compName])); }, [editorState] ); @@ -450,7 +451,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { for (let key of e.checkedNodes){ checkedComponents.add(key.title); } - editorState.setSelectedCompNames(checkedComponents, "leftPanel"); + selectCompsFromLeftPanel(editorState, checkedComponents); } const getCheckedKeys = () => { @@ -613,4 +614,4 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { return {layerControlContent}; -}; \ No newline at end of file +}; From b56b0ecc44db2f8cbd633ffa8052c9f7073ae839 Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Sat, 25 Jul 2026 01:44:51 +0500 Subject: [PATCH 3/3] add zustand pattern + replace the components with the zustand selector where we are checking the editor mode --- .../lowcoder/src/comps/comps/avatarGroup.tsx | 5 +- .../src/comps/comps/buttonComp/buttonComp.tsx | 3 +- .../comps/comps/buttonComp/dropdownComp.tsx | 5 +- .../src/comps/comps/buttonComp/linkComp.tsx | 5 +- .../comps/comps/buttonComp/scannerComp.tsx | 9 +- .../comps/buttonComp/toggleButtonComp.tsx | 7 +- .../lowcoder/src/comps/comps/carouselComp.tsx | 5 +- .../comps/chatBoxComponent/chatBoxComp.tsx | 3 +- .../comps/comps/columnLayout/columnLayout.tsx | 7 +- .../comps/comps/commentComp/commentComp.tsx | 5 +- .../comps/comps/containerComp/cardComp.tsx | 5 +- .../comps/containerComp/containerComp.tsx | 5 +- .../comps/containerComp/containerView.tsx | 12 +- .../comps/containerComp/pageLayoutComp.tsx | 5 +- .../comps/containerComp/textContainerComp.tsx | 5 +- .../src/comps/comps/customComp/customComp.tsx | 3 +- .../src/comps/comps/dateComp/dateComp.tsx | 25 +-- .../src/comps/comps/dateComp/timeComp.tsx | 21 +- .../lowcoder/src/comps/comps/dividerComp.tsx | 5 +- .../src/comps/comps/fileComp/fileComp.tsx | 5 +- .../src/comps/comps/fileViewerComp.tsx | 5 +- .../src/comps/comps/formComp/formComp.tsx | 9 +- .../lowcoder/src/comps/comps/iconComp.tsx | 5 +- .../lowcoder/src/comps/comps/iframeComp.tsx | 5 +- .../lowcoder/src/comps/comps/imageComp.tsx | 5 +- .../comps/comps/jsonComp/jsonEditorComp.tsx | 7 +- .../comps/comps/jsonComp/jsonExplorerComp.tsx | 9 +- .../comps/comps/jsonComp/jsonLottieComp.tsx | 7 +- .../jsonSchemaFormComp/jsonSchemaFormComp.tsx | 9 +- .../src/comps/comps/listViewComp/listView.tsx | 5 +- .../listViewComp/listViewPropertyView.tsx | 7 +- .../src/comps/comps/mediaComp/audioComp.tsx | 3 +- .../src/comps/comps/mediaComp/videoComp.tsx | 3 +- .../comps/comps/meetingComp/controlButton.tsx | 9 +- .../src/comps/comps/navComp/navComp.tsx | 3 +- .../comps/numberInputComp/numberInputComp.tsx | 9 +- .../numberInputComp/sliderCompConstants.tsx | 5 +- .../src/comps/comps/progressCircleComp.tsx | 5 +- .../lowcoder/src/comps/comps/progressComp.tsx | 5 +- .../lowcoder/src/comps/comps/qrCodeComp.tsx | 5 +- .../lowcoder/src/comps/comps/ratingComp.tsx | 7 +- .../responsiveLayout/responsiveLayout.tsx | 5 +- .../src/comps/comps/richTextEditorComp.tsx | 5 +- .../lowcoder/src/comps/comps/rootComp.tsx | 80 ++------ .../selectInputComp/cascaderContants.tsx | 9 +- .../selectInputComp/radioCompConstants.tsx | 9 +- .../selectInputComp/segmentedControl.tsx | 7 +- .../selectInputComp/selectCompConstants.tsx | 9 +- .../comps/selectInputComp/stepControl.tsx | 7 +- .../src/comps/comps/shapeComp/shapeComp.tsx | 9 +- .../src/comps/comps/signatureComp.tsx | 7 +- .../comps/comps/splitLayout/splitLayout.tsx | 5 +- .../lowcoder/src/comps/comps/switchComp.tsx | 3 +- .../src/comps/comps/tableComp/tableComp.tsx | 3 +- .../comps/comps/tableLiteComp/tableComp.tsx | 3 +- .../comps/comps/tabs/tabbedContainerComp.tsx | 5 +- .../src/comps/comps/tagsComp/tagsCompView.tsx | 5 +- .../lowcoder/src/comps/comps/textComp.tsx | 4 +- .../comps/comps/textInputComp/inputComp.tsx | 7 +- .../comps/comps/textInputComp/mentionComp.tsx | 9 +- .../comps/textInputComp/passwordComp.tsx | 7 +- .../comps/textInputComp/textAreaComp.tsx | 7 +- .../comps/comps/timelineComp/timelineComp.tsx | 5 +- .../lowcoder/src/comps/comps/timerComp.tsx | 5 +- .../src/comps/comps/treeComp/treeComp.tsx | 9 +- .../comps/comps/treeComp/treeSelectComp.tsx | 9 +- .../actionSelector/executeQueryAction.tsx | 6 + .../src/comps/controls/codeControl.tsx | 53 ++--- .../src/comps/controls/codeTextControl.tsx | 4 +- .../lowcoder/src/comps/editorState.tsx | 96 +++++---- .../lowcoder/src/comps/editorStore.test.ts | 41 ---- .../lowcoder/src/comps/editorStore.tsx | 84 -------- .../src/comps/editorStore/bottomResSlice.ts | 33 ++++ .../src/comps/editorStore/context.tsx | 39 ++++ .../src/comps/editorStore/deviceSlice.ts | 28 +++ .../src/comps/editorStore/editorModeSlice.ts | 21 ++ .../lowcoder/src/comps/editorStore/index.ts | 8 + .../src/comps/editorStore/interactionSlice.ts | 39 ++++ .../src/comps/editorStore/selectionSlice.ts | 50 +++++ .../src/comps/editorStore/store.test.ts | 184 ++++++++++++++++++ .../lowcoder/src/comps/editorStore/store.ts | 17 ++ .../lowcoder/src/comps/editorStore/types.ts | 31 +++ .../queries/queryComp/queryPropertyView.tsx | 9 +- .../src/layout/compSelectionWrapper.tsx | 7 +- .../src/pages/ComponentDoc/common/Example.tsx | 2 +- .../src/pages/ComponentPlayground/index.tsx | 2 +- .../src/pages/common/previewHeader.tsx | 13 +- .../lowcoder/src/pages/editor/LeftContent.tsx | 5 +- .../src/pages/editor/bottom/BottomContent.tsx | 11 +- .../src/pages/editor/bottom/BottomSidebar.tsx | 4 +- .../src/pages/editor/bottom/BottomTabs.tsx | 4 +- 91 files changed, 842 insertions(+), 454 deletions(-) delete mode 100644 client/packages/lowcoder/src/comps/editorStore.test.ts delete mode 100644 client/packages/lowcoder/src/comps/editorStore.tsx create mode 100644 client/packages/lowcoder/src/comps/editorStore/bottomResSlice.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/context.tsx create mode 100644 client/packages/lowcoder/src/comps/editorStore/deviceSlice.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/editorModeSlice.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/index.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/interactionSlice.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/selectionSlice.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/store.test.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/store.ts create mode 100644 client/packages/lowcoder/src/comps/editorStore/types.ts diff --git a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx index f370a4ef99..e3685c44fa 100644 --- a/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx +++ b/client/packages/lowcoder/src/comps/comps/avatarGroup.tsx @@ -13,6 +13,7 @@ import styled from "styled-components"; import { useContext, ReactElement, useEffect } from "react"; import { MultiCompBuilder, stateComp, withDefault } from "../generators"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { IconControl } from "../controls/iconControl"; import { ColorControl } from "../controls/colorControl"; import { optionsControl } from "../controls/optionsControl"; @@ -150,7 +151,7 @@ let AvatarGroupBasicComp = (function () { )}) .setPropertyViewFn((children) => ( <> - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.avatars.propertyView({})} @@ -175,7 +176,7 @@ let AvatarGroupBasicComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.avatar.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx index bd019ab66c..c4b577a18b 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonComp.tsx @@ -3,6 +3,7 @@ import { dropdownControl } from "comps/controls/dropdownControl"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { IconControl } from "comps/controls/iconControl"; import { CompNameContext, EditorContext, EditorState } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { withDefault } from "comps/generators"; import { NewChildren, UICompBuilder } from "comps/generators/uiCompBuilder"; import { @@ -146,7 +147,7 @@ type ChildrenType = NewChildren>; const ButtonPropertyView = React.memo((props: { children: ChildrenType }) => { - const { editorModeStatus } = useContext(EditorContext); + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); return ( <>
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx index ec2fa482e0..af116de3a1 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx @@ -11,6 +11,7 @@ import { Section, sectionNames } from "lowcoder-design"; import { trans } from "i18n"; import React, { ReactElement, useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import styled from "styled-components"; import EllipsisOutlined from "@ant-design/icons/EllipsisOutlined"; import { IconControl } from "comps/controls/iconControl"; @@ -256,7 +257,7 @@ const DropdownTmpComp = (function () { {children.options.propertyView({})}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{!children.onlyMenu.getView() && !children.onlyIcon.getView() ? children.onEvent.getPropertyView() @@ -267,7 +268,7 @@ const DropdownTmpComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.text.propertyView({ label: trans("label") })} diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx index b96fe77ea2..67f9f1745f 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx @@ -22,6 +22,7 @@ import { hasIcon } from "comps/utils"; import { RefControl } from "comps/controls/refControl"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import React, { useContext, useEffect } from "react"; const Link = styled(Button)<{ @@ -126,7 +127,7 @@ const LinkTmpComp = (function () { {children.text.propertyView({ label: trans("text") })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -141,7 +142,7 @@ const LinkTmpComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()}
{children.animationStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx index 8900ea914c..c36338a5bd 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/scannerComp.tsx @@ -34,6 +34,7 @@ import { arrayStringExposingStateControl, stringExposingStateControl } from "com import { BoolControl } from "comps/controls/boolControl"; import { RefControl } from "comps/controls/refControl"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const Error = styled.div` color: #f5222d; @@ -290,8 +291,8 @@ const ScannerTmpComp = (function () { {children.text.propertyView({ label: trans("text") })}
- {(useContext(EditorContext).editorModeStatus === "logic" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || + useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} @@ -315,8 +316,8 @@ const ScannerTmpComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || + useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.style.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx index ce82c9ff51..69b7109951 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/toggleButtonComp.tsx @@ -26,6 +26,7 @@ import { BoolControl } from "comps/controls/boolControl"; import { RefControl } from "comps/controls/refControl"; import React, { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { Tooltip } from "antd"; const IconWrapper = styled.div` @@ -112,7 +113,7 @@ const ToggleTmpComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -143,8 +144,8 @@ const ToggleTmpComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || + useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.showBorder.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx index 5d3f6d77bb..1b748e904e 100644 --- a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx @@ -17,6 +17,7 @@ import { AnimationStyle, AnimationStyleType, CarouselStyle } from "comps/control import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; // TODO: dots at top position needs proper margin (should be the same as bottom position) @@ -90,7 +91,7 @@ let CarouselBasicComp = (function () { {children.data.propertyView({ label: trans("data") })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.onEvent.getPropertyView()} @@ -98,7 +99,7 @@ let CarouselBasicComp = (function () { {children.autoPlay.propertyView({ label: trans("carousel.autoPlay") })}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.showDots.propertyView({ label: trans("carousel.showDots") })} {children.dotPosition.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/chatBoxComponent/chatBoxComp.tsx b/client/packages/lowcoder/src/comps/comps/chatBoxComponent/chatBoxComp.tsx index d8a874cb3b..d4af2d83bd 100644 --- a/client/packages/lowcoder/src/comps/comps/chatBoxComponent/chatBoxComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/chatBoxComponent/chatBoxComp.tsx @@ -42,6 +42,7 @@ import { import { RefControl } from "comps/controls/refControl"; import { hiddenPropertyView } from "comps/utils/propertyUtils"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { trans } from "i18n"; import { ChatBoxView } from "./components/ChatBoxView"; @@ -187,7 +188,7 @@ const avatarStyleOptions = [ const ChatBoxPropertyView = React.memo((props: { children: any }) => { const { children } = props; - const editorMode = useContext(EditorContext).editorModeStatus; + const editorMode = useEditorStore((state) => state.editorModeStatus); const [messageStyleSegment, setMessageStyleSegment] = useState("own"); const [avatarStyleSegment, setAvatarStyleSegment] = useState("own"); diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx index 2fc3fbdd62..ef94a64c77 100644 --- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx @@ -37,6 +37,7 @@ import { BoolCodeControl, NumberControl, StringControl } from "comps/controls/co import { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils"; import { DisabledContext } from "comps/generators/uiCompBuilder"; @@ -259,14 +260,14 @@ export const ResponsiveLayoutBaseComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.getPropertyView()} @@ -291,7 +292,7 @@ export const ResponsiveLayoutBaseComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx b/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx index f3b14959c9..00dc12c71c 100644 --- a/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/commentComp/commentComp.tsx @@ -28,6 +28,7 @@ import { doubleClickEvent, } from "comps/controls/eventHandlerControl"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; // Introducing styles @@ -387,7 +388,7 @@ let CommentBasicComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.value.propertyView({ @@ -418,7 +419,7 @@ let CommentBasicComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.sendCommentAble.getView() && children.buttonText.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx index c8661fa969..62c12b0ade 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx @@ -11,6 +11,7 @@ import { BoolCodeControl, StringControl } from "comps/controls/codeControl"; import { BoolControl } from "comps/controls/boolControl"; import { useContext, useEffect, useRef, useState } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { Card } from "antd"; import styled from "styled-components"; import { AnimationStyle, AnimationStyleType, CardHeaderStyle, CardHeaderStyleType, CardStyle, CardStyleType } from "comps/controls/styleControlConstants"; @@ -278,7 +279,7 @@ export const ContainerBaseComp = (function () { .setPropertyViewFn((children) => { return ( <> - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.size.propertyView({ @@ -337,7 +338,7 @@ export const ContainerBaseComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.cardType.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx index 1fa38cdc5c..0ebdda9787 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx @@ -16,6 +16,7 @@ import { BoolCodeControl } from "comps/controls/codeControl"; import { DisabledContext } from "comps/generators/uiCompBuilder"; import React, { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AnimationStyle } from "@lowcoder-ee/comps/controls/styleControlConstants"; import { styleControl } from "@lowcoder-ee/comps/controls/styleControl"; @@ -34,14 +35,14 @@ export const ContainerBaseComp = (function () { .setPropertyViewFn((children) => { return ( <> - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)}
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index 10ffd92e24..39404ad63f 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -333,10 +333,10 @@ const GridItemWrapper = React.memo(React.forwardRef( props: React.PropsWithChildren>, ref: React.ForwardedRef ) => { - const editorState = useContext(EditorContext); + const disableInteract = useEditorStore((state) => state.disableInteract); const { children, ...divProps } = props; return ( - + {props.children} ); @@ -360,6 +360,8 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { const [currentRowHeight, setRowHeight] = useState(positionParams.rowHeight || DEFAULT_ROW_HEIGHT); const editorState = useContext(EditorContext); const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const isDragging = useEditorStore((state) => state.isDragging); + const showGridLines = useEditorStore((state) => state.isDragging || state.forceShowGrid); const { readOnly } = useContext(ExternalEditorContext); const appSettingsComp = editorState?.getAppSettingsComp().getView(); @@ -538,7 +540,7 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { style={props.style} scrollContainerRef={props.scrollContainerRef} width={width ?? 0} - showGridLines={editorState?.showGridLines() && (isDroppable || enableGridLines)} + showGridLines={showGridLines && (isDroppable || enableGridLines)} isRowCountLocked={isRowCountLocked} isDraggable={isDraggable} isResizable={isResizable} @@ -596,12 +598,12 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => { minHeight={props.minHeight} bgColor={props.bgColor} radius={props.radius} - hintPlaceholder={!editorState?.isDragging && !readOnly && props.hintPlaceholder} + hintPlaceholder={!isDragging && !readOnly && props.hintPlaceholder} selectedSize={_.size(containerSelectNames)} clickItem={clickItem} isCanvas={props.isCanvas} showName={props.showName} - disableDirectionKey={editorState?.isDragging || readOnly} + disableDirectionKey={isDragging || readOnly} > {itemViews} diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/pageLayoutComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/pageLayoutComp.tsx index efa3dc6b3d..d4ed87e6f7 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/pageLayoutComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/pageLayoutComp.tsx @@ -12,6 +12,7 @@ import { BoolCodeControl } from "comps/controls/codeControl"; import { DisabledContext } from "comps/generators/uiCompBuilder"; import React, { useContext, useEffect, useState } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { ContainerChildren, @@ -40,7 +41,7 @@ export const ContainerBaseComp = (function () { .setPropertyViewFn((children) => { return ( <> - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)} @@ -48,7 +49,7 @@ export const ContainerBaseComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/textContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/textContainerComp.tsx index 653363f017..f4ed3ee5d8 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/textContainerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/textContainerComp.tsx @@ -23,6 +23,7 @@ import { styleControl } from "comps/controls/styleControl"; import { AnimationStyle, TextContainerStyle } from "comps/controls/styleControlConstants"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { alignWithJustifyControl } from "comps/controls/alignControl"; const typeOptions = [ @@ -76,12 +77,12 @@ export const ContainerBaseComp = (function () { {children.text.propertyView({})}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.container.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx index dd9d6489f5..52b23dbe11 100644 --- a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx @@ -12,6 +12,7 @@ import { EventData, EventTypeEnum } from "./types"; import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AnimationStyle, AnimationStyleType, CustomStyle, CustomStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants"; import { styleControl } from "@lowcoder-ee/comps/controls/styleControl"; // TODO: eventually to embedd in container so we have styling? @@ -243,7 +244,7 @@ const CustomCompBase = new UICompBuilder(childrenMap, (props, dispatch) => { .setPropertyViewFn((children) => { return ( <> - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.model.propertyView({ label: trans("customComp.data") })} {children.code.propertyView({ label: trans("customComp.code"), language: "html" })} diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx index e26bf4ab69..dd9b5af740 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx @@ -50,6 +50,7 @@ import { RefControl } from "comps/controls/refControl"; // import { CommonPickerMethods } from "antd/es/date-picker/generatePicker/interface"; import { DateRangeUIView } from "comps/comps/dateComp/dateRangeUIView"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { dropdownControl } from "comps/controls/dropdownControl"; import { timeZoneOptions } from "./timeZone"; import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; @@ -312,7 +313,7 @@ const DatePickerTmpCmp = new UICompBuilder(childrenMap, (props) => { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{requiredPropertyView(children)} {children.showValidationWhenEmpty.propertyView({ @@ -333,24 +334,24 @@ const DatePickerTmpCmp = new UICompBuilder(childrenMap, (props) => { )} {/*{commonAdvanceSection(children, children.dateType.value === "date")}*/} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && children.label.getPropertyView()} + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && children.label.getPropertyView()} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{formatPropertyView({ children, placeholder: DATE_FORMAT })} {children.placeholder.propertyView({ label: trans("date.placeholderText") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{timeFields(children, isMobile)} {children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && !isMobile && commonAdvanceSection(children)} + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && !isMobile && commonAdvanceSection(children)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} @@ -534,7 +535,7 @@ let DateRangeTmpCmp = (function () { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{requiredPropertyView(children)} {children.showValidationWhenEmpty.propertyView({ @@ -554,24 +555,24 @@ let DateRangeTmpCmp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && children.label.getPropertyView()} + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && children.label.getPropertyView()} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{formatPropertyView({ children })} {children.placeholder.propertyView({ label: trans("date.placeholderText") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{timeFields(children, isMobile)} {children.suffixIcon.propertyView({ label: trans("button.suffixIcon") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && commonAdvanceSection(children)} + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && commonAdvanceSection(children)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx index c5b8ecc6b6..2f00c8fbaa 100644 --- a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx @@ -54,6 +54,7 @@ import { RefControl } from "comps/controls/refControl"; import { TimePickerProps } from "antd/es/time-picker"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { dropdownControl } from "comps/controls/dropdownControl"; import { timeZoneOptions } from "./timeZone"; import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators"; @@ -235,7 +236,7 @@ const TimePickerTmpCmp = new UICompBuilder(childrenMap, (props) => { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{requiredPropertyView(children)} {children.showValidationWhenEmpty.propertyView({ @@ -254,15 +255,15 @@ const TimePickerTmpCmp = new UICompBuilder(childrenMap, (props) => {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && children.label.getPropertyView()} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && children.label.getPropertyView()} + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{formatPropertyView({ children, placeholder: TIME_FORMAT })} {children.placeholder.propertyView({ label: trans("time.placeholderText") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{commonAdvanceSection(children)} {children.use12Hours.propertyView({ label: trans("prop.use12Hours") })} @@ -270,7 +271,7 @@ const TimePickerTmpCmp = new UICompBuilder(childrenMap, (props) => {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} @@ -413,7 +414,7 @@ const TimeRangeTmpCmp = (function () { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{requiredPropertyView(children)} {children.showValidationWhenEmpty.propertyView({ @@ -432,15 +433,15 @@ const TimeRangeTmpCmp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && children.label.getPropertyView()} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && children.label.getPropertyView()} + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{formatPropertyView({ children, placeholder: TIME_FORMAT })} {children.placeholder.propertyView({ label: trans("time.placeholderText") })}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{commonAdvanceSection(children)} {children.use12Hours.propertyView({ label: trans("prop.use12Hours") })} @@ -448,7 +449,7 @@ const TimeRangeTmpCmp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/dividerComp.tsx b/client/packages/lowcoder/src/comps/comps/dividerComp.tsx index 287b684091..a953ecf7c2 100644 --- a/client/packages/lowcoder/src/comps/comps/dividerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/dividerComp.tsx @@ -17,6 +17,7 @@ import { AutoHeightControl } from "comps/controls/autoHeightControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; type IProps = DividerProps & { $style: DividerStyleType; @@ -131,13 +132,13 @@ const DividerTempComp = migrateOldData( {children.title.propertyView({ label: trans("divider.title") })}
} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{!_.isEmpty(children.title.getView()) && diff --git a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx index b804c53410..dfd123896a 100644 --- a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx @@ -46,6 +46,7 @@ import { DraggerUpload } from "./draggerUpload"; import { ImageCaptureModal } from "./ImageCaptureModal"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { checkIsMobile } from "@lowcoder-ee/util/commonUtils"; import { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl"; @@ -535,7 +536,7 @@ let FileTmpComp = new UICompBuilder(childrenMap, (props, dispatch) => { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.uploadType.getView() !== "single" && children.maxFiles.propertyView({ label: trans("file.maxFiles") })} {commonValidationFields(children)} @@ -580,7 +581,7 @@ let FileTmpComp = new UICompBuilder(childrenMap, (props, dispatch) => { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()}
{children.animationStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx b/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx index 159595231d..d912010662 100644 --- a/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx @@ -12,6 +12,7 @@ import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps import { trans } from "i18n"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AutoHeightControl } from "../controls/autoHeightControl"; import { BoolControl } from "../controls/boolControl"; @@ -109,7 +110,7 @@ let FileViewerBasicComp = (function () { })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)} {showDataLoadingIndicatorsPropertyView(children)} @@ -124,7 +125,7 @@ let FileViewerBasicComp = (function () { )}
- {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx index bece24fe23..a30b103952 100644 --- a/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/formComp/formComp.tsx @@ -7,6 +7,7 @@ import { import { Section, sectionNames } from "lowcoder-design"; import { genQueryId } from "comps/utils/idGenerator"; import { CompNameContext, EditorContext, EditorState } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { withMethodExposing } from "comps/generators/withMethodExposing"; import { ContainerPlaceholder } from "lowcoder-design"; import { @@ -214,7 +215,7 @@ const FormBaseComp = (function () { {children.resetAfterSubmit.propertyView({ label: trans("formComp.resetAfterSubmit") })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -225,7 +226,7 @@ const FormBaseComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.getPropertyView()} @@ -233,14 +234,14 @@ const FormBaseComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.initialData.propertyView({ label: trans("formComp.initialData") })} {children.invalidFormMessage.propertyView({ label: trans("formComp.invalidFormMessage") })}
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.stylePropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx index 8cc3716e16..3c335c212d 100644 --- a/client/packages/lowcoder/src/comps/comps/iconComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx @@ -31,6 +31,7 @@ import { } from "../controls/eventHandlerControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AssetType, IconscoutControl } from "@lowcoder-ee/comps/controls/iconscoutControl"; import { dropdownControl } from "../controls/dropdownControl"; import { useCompClickEventHandler } from "../utils/useCompClickEventHandler"; @@ -166,7 +167,7 @@ let IconBasicComp = (function () { })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -174,7 +175,7 @@ let IconBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.propertyView({ label: trans("iconComp.autoSize"), diff --git a/client/packages/lowcoder/src/comps/comps/iframeComp.tsx b/client/packages/lowcoder/src/comps/comps/iframeComp.tsx index c401653d16..9ca680e2ae 100644 --- a/client/packages/lowcoder/src/comps/comps/iframeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/iframeComp.tsx @@ -12,6 +12,7 @@ import log from "loglevel"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const Wrapper = styled.div<{$style: IframeStyleType; $animationStyle:AnimationStyleType}>` width: 100%; @@ -73,7 +74,7 @@ let IFrameCompBase = new UICompBuilder( {children.url.propertyView({ label: "Source URL", placeholder: "https://example.com", tooltip: trans("iframe.URLDesc") })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)} {children.allowDownload.propertyView({ label: trans("iframe.allowDownload") })} @@ -85,7 +86,7 @@ let IFrameCompBase = new UICompBuilder(
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/imageComp.tsx b/client/packages/lowcoder/src/comps/comps/imageComp.tsx index 8bc246a2b1..735af1ffff 100644 --- a/client/packages/lowcoder/src/comps/comps/imageComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/imageComp.tsx @@ -34,6 +34,7 @@ import { DEFAULT_IMG_URL } from "util/stringUtils"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { StringControl } from "../controls/codeControl"; import { PositionControl } from "comps/controls/dropdownControl"; import { dropdownControl } from "../controls/dropdownControl"; @@ -259,7 +260,7 @@ let ImageBasicComp = new UICompBuilder(childrenMap, (props) => { {children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({})}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -274,7 +275,7 @@ let ImageBasicComp = new UICompBuilder(childrenMap, (props) => {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
diff --git a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonEditorComp.tsx b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonEditorComp.tsx index 8b1d4c8400..35bb8ff082 100644 --- a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonEditorComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonEditorComp.tsx @@ -20,6 +20,7 @@ import { } from "base/codeEditor/codeMirror"; import { useExtensions } from "base/codeEditor/extensions"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl"; import { BoolControl } from "@lowcoder-ee/comps/controls/boolControl"; @@ -177,9 +178,9 @@ let JsonEditorTmpComp = (function () { }); }) .setPropertyViewFn((children) => { - const editorContext = useContext(EditorContext); - const isLogicMode = editorContext.editorModeStatus === "logic" || editorContext.editorModeStatus === "both"; - const isLayoutMode = editorContext.editorModeStatus === "layout" || editorContext.editorModeStatus === "both"; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); + const isLogicMode = editorModeStatus === "logic" || editorModeStatus === "both"; + const isLayoutMode = editorModeStatus === "layout" || editorModeStatus === "both"; return ( <> diff --git a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonExplorerComp.tsx b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonExplorerComp.tsx index f929dd68ce..e130092704 100644 --- a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonExplorerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonExplorerComp.tsx @@ -10,6 +10,7 @@ import { ArrayOrJSONObjectControl, NumberControl } from "comps/controls/codeCont import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { useContext, useEffect } from "react"; import { AnimationStyle, AnimationStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants"; import { styleControl } from "@lowcoder-ee/comps/controls/styleControl"; @@ -96,7 +97,7 @@ let JsonExplorerTmpComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{hiddenPropertyView(children)} {children.expandToggle.propertyView({ label: trans("jsonExplorer.expandToggle") })} @@ -104,7 +105,7 @@ let JsonExplorerTmpComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.indent.propertyView({ label: trans("jsonExplorer.indent") })}
@@ -117,8 +118,8 @@ let JsonExplorerTmpComp = (function () { label: trans('prop.showVerticalScrollbar'), })}
} - {(useContext(EditorContext).editorModeStatus === 'layout' || - useContext(EditorContext).editorModeStatus === 'both') && ( + {(useEditorStore((state) => state.editorModeStatus) === 'layout' || + useEditorStore((state) => state.editorModeStatus) === 'both') && ( <>
{children.theme.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx index 92f3559368..5e6c4eec8d 100644 --- a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx @@ -19,6 +19,7 @@ import { } from "../../generators/withExposing"; import { defaultLottie } from "./jsonConstants"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AssetType, IconscoutControl } from "@lowcoder-ee/comps/controls/iconscoutControl"; import { DotLottie } from "@lottiefiles/dotlottie-react"; import { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl"; @@ -292,7 +293,7 @@ let JsonLottieTmpComp = (function () { {children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({})}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} {children.speed.propertyView({ label: trans("jsonLottie.speed")})} @@ -305,7 +306,7 @@ let JsonLottieTmpComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.autoHeight.getPropertyView()} {children.aspectRatio.propertyView({ @@ -316,7 +317,7 @@ let JsonLottieTmpComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/jsonSchemaFormComp.tsx b/client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/jsonSchemaFormComp.tsx index d5c956b85c..e2924ae99b 100644 --- a/client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/jsonSchemaFormComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/jsonSchemaFormComp.tsx @@ -24,6 +24,7 @@ import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps import { AutoHeightControl } from "../../controls/autoHeightControl"; import { useContext, useEffect, useRef, useState, createContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import ObjectFieldTemplate from './ObjectFieldTemplate'; import ArrayFieldTemplate from './ArrayFieldTemplate'; import { Select } from 'antd'; @@ -380,8 +381,8 @@ let FormBasicComp = (function () { : "JSONForms UI schema object for configuring controls, scopes, layouts, categories, rules, and renderer options. It should match the JSON Schema fields."; return ( <> - {(useContext(EditorContext).editorModeStatus === "logic" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || + useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.formType.propertyView({ radioButton: true, @@ -514,7 +515,7 @@ let FormBasicComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -524,7 +525,7 @@ let FormBasicComp = (function () { {showDataLoadingIndicatorsPropertyView(children)}
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.autoHeight.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx index 4b62a66b93..5dec51164e 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx @@ -1,5 +1,6 @@ import { default as Pagination } from "antd/es/pagination"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { BackgroundColorContext } from "comps/utils/backgroundColorContext"; import _ from "lodash"; import { ConstructorToView } from "lowcoder-core"; @@ -319,7 +320,7 @@ export function ListView(props: Props) { const children = comp.children; const ref = useRef(null); const editorState = useContext(EditorContext); - const isDragging = editorState.isDragging; + const isDragging = useEditorStore((state) => state.isDragging); const [listHeight, setListHeight] = useDelayState(0, isDragging); const dynamicHeight = useMemo(() => children.dynamicHeight.getView(), [children.dynamicHeight]); const heightUnitOfRow = useMemo( @@ -375,7 +376,7 @@ export function ListView(props: Props) { const commonLayout = comp.realSimpleContainer()!.children.layout.getView(); const isOneItem = - pageInfo.currentPageSize > 0 && (_.isEmpty(commonLayout) || editorState.isDragging); + pageInfo.currentPageSize > 0 && (_.isEmpty(commonLayout) || isDragging); const noOfRows = isOneItem ? 1 : Math.floor((pageInfo.currentPageSize + noOfColumns - 1) / noOfColumns); diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx index b3c3911ce1..9b3f7190d0 100644 --- a/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx +++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listViewPropertyView.tsx @@ -4,6 +4,7 @@ import { ListViewImplComp } from "./listViewComp"; import { ListCompType } from "./listViewUtils"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { disabledPropertyView, hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils"; type Props = { @@ -47,13 +48,13 @@ export function listPropertyView(compType: ListCompType) { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{comp.children.pagination.getPropertyView()}
)} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -64,7 +65,7 @@ export function listPropertyView(compType: ListCompType) {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.horizontalGridCells.propertyView({ label: trans('prop.horizontalGridCells'), diff --git a/client/packages/lowcoder/src/comps/comps/mediaComp/audioComp.tsx b/client/packages/lowcoder/src/comps/comps/mediaComp/audioComp.tsx index 9b64c1bbc2..d24c225622 100644 --- a/client/packages/lowcoder/src/comps/comps/mediaComp/audioComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/mediaComp/audioComp.tsx @@ -15,6 +15,7 @@ import { hiddenPropertyView, showDataLoadingIndicatorsPropertyView } from "comps import { mediaCommonChildren, mediaMethods } from "./mediaUtils"; import { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const Container = styled.div<{ $style: any; $animationStyle: AnimationStyleType }>` ${props => props.$style}; @@ -87,7 +88,7 @@ let AudioBasicComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} diff --git a/client/packages/lowcoder/src/comps/comps/mediaComp/videoComp.tsx b/client/packages/lowcoder/src/comps/comps/mediaComp/videoComp.tsx index a4f5fb751e..925148752c 100644 --- a/client/packages/lowcoder/src/comps/comps/mediaComp/videoComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/mediaComp/videoComp.tsx @@ -23,6 +23,7 @@ import { mediaCommonChildren, mediaMethods } from "./mediaUtils"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import styled, { css } from "styled-components"; const EventOptions = [ @@ -141,7 +142,7 @@ let VideoBasicComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.onEvent.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx index c5e7709c97..b5f1d82b6f 100644 --- a/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx +++ b/client/packages/lowcoder/src/comps/comps/meetingComp/controlButton.tsx @@ -3,6 +3,7 @@ import { dropdownControl } from "comps/controls/dropdownControl"; import { ButtonEventHandlerControl } from "comps/controls/eventHandlerControl"; import { IconControl } from "comps/controls/iconControl"; import { CompNameContext, EditorContext, EditorState } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { withDefault } from "comps/generators"; import { UICompBuilder } from "comps/generators/uiCompBuilder"; import _ from "lodash"; @@ -337,8 +338,8 @@ let ButtonTmpComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || + useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -348,8 +349,8 @@ let ButtonTmpComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || + useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.autoHeight.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx index 41006481e5..110d724d9b 100644 --- a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx @@ -35,6 +35,7 @@ import { trans } from "i18n"; import { useContext, useState, useCallback } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { createNavItemsControl } from "./components/NavItemsControl"; import { Layers } from "constants/Layers"; import { CanvasContainerID } from "constants/domLocators"; @@ -774,7 +775,7 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => { ); }) .setPropertyViewFn((children) => { - const mode = useContext(EditorContext).editorModeStatus; + const mode = useEditorStore((state) => state.editorModeStatus); const showLogic = mode === "logic" || mode === "both"; const showLayout = mode === "layout" || mode === "both"; const [styleSegment, setStyleSegment] = useState("normal"); diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx index fad9d36d14..693fb065bb 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx @@ -52,6 +52,7 @@ import { import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { migrateOldData } from "comps/generators/simpleGenerators"; import { fixOldInputCompData } from "../textInputComp/textInputConstants"; @@ -458,7 +459,7 @@ let NumberInputTmpComp = (function () { - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{requiredPropertyView(children)} {children.showValidationWhenEmpty.propertyView({label: trans("prop.showEmptyValidation")})} @@ -475,11 +476,11 @@ let NumberInputTmpComp = (function () { )} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( children.label.getPropertyView() )} - {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{children.step.propertyView({ label: trans("numberInput.step") })} {children.precision.propertyView({ label: trans("numberInput.precision") })} @@ -494,7 +495,7 @@ let NumberInputTmpComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx index c2a45c121a..7e1b3ea8ca 100644 --- a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx @@ -14,6 +14,7 @@ import { IconControl } from "comps/controls/iconControl"; import { trans } from "i18n"; import { memo, useCallback, useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const getStyle = (style: SliderStyleType, vertical: boolean, disabledStyle?: DisabledSliderStyleType) => { return css` @@ -112,7 +113,7 @@ export const SliderChildren = { }; const InteractionSection = memo(({ children }: { children: RecordConstructorToComp }) => { - const editorModeStatus = useContext(EditorContext).editorModeStatus; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); if (!["logic", "both"].includes(editorModeStatus)) { return null; @@ -130,7 +131,7 @@ const InteractionSection = memo(({ children }: { children: RecordConstructorToCo }); const LayoutSection = memo(({ children }: { children: RecordConstructorToComp }) => { - const editorModeStatus = useContext(EditorContext).editorModeStatus; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); if (!["layout", "both"].includes(editorModeStatus)) { return null; diff --git a/client/packages/lowcoder/src/comps/comps/progressCircleComp.tsx b/client/packages/lowcoder/src/comps/comps/progressCircleComp.tsx index e4e70a6327..8406d5dd81 100644 --- a/client/packages/lowcoder/src/comps/comps/progressCircleComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/progressCircleComp.tsx @@ -13,6 +13,7 @@ import { dropdownControl } from "../controls/dropdownControl"; import { NumberControl } from "../controls/codeControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { ProgressTypeOptions, StrokeLinecapOptions, @@ -184,13 +185,13 @@ let ProgressCircleTmpComp = (function () {
)} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/progressComp.tsx b/client/packages/lowcoder/src/comps/comps/progressComp.tsx index 1863c01508..6a01f75be3 100644 --- a/client/packages/lowcoder/src/comps/comps/progressComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/progressComp.tsx @@ -12,6 +12,7 @@ import { trans } from "i18n"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const getStyle = (style: ProgressStyleType) => { return css` @@ -76,7 +77,7 @@ const ProgressBasicComp = (function () { })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{hiddenPropertyView(children)} {children.showInfo.propertyView({ @@ -86,7 +87,7 @@ const ProgressBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/qrCodeComp.tsx b/client/packages/lowcoder/src/comps/comps/qrCodeComp.tsx index 1836bfe9e9..0d1a449210 100644 --- a/client/packages/lowcoder/src/comps/comps/qrCodeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/qrCodeComp.tsx @@ -14,6 +14,7 @@ import { StringControl } from "comps/controls/codeControl"; import { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { withDefault } from "../generators"; // TODO: add styling for image (size) @@ -90,7 +91,7 @@ let QRCodeBasicComp = (function () { })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{hiddenPropertyView(children)}
@@ -107,7 +108,7 @@ let QRCodeBasicComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/ratingComp.tsx b/client/packages/lowcoder/src/comps/comps/ratingComp.tsx index 025453c256..35ba4188f1 100644 --- a/client/packages/lowcoder/src/comps/comps/ratingComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/ratingComp.tsx @@ -17,6 +17,7 @@ import { trans } from "i18n"; import { useContext, useEffect, useRef } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const EventOptions = [changeEvent] as const; @@ -124,7 +125,7 @@ const RatingBasicComp = (function () { - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -140,11 +141,11 @@ const RatingBasicComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx index e4a7fd0b57..6d6cd8d6e2 100644 --- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx @@ -39,6 +39,7 @@ import { BoolCodeControl, NumberControl } from "comps/controls/codeControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils"; import { DisabledContext } from "comps/generators/uiCompBuilder"; @@ -301,14 +302,14 @@ export const ResponsiveLayoutBaseComp = (function () { })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx index 979b7fbfa3..3cb77593ae 100644 --- a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx @@ -27,6 +27,7 @@ import { RichTextEditorStyle, RichTextEditorStyleType } from "comps/controls/sty import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const localizeStyle = css` & .ql-snow { @@ -409,7 +410,7 @@ const RichTextEditorCompBase = new UICompBuilder(childrenMap, (props) => { - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -419,7 +420,7 @@ const RichTextEditorCompBase = new UICompBuilder(childrenMap, (props) => {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/rootComp.tsx b/client/packages/lowcoder/src/comps/comps/rootComp.tsx index a0f4ead0b7..8c63a97ea3 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, useRef, useState } from "react"; +import { HTMLAttributes, Suspense, lazy, useContext, useMemo, useState } from "react"; import { setFieldsNoTypeCheck } from "util/objectUtils"; import { AppSettingsComp } from "./appSettingsComp"; import { PreloadComp } from "./preLoadComp"; @@ -31,7 +31,6 @@ import React from "react"; import { isEqual } from "lodash"; import {LoadingBarHideTrigger} from "@lowcoder-ee/util/hideLoading"; import clsx from "clsx"; -import { useUnmount } from "react-use"; import { AIHelperModal, AIHelperProvider } from "components/ai-helper"; import { createEditorStore, EditorStoreProvider } from "comps/editorStore"; import { EditorPropertySectionProvider } from "comps/editorPropertySectionContext"; @@ -61,13 +60,20 @@ const childrenMap = { const RootView = React.memo((props: RootViewProps) => { const previewTheme = useContext(ThemeContext); const { comp, isModuleRoot, ...divProps } = props; - const [editorState, setEditorState] = useState(); const [editorStore] = useState(createEditorStore); + const [editorState, setEditorState] = useState( + () => + new EditorState( + comp, + (changeEditorStateFn) => { + setEditorState(changeEditorStateFn); + }, + isModuleRoot, + editorStore + ) + ); 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(); @@ -83,47 +89,13 @@ const RootView = React.memo((props: RootViewProps) => { previewTheme?.previewTheme ? "preview-theme" : 'default-theme-id' ); - useEffect(() => { - return () => { - mountedRef.current = false; - }; - }, []); - - useEffect(() => { - if (!mountedRef.current) return; - - const newEditorState = new EditorState(comp, (changeEditorStateFn) => { - if (mountedRef.current) { - setEditorState((oldState) => { - return (oldState ? changeEditorStateFn(oldState) : undefined) - }); - } - }, undefined, isModuleRoot, editorStore); - editorStateRef.current = newEditorState; - setEditorState(newEditorState); - - return () => { - if (editorStateRef.current) { - editorStateRef.current = undefined; - } - }; - }, [isModuleRoot, editorStore]); - - useEffect(() => { - if (!mountedRef.current || !editorState) return; - - if (prevCompRef.current !== comp) { - editorState.setComp(() => comp); - prevCompRef.current = comp; - } - }, [comp, editorState]); - - useUnmount(() => { - setEditorState(undefined); - if (editorStateRef.current) { - editorStateRef.current = undefined; - } - }); + const currentEditorState = useMemo( + () => + editorState.rootComp === comp + ? editorState + : setFieldsNoTypeCheck(editorState, { rootComp: comp }), + [comp, editorState] + ); const themeContextValue = useMemo( () => ({ @@ -133,16 +105,8 @@ const RootView = React.memo((props: RootViewProps) => { [theme, themeId] ); - if (!editorState && !isUserViewMode && readOnly) { - return ; - } - const SuspenseFallback = isModuleRoot ? : ; - if (!editorState) { - return SuspenseFallback; - } - return (
{ )} style={{height: '100%'}}> - + - + {Object.keys(comp.children.queries.children).map((key) => (
{comp.children.queries.children[key].getView()}
@@ -182,7 +146,6 @@ export class RootComp extends RootCompBase { preloaded = false; preloadId = ""; isModuleRoot = false; - private editorStateRef?: EditorState; getView() { if (!this.preloaded) { @@ -193,7 +156,6 @@ export class RootComp extends RootCompBase { clearPreload() { this.children.preload.clear(); - this.editorStateRef = undefined; } setModuleRoot(moduleRoot: boolean) { diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx index d88289c880..e67f766935 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/cascaderContants.tsx @@ -24,6 +24,7 @@ import { PaddingControl } from "../../controls/paddingControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; export const defaultDataSource = JSON.stringify(i18nObjs.cascader, null, " "); @@ -57,7 +58,7 @@ export const CascaderPropertyView = ( {placeholderPropertyView(children)}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -67,18 +68,18 @@ export const CascaderPropertyView = (
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{allowClearPropertyView(children)} {showSearchPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioCompConstants.tsx index 25efd5ef75..ceb180dc2d 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/radioCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/radioCompConstants.tsx @@ -22,6 +22,7 @@ import { RefControl } from "comps/controls/refControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { withDefault } from "@lowcoder-ee/comps/generators"; export const RadioLayoutOptions = [ @@ -66,7 +67,7 @@ export const RadioPropertyView = ( {children.defaultValue.propertyView({ label: trans("prop.defaultValue") })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
@@ -79,7 +80,7 @@ export const RadioPropertyView = ( )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.layout.propertyView({ label: trans("radio.options"), @@ -94,11 +95,11 @@ export const RadioPropertyView = (
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx index 78724cab55..38a6af194f 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx @@ -25,6 +25,7 @@ import { RefControl } from "comps/controls/refControl"; import { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { migrateOldData, withDefault } from "comps/generators/simpleGenerators"; import { fixOldInputCompData } from "../textInputComp/textInputConstants"; @@ -126,7 +127,7 @@ let SegmentedControlBasicComp = (function () { {children.defaultValue.propertyView({ label: trans("prop.defaultValue") })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
@@ -136,11 +137,11 @@ let SegmentedControlBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx index 08d7690fd9..2813c3e4ab 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx @@ -59,6 +59,7 @@ import { refMethods } from "comps/generators/withMethodExposing"; import { blurMethod, focusMethod } from "comps/utils/methodUtils"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { styleControl } from "comps/controls/styleControl"; import SupaDemoDisplay from "comps/utils/supademoDisplay"; @@ -345,7 +346,7 @@ export const SelectPropertyView = ( {placeholderPropertyView(children)}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <> <> @@ -361,10 +362,10 @@ export const SelectPropertyView = ( )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && children.label.getPropertyView()} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{allowClearPropertyView(children)} {showSearchPropertyView(children)} @@ -372,7 +373,7 @@ export const SelectPropertyView = ( )} {["layout", "both"].includes( - useContext(EditorContext).editorModeStatus + useEditorStore((state) => state.editorModeStatus) ) && ( <>
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx index 9fcfaa6216..ae4ee5949a 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx @@ -18,6 +18,7 @@ import { RefControl } from "comps/controls/refControl"; import { dropdownControl } from "comps/controls/dropdownControl"; import { useContext, useState, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils"; import { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl"; @@ -245,7 +246,7 @@ let StepControlBasicComp = (function () { {children.initialValue.propertyView({ label: trans("step.initialValue"), tooltip : trans("step.initialValueTooltip")})}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.onEvent.getPropertyView()} @@ -257,7 +258,7 @@ let StepControlBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.autoHeight.getPropertyView()} {children.size.propertyView({ @@ -298,7 +299,7 @@ let StepControlBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/shapeComp/shapeComp.tsx b/client/packages/lowcoder/src/comps/comps/shapeComp/shapeComp.tsx index fca9c6b8b8..58902036c3 100644 --- a/client/packages/lowcoder/src/comps/comps/shapeComp/shapeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/shapeComp/shapeComp.tsx @@ -25,6 +25,7 @@ import { BoolCodeControl } from "comps/controls/codeControl"; import { DisabledContext } from "comps/generators/uiCompBuilder"; import React, { useContext, useEffect, useState } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; export const ContainerBaseComp = (function () { const childrenMap = { @@ -49,8 +50,8 @@ export const ContainerBaseComp = (function () { IconType: "All", })}
- {(useContext(EditorContext).editorModeStatus === "logic" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || + useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)} @@ -58,8 +59,8 @@ export const ContainerBaseComp = (function () {
)} - {(useContext(EditorContext).editorModeStatus === "layout" || - useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "layout" || + useEditorStore((state) => state.editorModeStatus) === "both") && ( <>
{children.container.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/signatureComp.tsx b/client/packages/lowcoder/src/comps/comps/signatureComp.tsx index d113e08df4..9ece7e3dad 100644 --- a/client/packages/lowcoder/src/comps/comps/signatureComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/signatureComp.tsx @@ -29,6 +29,7 @@ import { formDataChildren, FormDataPropertyView } from "./formComp/formDataConst import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const Wrapper = styled.div<{ $style: SignatureStyleType; $isEmpty: boolean }>` height: 100%; @@ -235,7 +236,7 @@ let SignatureTmpComp = (function () { - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -245,11 +246,11 @@ let SignatureTmpComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/splitLayout/splitLayout.tsx b/client/packages/lowcoder/src/comps/comps/splitLayout/splitLayout.tsx index 04f3ca3d66..13ca60d16c 100644 --- a/client/packages/lowcoder/src/comps/comps/splitLayout/splitLayout.tsx +++ b/client/packages/lowcoder/src/comps/comps/splitLayout/splitLayout.tsx @@ -17,6 +17,7 @@ import { trans } from "i18n"; import { ContainerBaseProps, gridItemCompToGridItems, InnerGrid } from "../containerComp/containerView"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils"; import { DisabledContext } from "comps/generators/uiCompBuilder"; @@ -202,13 +203,13 @@ export const SplitLayoutBaseComp = (function () { {children.columns.propertyView({ title: trans("splitLayout.column") })}
- {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && ( + {(useEditorStore((state) => state.editorModeStatus) === "logic" || useEditorStore((state) => state.editorModeStatus) === "both") && (
{disabledPropertyView(children)} {hiddenPropertyView(children)}
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.orientation.propertyView({ diff --git a/client/packages/lowcoder/src/comps/comps/switchComp.tsx b/client/packages/lowcoder/src/comps/comps/switchComp.tsx index 40bada394f..bf320822b9 100644 --- a/client/packages/lowcoder/src/comps/comps/switchComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/switchComp.tsx @@ -20,6 +20,7 @@ import { fixOldInputCompData } from "./textInputComp/textInputConstants"; import { useCallback, useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const EventOptions = [ changeEvent, @@ -140,7 +141,7 @@ let SwitchTmpComp = (function () { }); }) .setPropertyViewFn((children) => { - const editorModeStatus = useContext(EditorContext).editorModeStatus; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); const isLogicMode = ["logic", "both"].includes(editorModeStatus); const isLayoutMode = ["layout", "both"].includes(editorModeStatus); diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableComp.tsx index ac77c38e2b..83cea772db 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableComp.tsx @@ -62,6 +62,7 @@ import { RowColorComp, RowHeightComp, SortValue, TableChildrenView, TableInitCom import { useContext, useState } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; export class TableImplComp extends TableInitComp implements IContainer { private prevUnevaledValue?: string; @@ -565,7 +566,7 @@ let TableTmpComp = withViewFn(TableImplComp, (comp) => { const withEditorModeStatus = (Component:any) => (props:any) => { - const editorModeStatus = useContext(EditorContext).editorModeStatus; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); const {ref, ...otherProps} = props; return ; }; diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx index 652867aa9d..eb9af83ad1 100644 --- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx @@ -42,6 +42,7 @@ import { RowColorComp, RowHeightComp, TableChildrenView, TableInitComp } from ". import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { tableMethodExposings } from "./methods/tableMethodExposings"; import { buildSortedDataNode, buildFilteredDataNode, buildOriDisplayDataNode, buildColumnAggrNode } from "./nodes/dataNodes"; @@ -242,7 +243,7 @@ let TableTmpComp = withViewFn(TableImplComp, (comp) => { }); const withEditorModeStatus = (Component:any) => (props:any) => { - const editorModeStatus = useContext(EditorContext).editorModeStatus; + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); const {ref, ...otherProps} = props; return ; }; diff --git a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx index af7dd4acce..3f1c03b370 100644 --- a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx @@ -31,6 +31,7 @@ import { trans } from "i18n"; import { BoolCodeControl, NumberControl } from "comps/controls/codeControl"; import { DisabledContext } from "comps/generators/uiCompBuilder"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"; import { BoolControl } from "comps/controls/boolControl"; import { PositionControl,dropdownControl } from "comps/controls/dropdownControl"; @@ -351,7 +352,7 @@ export const TabbedContainerBaseComp = (function () { {children.selectedTabKey.propertyView({ label: trans("prop.defaultValue") })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -379,7 +380,7 @@ export const TabbedContainerBaseComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.placement.propertyView({ label: trans("tabbedContainer.placement"), radioButton: true })} diff --git a/client/packages/lowcoder/src/comps/comps/tagsComp/tagsCompView.tsx b/client/packages/lowcoder/src/comps/comps/tagsComp/tagsCompView.tsx index cb9c443325..27cf7e8a6d 100644 --- a/client/packages/lowcoder/src/comps/comps/tagsComp/tagsCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tagsComp/tagsCompView.tsx @@ -2,6 +2,7 @@ import styled from "styled-components"; import React, { useContext, useState, useRef, useEffect } from "react"; import { Tag, App } from "antd"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { PresetStatusColorTypes } from "antd/es/_util/colors"; import { hashToNum } from "util/stringUtils"; import { TagsCompOptionsControl } from "comps/controls/optionsControl"; @@ -343,7 +344,7 @@ const multiTags = (function () { {children.editable.propertyView({ label: "Editable" })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -351,7 +352,7 @@ const multiTags = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.style.getPropertyView()}
)} diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx index 708a4f4aa3..cec212e630 100644 --- a/client/packages/lowcoder/src/comps/comps/textComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx @@ -20,6 +20,7 @@ import { PaddingControl } from "../controls/paddingControl"; import React, { useContext, useEffect, useRef, useMemo } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { clickEvent, doubleClickEvent, eventHandlerControl } from "../controls/eventHandlerControl"; import { NewChildren } from "../generators/uiCompBuilder"; import { RecordConstructorToComp } from "lowcoder-core"; @@ -160,8 +161,7 @@ type ChildrenType = NewChildren>; const TextPropertyView = React.memo((props: { children: ChildrenType }) => { - const editorContext = useContext(EditorContext); - const editorModeStatus = useMemo(() => editorContext.editorModeStatus, [editorContext.editorModeStatus]); + const editorModeStatus = useEditorStore((state) => state.editorModeStatus); const basicSection = useMemo(() => (
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx index 0a3ca6bf63..0c92d28fc5 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx @@ -37,6 +37,7 @@ import { NumberControl } from "comps/controls/codeControl"; import React, { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; /** * Input Comp @@ -106,11 +107,11 @@ let InputBasicComp = new UICompBuilder(childrenMap, (props) => { - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{hiddenPropertyView(children)}
@@ -123,7 +124,7 @@ let InputBasicComp = new UICompBuilder(childrenMap, (props) => { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx index da307ce764..5e6d80aa1d 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/mentionComp.tsx @@ -56,6 +56,7 @@ import { import React, { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { migrateOldData } from "comps/generators/simpleGenerators"; const Wrapper = styled.div<{ @@ -228,7 +229,7 @@ let MentionTmpComp = (function () { {children.placeholder.propertyView({ label: trans("prop.placeholder"), })} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.mentionList.propertyView({ label: trans("mention.mentionList"), }) @@ -236,11 +237,11 @@ let MentionTmpComp = (function () {
- {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} @@ -259,7 +260,7 @@ let MentionTmpComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx index 4b9cf56389..4d6a8b53b6 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx @@ -41,6 +41,7 @@ import { hasIcon } from "comps/utils"; import { RefControl } from "comps/controls/refControl"; import React, { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { migrateOldData } from "comps/generators/simpleGenerators"; import { NumberControl } from "comps/controls/codeControl"; @@ -106,11 +107,11 @@ let PasswordTmpComp = (function () { - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{hiddenPropertyView(children)}
@@ -130,7 +131,7 @@ let PasswordTmpComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx index 049d5d88d7..f65ad36629 100644 --- a/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx @@ -37,6 +37,7 @@ import { NumberControl } from "comps/controls/codeControl"; import React, { useContext, useEffect } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { migrateOldData } from "comps/generators/simpleGenerators"; const TextAreaStyled = styled(TextArea)<{ @@ -126,11 +127,11 @@ let TextAreaTmpComp = (function () { - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.getPropertyView()} @@ -148,7 +149,7 @@ let TextAreaTmpComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx index 06e1ff1a4e..b5ca0380b1 100644 --- a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx @@ -49,6 +49,7 @@ import { timelineDate, timelineNode, TimelineDataTooltip } from "./timelineConst import { convertTimeLineData } from "./timelineUtils"; import { default as Timeline } from "antd/es/timeline"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { styled } from "styled-components"; import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler"; @@ -201,7 +202,7 @@ let TimeLineBasicComp = (function () { })}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.onEvent.getPropertyView()} {hiddenPropertyView(children)} @@ -209,7 +210,7 @@ let TimeLineBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.autoHeight.getPropertyView()} {!children.autoHeight.getView() && diff --git a/client/packages/lowcoder/src/comps/comps/timerComp.tsx b/client/packages/lowcoder/src/comps/comps/timerComp.tsx index e9fc26ad05..dd13bf985e 100644 --- a/client/packages/lowcoder/src/comps/comps/timerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/timerComp.tsx @@ -12,6 +12,7 @@ import styled from "styled-components"; import { useContext, useState, useEffect, useMemo } from "react"; import { stateComp } from "../generators"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { dropdownControl } from "../controls/dropdownControl"; import { stringExposingStateControl } from "comps/controls/codeStateControl"; import { BoolControl } from "comps/controls/boolControl"; @@ -229,7 +230,7 @@ const TimerCompPropertyView = React.memo((props: { }) => { return ( <> - {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{props.children.timerType.propertyView({ @@ -250,7 +251,7 @@ const TimerCompPropertyView = React.memo((props: { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{props.children.style.getPropertyView()} diff --git a/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx b/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx index a2747e0090..f6c47b0d9b 100644 --- a/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx @@ -28,6 +28,7 @@ import { TreeEventHandlerControl } from "comps/controls/eventHandlerControl"; import { trans } from "i18n"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { AutoHeightControl } from "@lowcoder-ee/comps/controls/autoHeightControl"; import { showDataLoadingIndicatorsPropertyView } from "@lowcoder-ee/comps/utils/propertyUtils"; @@ -145,7 +146,7 @@ let TreeBasicComp = (function () { {treeDataPropertyView(children)}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <> {formSection(children)}
@@ -164,7 +165,7 @@ let TreeBasicComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.autoHeight.getPropertyView()} {!children.autoHeight.getView() && @@ -178,9 +179,9 @@ let TreeBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (children.label.getPropertyView())} + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (children.label.getPropertyView())} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx index 482316e0fe..bca3d28e75 100644 --- a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx @@ -37,6 +37,7 @@ import { BaseSelectRef } from "rc-select"; import { RefControl } from "comps/controls/refControl"; import { useContext } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; const StyledTreeSelect = styled(TreeSelect)<{ $style: TreeSelectStyleType }>` width: 100%; @@ -160,7 +161,7 @@ let TreeBasicComp = (function () { {placeholderPropertyView(children)}
- {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["logic", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <> {formSection(children)}
@@ -179,7 +180,7 @@ let TreeBasicComp = (function () { )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && (
{children.expanded.propertyView({ label: trans("tree.expanded") })} {children.defaultExpandAll.propertyView({ label: trans("tree.defaultExpandAll") })} @@ -188,9 +189,9 @@ let TreeBasicComp = (function () {
)} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( children.label.getPropertyView() )} + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( children.label.getPropertyView() )} - {["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && ( + {["layout", "both"].includes(useEditorStore((state) => state.editorModeStatus)) && ( <>
{children.style.getPropertyView()}
{children.labelStyle.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx index 2ab7186edb..e924b39c63 100644 --- a/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx +++ b/client/packages/lowcoder/src/comps/controls/actionSelector/executeQueryAction.tsx @@ -2,6 +2,7 @@ import { executeQueryAction, routeByNameAction } from "lowcoder-core"; import { InputTypeEnum } from "comps/comps/moduleContainerComp/ioComp/inputListItemComp"; import { SimpleNameComp } from "comps/comps/simpleNameComp"; import { EditorContext, EditorState } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { MultiCompBuilder } from "comps/generators/multi"; import { BranchDiv, Dropdown } from "lowcoder-design"; import { BottomResTypeEnum } from "types/bottomRes"; @@ -18,6 +19,11 @@ const ExecuteQueryPropertyView = ({ comp: any, placement?: "query" | "table" }) => { + // subscribed so the EditorContext.Consumer below re-renders when the selected + // bottom resource changes (that field no longer flips EditorContext's identity) + useEditorStore((state) => state.selectedBottomResName); + useEditorStore((state) => state.selectedBottomResType); + const getQueryOptions = useCallback((editorState?: EditorState) => { if (!editorState) return []; const options: { diff --git a/client/packages/lowcoder/src/comps/controls/codeControl.tsx b/client/packages/lowcoder/src/comps/controls/codeControl.tsx index 36788b842d..72b9cc1fcb 100644 --- a/client/packages/lowcoder/src/comps/controls/codeControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/codeControl.tsx @@ -2,6 +2,7 @@ import type { EditorState } from "@codemirror/state"; import { isThemeColorKey } from "api/commonSettingApi"; import type { Language } from "base/codeEditor/codeEditorTypes"; import { EditorContext } from "comps/editorState"; +import { EditorStoreSelector } from "comps/editorStore"; import { withDefault } from "comps/generators/simpleGenerators"; import { CompExposingContext } from "comps/generators/withContext"; import { exposingDataForAutoComplete } from "comps/utils/exposingTypes"; @@ -195,31 +196,33 @@ export function codeControl< {(editorState) => ( {(exposingData) => ( - <> - - - - + state.forceShowGrid}> + {(forceShowGrid) => ( + + + + )} + )} )} diff --git a/client/packages/lowcoder/src/comps/controls/codeTextControl.tsx b/client/packages/lowcoder/src/comps/controls/codeTextControl.tsx index 5f6021d930..f77ded8993 100644 --- a/client/packages/lowcoder/src/comps/controls/codeTextControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/codeTextControl.tsx @@ -1,6 +1,7 @@ // import { CodeEditor } from "base/codeEditor/codeEditor"; import { CompParams } from "lowcoder-core"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { valueComp } from "comps/generators"; import { CompExposingContext } from "comps/generators/withContext"; import { exposingDataForAutoComplete } from "comps/utils/exposingTypes"; @@ -26,6 +27,7 @@ function CodeTextEditor(props: CodeTextEditorProps) { const { codeText, onChange, enableExposingDataAutoCompletion = false, ...params } = props; const compExposingData = useContext(CompExposingContext); const editorState = useContext(EditorContext); + const forceShowGrid = useEditorStore((state) => state.forceShowGrid); const expsoingData = useMemo(() => { if (enableExposingDataAutoCompletion) { @@ -47,7 +49,7 @@ function CodeTextEditor(props: CodeTextEditorProps) { exposingData={expsoingData} boostExposingData={compExposingData} onChange={(state) => onChange(state.doc.toString())} - enableClickCompName={editorState.forceShowGrid} + enableClickCompName={forceShowGrid} /> ); diff --git a/client/packages/lowcoder/src/comps/editorState.tsx b/client/packages/lowcoder/src/comps/editorState.tsx index 9fb08861e6..a146a73578 100644 --- a/client/packages/lowcoder/src/comps/editorState.tsx +++ b/client/packages/lowcoder/src/comps/editorState.tsx @@ -17,11 +17,13 @@ import { NameAndExposingInfo } from "./utils/exposingTypes"; import { checkName } from "./utils/rename"; import { trans } from "i18n"; import type { UiLayoutType } from "./comps/uiComp"; -import { getEditorModeStatus, saveCollisionStatus } from "util/localStorageUtil"; +import { saveCollisionStatus } from "util/localStorageUtil"; import { createEditorStore, type EditorStoreApi, type SelectSourceType, + type DeviceType, + type DeviceOrientation, } from "./editorStore"; type RootComp = InstanceType; @@ -38,8 +40,7 @@ export type CompInfo = { dataDesc: Record; }; -export type DeviceType = "desktop" | "tablet" | "mobile"; -export type DeviceOrientation = "landscape" | "portrait"; +export type { DeviceType, DeviceOrientation }; /** * All editor states are placed here and are still immutable. @@ -50,17 +51,7 @@ export type DeviceOrientation = "landscape" | "portrait"; */ export class EditorState { readonly rootComp: RootComp; - readonly editorModeStatus: string = ""; readonly collisionStatus: boolean = false; - readonly isDragging: boolean = false; - readonly draggingCompType: string = "button"; - readonly forceShowGrid: boolean = false; // show grid lines - readonly disableInteract: boolean = false; // disable comp's interaction (such as click button event) - readonly selectedBottomResName: string = ""; - readonly selectedBottomResType?: BottomResTypeEnum; - readonly showResultCompName: string = ""; - readonly deviceType: DeviceType = "desktop"; - readonly deviceOrientation: DeviceOrientation = "portrait"; private readonly setEditorState: ( fn: (editorState: EditorState) => EditorState @@ -69,13 +60,11 @@ export class EditorState { constructor( rootComp: RootComp, setEditorState: (fn: (editorState: EditorState) => EditorState) => void, - initialEditorModeStatus: string = getEditorModeStatus(), isModuleRoot: boolean = false, private readonly editorStore: EditorStoreApi = createEditorStore(), ) { this.rootComp = rootComp; this.setEditorState = setEditorState; - this.editorModeStatus = initialEditorModeStatus; // save collision status from app dsl to localstorage // but only for apps, not for modules (to prevent modules from overwriting the app's setting) @@ -84,9 +73,6 @@ export class EditorState { } } - /** - * use changeState most of the time, and you can use this method to get the latest editorState. (similar to react's setState method) - */ private changeStateFn(fn: (editorState: EditorState) => ChangeableProps) { this.setEditorState((oldState) => { const stateChanges = fn(oldState); @@ -94,10 +80,6 @@ export class EditorState { }); } - private changeState(params: ChangeableProps) { - this.changeStateFn(() => params); - } - get showPropertyPane() { return this.editorStore.getState().showPropertyPane; } @@ -110,6 +92,46 @@ export class EditorState { return this.editorStore.getState().selectSource; } + get isDragging() { + return this.editorStore.getState().isDragging; + } + + get draggingCompType() { + return this.editorStore.getState().draggingCompType; + } + + get forceShowGrid() { + return this.editorStore.getState().forceShowGrid; + } + + get disableInteract() { + return this.editorStore.getState().disableInteract; + } + + get editorModeStatus() { + return this.editorStore.getState().editorModeStatus; + } + + get selectedBottomResName() { + return this.editorStore.getState().selectedBottomResName; + } + + get selectedBottomResType() { + return this.editorStore.getState().selectedBottomResType; + } + + get showResultCompName() { + return this.editorStore.getState().showResultCompName; + } + + get deviceType() { + return this.editorStore.getState().deviceType; + } + + get deviceOrientation() { + return this.editorStore.getState().deviceOrientation; + } + getAllCompMap() { return { ...this.getAllHooksCompMap(), @@ -376,42 +398,35 @@ export class EditorState { } setEditorModeStatus(newEditorModeStatus: string) { - this.changeState({ editorModeStatus: newEditorModeStatus }); + this.editorStore.getState().setEditorModeStatus(newEditorModeStatus); } setDeviceType(type: DeviceType) { - this.changeState({ deviceType: type }); + this.editorStore.getState().setDeviceType(type); } setDeviceOrientation(orientation: DeviceOrientation) { - this.changeState({ deviceOrientation: orientation }); + this.editorStore.getState().setDeviceOrientation(orientation); } setDragging(dragging: boolean) { - if (this.isDragging === dragging) { - return; - } - this.changeState({ isDragging: dragging }); + this.editorStore.getState().setDragging(dragging); } setDraggingCompType(draggingComp: string) { - this.changeState({ draggingCompType: draggingComp, isDragging: true }); + this.editorStore.getState().setDraggingCompType(draggingComp); } setForceShowGrid(forceShowGrid: boolean) { - if (this.forceShowGrid !== forceShowGrid) { - this.changeState({ forceShowGrid }); - } + this.editorStore.getState().setForceShowGrid(forceShowGrid); } showGridLines() { - return this.isDragging || this.forceShowGrid; + return this.editorStore.getState().showGridLines(); } setDisableInteract(disableInteract: boolean) { - if (this.disableInteract !== disableInteract) { - this.changeState({ disableInteract }); - } + this.editorStore.getState().setDisableInteract(disableInteract); } setShowPropertyPane(showPropertyPane: boolean) { @@ -432,14 +447,11 @@ export class EditorState { } setSelectedBottomRes(name: string, type?: BottomResTypeEnum) { - this.changeState({ - selectedBottomResName: name, - selectedBottomResType: type, - }); + this.editorStore.getState().setSelectedBottomRes(name, type); } setShowResultCompName(showResultCompName: string | undefined) { - this.changeState({ showResultCompName }); + this.editorStore.getState().setShowResultCompName(showResultCompName); } getUIComp() { diff --git a/client/packages/lowcoder/src/comps/editorStore.test.ts b/client/packages/lowcoder/src/comps/editorStore.test.ts deleted file mode 100644 index 40756c7973..0000000000 --- a/client/packages/lowcoder/src/comps/editorStore.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createEditorStore } from "./editorStore"; - -describe("editorStore", () => { - test("updates selection and property pane together", () => { - const store = createEditorStore(); - const selectedCompNames = new Set(["button1"]); - - store.getState().setSelectedCompNames(selectedCompNames, "leftPanel"); - - expect(store.getState().selectedCompNames).toEqual(selectedCompNames); - expect(store.getState().selectedCompNames).not.toBe(selectedCompNames); - expect(store.getState().selectSource).toBe("leftPanel"); - expect(store.getState().showPropertyPane).toBe(true); - - store.getState().setSelectedCompNames(new Set()); - expect(store.getState().selectedCompNames.size).toBe(0); - expect(store.getState().showPropertyPane).toBe(false); - }); - - test("does not notify subscribers for an unchanged selection", () => { - const store = createEditorStore(); - const subscriber = jest.fn(); - const unsubscribe = store.subscribe(subscriber); - - store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); - store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); - - expect(subscriber).toHaveBeenCalledTimes(1); - unsubscribe(); - }); - - test("creates isolated editor stores", () => { - const firstStore = createEditorStore(); - const secondStore = createEditorStore(); - - firstStore.getState().setSelectedCompNames(new Set(["button1"])); - - expect(firstStore.getState().selectedCompNames).toEqual(new Set(["button1"])); - expect(secondStore.getState().selectedCompNames.size).toBe(0); - }); -}); diff --git a/client/packages/lowcoder/src/comps/editorStore.tsx b/client/packages/lowcoder/src/comps/editorStore.tsx deleted file mode 100644 index 50ea3d8ffb..0000000000 --- a/client/packages/lowcoder/src/comps/editorStore.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { createContext, type PropsWithChildren, useContext } from "react"; -import { useStore } from "zustand"; -import { createStore, type StoreApi } from "zustand/vanilla"; - -export type SelectSourceType = - | "editor" - | "leftPanel" - | "addComp" - | "rightPanel" - | "alignComp" - | "layoutComp" - | "nestComp" - | "deleteComp" - | "moveComp" - | "renameComp" - | "resizeComp"; - -export type EditorStoreState = { - selectedCompNames: Set; - selectSource?: SelectSourceType; - showPropertyPane: boolean; - setSelectedCompNames: ( - selectedCompNames: Set, - selectSource?: SelectSourceType - ) => void; - setShowPropertyPane: (showPropertyPane: boolean) => void; -}; - -export type EditorStoreApi = StoreApi; - -function areSetsEqual(left: Set, right: Set) { - return left.size === right.size && Array.from(left).every((name) => right.has(name)); -} - -export function createEditorStore(): EditorStoreApi { - return createStore((set) => ({ - selectedCompNames: new Set(), - selectSource: undefined, - showPropertyPane: false, - setSelectedCompNames: (selectedCompNames, selectSource) => - set((state) => { - if (selectedCompNames.size === 0 && state.selectedCompNames.size === 0) { - return state; - } - - const showPropertyPane = selectedCompNames.size > 0; - if ( - areSetsEqual(state.selectedCompNames, selectedCompNames) && - state.selectSource === selectSource && - state.showPropertyPane === showPropertyPane - ) { - return state; - } - - return { - selectedCompNames: new Set(selectedCompNames), - selectSource, - showPropertyPane, - }; - }), - setShowPropertyPane: (showPropertyPane) => - set((state) => - state.showPropertyPane === showPropertyPane ? state : { showPropertyPane } - ), - })); -} - -const EditorStoreContext = createContext(undefined); - -type EditorStoreProviderProps = PropsWithChildren<{ - store: EditorStoreApi; -}>; - -export function EditorStoreProvider({ children, store }: EditorStoreProviderProps) { - return {children}; -} - -export function useEditorStore(selector: (state: EditorStoreState) => T): T { - const store = useContext(EditorStoreContext); - if (!store) { - throw new Error("useEditorStore must be used inside EditorStoreProvider"); - } - return useStore(store, selector); -} diff --git a/client/packages/lowcoder/src/comps/editorStore/bottomResSlice.ts b/client/packages/lowcoder/src/comps/editorStore/bottomResSlice.ts new file mode 100644 index 0000000000..13af12c800 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/bottomResSlice.ts @@ -0,0 +1,33 @@ +import type { StateCreator } from "zustand/vanilla"; +import { BottomResTypeEnum } from "types/bottomRes"; +import type { EditorStoreState } from "./types"; + +export type BottomResSlice = { + selectedBottomResName: string; + selectedBottomResType?: BottomResTypeEnum; + showResultCompName: string | undefined; + setSelectedBottomRes: (name: string, type?: BottomResTypeEnum) => void; + setShowResultCompName: (showResultCompName: string | undefined) => void; +}; + +export const createBottomResSlice: StateCreator< + EditorStoreState, + [], + [], + BottomResSlice +> = (set) => ({ + selectedBottomResName: "", + selectedBottomResType: undefined, + showResultCompName: "", + setSelectedBottomRes: (selectedBottomResName, selectedBottomResType) => + set((state) => + state.selectedBottomResName === selectedBottomResName && + state.selectedBottomResType === selectedBottomResType + ? state + : { selectedBottomResName, selectedBottomResType } + ), + setShowResultCompName: (showResultCompName) => + set((state) => + state.showResultCompName === showResultCompName ? state : { showResultCompName } + ), +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/context.tsx b/client/packages/lowcoder/src/comps/editorStore/context.tsx new file mode 100644 index 0000000000..87c8f4fe18 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/context.tsx @@ -0,0 +1,39 @@ +import React, { createContext, type PropsWithChildren, type ReactNode, useContext } from "react"; +import { useStore } from "zustand"; +import type { EditorStoreApi, EditorStoreState } from "./types"; + +const EditorStoreContext = createContext(undefined); + +type EditorStoreProviderProps = PropsWithChildren<{ + store: EditorStoreApi; +}>; + +export function EditorStoreProvider({ children, store }: EditorStoreProviderProps) { + return {children}; +} + +function useEditorStoreApi(): EditorStoreApi { + const store = useContext(EditorStoreContext); + if (!store) { + throw new Error("useEditorStore must be used inside EditorStoreProvider"); + } + return store; +} + +export function useEditorStore(selector: (state: EditorStoreState) => T): T { + return useStore(useEditorStoreApi(), selector); +} + +/** + * Render-prop escape hatch for reading the store reactively from class components + * (e.g. Comp subclasses), which can't call the useEditorStore hook directly. + */ +export function EditorStoreSelector({ + selector, + children, +}: { + selector: (state: EditorStoreState) => T; + children: (value: T) => ReactNode; +}) { + return <>{children(useEditorStore(selector))}; +} diff --git a/client/packages/lowcoder/src/comps/editorStore/deviceSlice.ts b/client/packages/lowcoder/src/comps/editorStore/deviceSlice.ts new file mode 100644 index 0000000000..f5e04846f9 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/deviceSlice.ts @@ -0,0 +1,28 @@ +import type { StateCreator } from "zustand/vanilla"; +import type { EditorStoreState } from "./types"; + +export type DeviceType = "desktop" | "tablet" | "mobile"; +export type DeviceOrientation = "landscape" | "portrait"; + +export type DeviceSlice = { + deviceType: DeviceType; + deviceOrientation: DeviceOrientation; + setDeviceType: (deviceType: DeviceType) => void; + setDeviceOrientation: (deviceOrientation: DeviceOrientation) => void; +}; + +export const createDeviceSlice: StateCreator< + EditorStoreState, + [], + [], + DeviceSlice +> = (set) => ({ + deviceType: "desktop", + deviceOrientation: "portrait", + setDeviceType: (deviceType) => + set((state) => (state.deviceType === deviceType ? state : { deviceType })), + setDeviceOrientation: (deviceOrientation) => + set((state) => + state.deviceOrientation === deviceOrientation ? state : { deviceOrientation } + ), +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/editorModeSlice.ts b/client/packages/lowcoder/src/comps/editorStore/editorModeSlice.ts new file mode 100644 index 0000000000..2c9aa37af3 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/editorModeSlice.ts @@ -0,0 +1,21 @@ +import type { StateCreator } from "zustand/vanilla"; +import { getEditorModeStatus } from "util/localStorageUtil"; +import type { EditorStoreState } from "./types"; + +export type EditorModeSlice = { + editorModeStatus: string; + setEditorModeStatus: (editorModeStatus: string) => void; +}; + +export const createEditorModeSlice: StateCreator< + EditorStoreState, + [], + [], + EditorModeSlice +> = (set) => ({ + editorModeStatus: getEditorModeStatus(), + setEditorModeStatus: (editorModeStatus) => + set((state) => + state.editorModeStatus === editorModeStatus ? state : { editorModeStatus } + ), +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/index.ts b/client/packages/lowcoder/src/comps/editorStore/index.ts new file mode 100644 index 0000000000..6102934c1b --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/index.ts @@ -0,0 +1,8 @@ +export type { SelectSourceType, EditorStoreState, EditorStoreApi } from "./types"; +export type { SelectionSlice } from "./selectionSlice"; +export type { InteractionSlice } from "./interactionSlice"; +export type { EditorModeSlice } from "./editorModeSlice"; +export type { BottomResSlice } from "./bottomResSlice"; +export type { DeviceSlice, DeviceType, DeviceOrientation } from "./deviceSlice"; +export { createEditorStore } from "./store"; +export { EditorStoreProvider, useEditorStore, EditorStoreSelector } from "./context"; diff --git a/client/packages/lowcoder/src/comps/editorStore/interactionSlice.ts b/client/packages/lowcoder/src/comps/editorStore/interactionSlice.ts new file mode 100644 index 0000000000..528d89b6ca --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/interactionSlice.ts @@ -0,0 +1,39 @@ +import type { StateCreator } from "zustand/vanilla"; +import type { EditorStoreState } from "./types"; + +export type InteractionSlice = { + isDragging: boolean; + draggingCompType: string; + forceShowGrid: boolean; + disableInteract: boolean; + setDragging: (dragging: boolean) => void; + setDraggingCompType: (draggingCompType: string) => void; + setForceShowGrid: (forceShowGrid: boolean) => void; + setDisableInteract: (disableInteract: boolean) => void; + showGridLines: () => boolean; +}; + +export const createInteractionSlice: StateCreator< + EditorStoreState, + [], + [], + InteractionSlice +> = (set, get) => ({ + isDragging: false, + draggingCompType: "button", + forceShowGrid: false, + disableInteract: false, + setDragging: (dragging) => + set((state) => (state.isDragging === dragging ? state : { isDragging: dragging })), + setDraggingCompType: (draggingCompType) => + set({ draggingCompType, isDragging: true }), + setForceShowGrid: (forceShowGrid) => + set((state) => + state.forceShowGrid === forceShowGrid ? state : { forceShowGrid } + ), + setDisableInteract: (disableInteract) => + set((state) => + state.disableInteract === disableInteract ? state : { disableInteract } + ), + showGridLines: () => get().isDragging || get().forceShowGrid, +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/selectionSlice.ts b/client/packages/lowcoder/src/comps/editorStore/selectionSlice.ts new file mode 100644 index 0000000000..4902c8a9cc --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/selectionSlice.ts @@ -0,0 +1,50 @@ +import type { StateCreator } from "zustand/vanilla"; +import type { EditorStoreState, SelectSourceType } from "./types"; +import { areSetsEqual } from "./types"; + +export type SelectionSlice = { + selectedCompNames: Set; + selectSource?: SelectSourceType; + showPropertyPane: boolean; + setSelectedCompNames: ( + selectedCompNames: Set, + selectSource?: SelectSourceType + ) => void; + setShowPropertyPane: (showPropertyPane: boolean) => void; +}; + +export const createSelectionSlice: StateCreator< + EditorStoreState, + [], + [], + SelectionSlice +> = (set) => ({ + selectedCompNames: new Set(), + selectSource: undefined, + showPropertyPane: false, + setSelectedCompNames: (selectedCompNames, selectSource) => + set((state) => { + if (selectedCompNames.size === 0 && state.selectedCompNames.size === 0) { + return state; + } + + const showPropertyPane = selectedCompNames.size > 0; + if ( + areSetsEqual(state.selectedCompNames, selectedCompNames) && + state.selectSource === selectSource && + state.showPropertyPane === showPropertyPane + ) { + return state; + } + + return { + selectedCompNames: new Set(selectedCompNames), + selectSource, + showPropertyPane, + }; + }), + setShowPropertyPane: (showPropertyPane) => + set((state) => + state.showPropertyPane === showPropertyPane ? state : { showPropertyPane } + ), +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/store.test.ts b/client/packages/lowcoder/src/comps/editorStore/store.test.ts new file mode 100644 index 0000000000..17bef0e1ba --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/store.test.ts @@ -0,0 +1,184 @@ +import { createEditorStore } from "./store"; +import { BottomResTypeEnum } from "types/bottomRes"; + +describe("editorStore selection slice", () => { + test("updates selection and property pane together", () => { + const store = createEditorStore(); + const selectedCompNames = new Set(["button1"]); + + store.getState().setSelectedCompNames(selectedCompNames, "leftPanel"); + + expect(store.getState().selectedCompNames).toEqual(selectedCompNames); + expect(store.getState().selectedCompNames).not.toBe(selectedCompNames); + expect(store.getState().selectSource).toBe("leftPanel"); + expect(store.getState().showPropertyPane).toBe(true); + + store.getState().setSelectedCompNames(new Set()); + expect(store.getState().selectedCompNames.size).toBe(0); + expect(store.getState().showPropertyPane).toBe(false); + }); + + test("does not notify subscribers for an unchanged selection", () => { + const store = createEditorStore(); + const subscriber = jest.fn(); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); + store.getState().setSelectedCompNames(new Set(["button1"]), "leftPanel"); + + expect(subscriber).toHaveBeenCalledTimes(1); + unsubscribe(); + }); + + test("creates isolated editor stores", () => { + const firstStore = createEditorStore(); + const secondStore = createEditorStore(); + + firstStore.getState().setSelectedCompNames(new Set(["button1"])); + + expect(firstStore.getState().selectedCompNames).toEqual(new Set(["button1"])); + expect(secondStore.getState().selectedCompNames.size).toBe(0); + }); +}); + +describe("editorStore interaction slice", () => { + test("defaults to not dragging with grid lines hidden", () => { + const store = createEditorStore(); + + expect(store.getState().isDragging).toBe(false); + expect(store.getState().forceShowGrid).toBe(false); + expect(store.getState().draggingCompType).toBe("button"); + expect(store.getState().disableInteract).toBe(false); + expect(store.getState().showGridLines()).toBe(false); + }); + + test("showGridLines is true while dragging or forced", () => { + const store = createEditorStore(); + + store.getState().setDragging(true); + expect(store.getState().showGridLines()).toBe(true); + + store.getState().setDragging(false); + expect(store.getState().showGridLines()).toBe(false); + + store.getState().setForceShowGrid(true); + expect(store.getState().showGridLines()).toBe(true); + }); + + test("setDraggingCompType also marks dragging as true", () => { + const store = createEditorStore(); + + store.getState().setDraggingCompType("table"); + + expect(store.getState().draggingCompType).toBe("table"); + expect(store.getState().isDragging).toBe(true); + }); + + test("does not notify subscribers for unchanged interaction fields", () => { + const store = createEditorStore(); + const subscriber = jest.fn(); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setDragging(false); // already false + store.getState().setForceShowGrid(false); // already false + store.getState().setDisableInteract(false); // already false + + expect(subscriber).not.toHaveBeenCalled(); + unsubscribe(); + }); + + test("interaction state is isolated across store instances", () => { + const firstStore = createEditorStore(); + const secondStore = createEditorStore(); + + firstStore.getState().setForceShowGrid(true); + + expect(firstStore.getState().forceShowGrid).toBe(true); + expect(secondStore.getState().forceShowGrid).toBe(false); + }); +}); + +describe("editorStore editor mode slice", () => { + test("defaults to the persisted editor mode status", () => { + localStorage.setItem("editor_mode_status", "logic"); + const store = createEditorStore(); + + expect(store.getState().editorModeStatus).toBe("logic"); + localStorage.removeItem("editor_mode_status"); + }); + + test("does not notify subscribers for an unchanged editor mode", () => { + const store = createEditorStore(); + const subscriber = jest.fn(); + store.getState().setEditorModeStatus("both"); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setEditorModeStatus("both"); + + expect(subscriber).not.toHaveBeenCalled(); + unsubscribe(); + }); +}); + +describe("editorStore bottom res slice", () => { + test("selecting a bottom resource sets name and type together", () => { + const store = createEditorStore(); + + store.getState().setSelectedBottomRes("query1", BottomResTypeEnum.Query); + + expect(store.getState().selectedBottomResName).toBe("query1"); + expect(store.getState().selectedBottomResType).toBe(BottomResTypeEnum.Query); + }); + + test("setShowResultCompName accepts undefined to clear the result panel", () => { + const store = createEditorStore(); + + store.getState().setShowResultCompName("query1"); + expect(store.getState().showResultCompName).toBe("query1"); + + store.getState().setShowResultCompName(undefined); + expect(store.getState().showResultCompName).toBeUndefined(); + }); + + test("does not notify subscribers for an unchanged bottom resource selection", () => { + const store = createEditorStore(); + store.getState().setSelectedBottomRes("query1", BottomResTypeEnum.Query); + const subscriber = jest.fn(); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setSelectedBottomRes("query1", BottomResTypeEnum.Query); + + expect(subscriber).not.toHaveBeenCalled(); + unsubscribe(); + }); +}); + +describe("editorStore device slice", () => { + test("defaults to desktop portrait", () => { + const store = createEditorStore(); + + expect(store.getState().deviceType).toBe("desktop"); + expect(store.getState().deviceOrientation).toBe("portrait"); + }); + + test("updates device type and orientation independently", () => { + const store = createEditorStore(); + + store.getState().setDeviceType("mobile"); + store.getState().setDeviceOrientation("landscape"); + + expect(store.getState().deviceType).toBe("mobile"); + expect(store.getState().deviceOrientation).toBe("landscape"); + }); + + test("does not notify subscribers for an unchanged device type", () => { + const store = createEditorStore(); + const subscriber = jest.fn(); + const unsubscribe = store.subscribe(subscriber); + + store.getState().setDeviceType("desktop"); // already desktop + + expect(subscriber).not.toHaveBeenCalled(); + unsubscribe(); + }); +}); diff --git a/client/packages/lowcoder/src/comps/editorStore/store.ts b/client/packages/lowcoder/src/comps/editorStore/store.ts new file mode 100644 index 0000000000..5b9abf79bc --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/store.ts @@ -0,0 +1,17 @@ +import { createStore } from "zustand/vanilla"; +import type { EditorStoreApi, EditorStoreState } from "./types"; +import { createSelectionSlice } from "./selectionSlice"; +import { createInteractionSlice } from "./interactionSlice"; +import { createEditorModeSlice } from "./editorModeSlice"; +import { createBottomResSlice } from "./bottomResSlice"; +import { createDeviceSlice } from "./deviceSlice"; + +export function createEditorStore(): EditorStoreApi { + return createStore((...a) => ({ + ...createSelectionSlice(...a), + ...createInteractionSlice(...a), + ...createEditorModeSlice(...a), + ...createBottomResSlice(...a), + ...createDeviceSlice(...a), + })); +} diff --git a/client/packages/lowcoder/src/comps/editorStore/types.ts b/client/packages/lowcoder/src/comps/editorStore/types.ts new file mode 100644 index 0000000000..6b8154f599 --- /dev/null +++ b/client/packages/lowcoder/src/comps/editorStore/types.ts @@ -0,0 +1,31 @@ +import type { StoreApi } from "zustand/vanilla"; +import type { SelectionSlice } from "./selectionSlice"; +import type { InteractionSlice } from "./interactionSlice"; +import type { EditorModeSlice } from "./editorModeSlice"; +import type { BottomResSlice } from "./bottomResSlice"; +import type { DeviceSlice } from "./deviceSlice"; + +export type SelectSourceType = + | "editor" + | "leftPanel" + | "addComp" + | "rightPanel" + | "alignComp" + | "layoutComp" + | "nestComp" + | "deleteComp" + | "moveComp" + | "renameComp" + | "resizeComp"; + +export type EditorStoreState = SelectionSlice & + InteractionSlice & + EditorModeSlice & + BottomResSlice & + DeviceSlice; + +export type EditorStoreApi = StoreApi; + +export function areSetsEqual(left: Set, right: Set) { + return left.size === right.size && Array.from(left).every((name) => right.has(name)); +} diff --git a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx index fd4939da47..b282e481a2 100644 --- a/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx +++ b/client/packages/lowcoder/src/comps/queries/queryComp/queryPropertyView.tsx @@ -27,6 +27,7 @@ import { useSelector } from "react-redux"; import { getDataSource, getDataSourceTypes } from "redux/selectors/datasourceSelectors"; import { BottomResTypeEnum } from "types/bottomRes"; import { EditorContext } from "../../editorState"; +import { useEditorStore } from "../../editorStore"; import { JSTriggerTypeOptions, QueryComp, TriggerType, TriggerTypeOptions } from "../queryComp"; import { ResourceDropdown } from "../resourceDropdown"; import { NOT_SUPPORT_GUI_SQL_QUERY, SQLQuery } from "../sqlQuery/SQLQuery"; @@ -216,6 +217,8 @@ export const QueryGeneralPropertyView = (props: { }) => { const { comp, placement = "editor" } = props; const editorState = useContext(EditorContext); + const selectedBottomResName = useEditorStore((state) => state.selectedBottomResName); + const selectedBottomResType = useEditorStore((state) => state.selectedBottomResType); const datasource = useSelector(getDataSource); const children = comp.children; @@ -261,8 +264,8 @@ export const QueryGeneralPropertyView = (props: { })) .filter((option) => { // Filter out the current query under query - if (editorState.selectedBottomResType === BottomResTypeEnum.Query) { - return option.value !== editorState.selectedBottomResName; + if (selectedBottomResType === BottomResTypeEnum.Query) { + return option.value !== selectedBottomResName; } return true; }) || []; @@ -278,7 +281,7 @@ export const QueryGeneralPropertyView = (props: { } }); return options; - }, [editorState]); + }, [editorState, selectedBottomResName, selectedBottomResType]); return ( diff --git a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx index 739e87723f..2b95fb20ab 100644 --- a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx +++ b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx @@ -1,4 +1,4 @@ -import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { UICompType } from "comps/uiCompRegistry"; import { Layers } from "constants/Layers"; import { ModulePrimaryColor, PrimaryColor } from "constants/style"; @@ -10,7 +10,6 @@ import React, { MouseEvent, MouseEventHandler, useCallback, - useContext, useMemo, useRef, useState, @@ -280,7 +279,7 @@ export const CompSelectionWrapper = React.memo((props: { resizeIconSize: "small" | "normal"; }) => { const nameDivRef = useRef(null); - const editorState = useContext(EditorContext); + const showGridLines = useEditorStore((state) => state.isDragging || state.forceShowGrid); const [hover, setHover] = useState(false); // Cleanup on unmount @@ -329,7 +328,7 @@ export const CompSelectionWrapper = React.memo((props: { onMouseOut, onClick: props.onClick, $hover: hover || undefined, - $showDashLine: editorState.showGridLines() || props.hidden, + $showDashLine: showGridLines || props.hidden, $isSelected: props.isSelected, $isHidden: props.hidden, } diff --git a/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx b/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx index 3138eb332a..c12363bdab 100644 --- a/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx +++ b/client/packages/lowcoder/src/pages/ComponentDoc/common/Example.tsx @@ -193,7 +193,7 @@ const externalState: ExternalEditorContextState = { }; const editorStore = createEditorStore(); -const editorState = new EditorState(new RootComp({ value: {} }), () => {}, undefined, false, editorStore); +const editorState = new EditorState(new RootComp({ value: {} }), () => {}, false, editorStore); export default function Example(props: IProps) { const { diff --git a/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx b/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx index 3e189018ff..8c5fb8d0e4 100644 --- a/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx +++ b/client/packages/lowcoder/src/pages/ComponentPlayground/index.tsx @@ -52,7 +52,7 @@ const Wrapper = styled.div` `; const editorStore = createEditorStore(); -const editorState = new EditorState(new RootComp({ value: {} }), () => {}, undefined, false, editorStore); +const editorState = new EditorState(new RootComp({ value: {} }), () => {}, false, editorStore); export default function ComponentPlayground() { window.__LOWCODER_ORG__ = {}; diff --git a/client/packages/lowcoder/src/pages/common/previewHeader.tsx b/client/packages/lowcoder/src/pages/common/previewHeader.tsx index bf73cd9803..2e63d061aa 100644 --- a/client/packages/lowcoder/src/pages/common/previewHeader.tsx +++ b/client/packages/lowcoder/src/pages/common/previewHeader.tsx @@ -26,6 +26,7 @@ import MobileOutlined from "@ant-design/icons/MobileOutlined"; import TabletOutlined from "@ant-design/icons/TabletOutlined"; import DesktopOutlined from "@ant-design/icons/DesktopOutlined"; import { DeviceOrientation, DeviceType, EditorContext } from "@lowcoder-ee/comps/editorState"; +import { useEditorStore } from "@lowcoder-ee/comps/editorStore"; import { getBrandingSetting } from "@lowcoder-ee/redux/selectors/enterpriseSelectors"; import { buildMaterialPreviewURL } from "@lowcoder-ee/util/materialUtils"; @@ -150,6 +151,8 @@ export function HeaderProfile(props: { user: User }) { const PreviewHeaderComp = () => { const params = useParams(); const editorState = useContext(EditorContext); + const deviceType = useEditorStore((state) => state.deviceType); + const deviceOrientation = useEditorStore((state) => state.deviceOrientation); const user = useSelector(getUser); const application = useSelector(currentApplication); const isPublicApp = useSelector(isPublicApplication); @@ -237,20 +240,20 @@ const PreviewHeaderComp = () => { { value: 'tablet', icon: }, { value: 'desktop', icon: }, ]} - value={editorState.deviceType} + value={deviceType} onChange={(value) => { editorState.setDeviceType(value); }} /> {/* Orientation */} - {editorState.deviceType !== 'desktop' && ( + {deviceType !== 'desktop' && ( options={[ { value: 'portrait', label: "Portrait" }, { value: 'landscape', label: "Landscape" }, ]} - value={editorState.deviceOrientation} + value={deviceOrientation} onChange={(value) => { editorState.setDeviceOrientation(value); }} @@ -260,8 +263,8 @@ const PreviewHeaderComp = () => { ); }, [ isPublicApp, - editorState.deviceType, - editorState.deviceOrientation, + deviceType, + deviceOrientation, ]); return ( diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index ff103b58fd..966b7f1174 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -285,6 +285,7 @@ export const LeftContent = (props: LeftContentProps) => { const { uiComp } = props; const editorState = useContext(EditorContext); const selectedCompNames = useEditorStore((state) => state.selectedCompNames); + const selectedBottomResName = useEditorStore((state) => state.selectedBottomResName); const [expandedKeys, setExpandedKeys] = useState>([]); const [showData, setShowData] = useState([]); @@ -546,11 +547,11 @@ export const LeftContent = (props: LeftContentProps) => { name={item.name} desc={item.dataDesc} data={item.data} - isSelected={editorState.selectedBottomResName === item.name} + isSelected={selectedBottomResName === item.name} onClick={() => handleBottomResItemClick(item.type as BottomResTypeEnum, item.name)} /> )); - }, [editorState, handleBottomResItemClick]); + }, [editorState, selectedBottomResName, handleBottomResItemClick]); const hookCompsCollapse = useMemo(() => { return _.sortBy( diff --git a/client/packages/lowcoder/src/pages/editor/bottom/BottomContent.tsx b/client/packages/lowcoder/src/pages/editor/bottom/BottomContent.tsx index a233558154..7acc70c2c0 100644 --- a/client/packages/lowcoder/src/pages/editor/bottom/BottomContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/bottom/BottomContent.tsx @@ -3,6 +3,7 @@ import styled, { css } from "styled-components"; import { NofileIcon } from "lowcoder-design"; import { EmptyTab } from "./BottomTabs"; import { CompNameContext, EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { BottomSidebar } from "./BottomSidebar"; import { useSelector } from "react-redux"; import { MetaDataContext } from "base/codeEditor/codeEditorTypes"; @@ -62,6 +63,8 @@ export function BottomSkeleton() { export const BottomContent = () => { const editorState = useContext(EditorContext); const datasourceInfos = useSelector(getDataSource); + const selectedBottomResName = useEditorStore((state) => state.selectedBottomResName); + const selectedBottomResType = useEditorStore((state) => state.selectedBottomResType); const selectedComp = editorState.selectedBottomResComp(); const [isCreatePanelShow, showCreatePanel] = useState(false); @@ -70,7 +73,7 @@ export const BottomContent = () => { const transformerItems = editorState.getTransformersComp().getView(); const folderItems = editorState.getFoldersComp().getView(); const dataResponderItems = editorState.getDataRespondersComp().getView(); - const isFolderSelected = editorState.selectedBottomResType === BottomResTypeEnum.Folder; + const isFolderSelected = selectedBottomResType === BottomResTypeEnum.Folder; const refTreeComp = editorState.rootComp.children.refTree; const bottomResItems = [ @@ -97,7 +100,7 @@ export const BottomContent = () => { showCreatePanel(false); const isFolder = type === BottomResTypeEnum.Folder; - const parent = isFolderSelected && !isFolder ? editorState.selectedBottomResName : ""; + const parent = isFolderSelected && !isFolder ? selectedBottomResName : ""; const index = isFolder ? folderItems.length : undefined; editorState.rootComp.children.refTree.appendRef(parent, id, index); }; @@ -123,8 +126,8 @@ export const BottomContent = () => { }; useEffect(() => { - editorState.selectedBottomResName && showCreatePanel(false); - }, [editorState.selectedBottomResName, showCreatePanel]); + selectedBottomResName && showCreatePanel(false); + }, [selectedBottomResName, showCreatePanel]); // keep reference unchanged when metaData unchange, avoid re-configure when auto-completion changes const selectedDatasourceId = diff --git a/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx b/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx index 6f8a6f4d79..fd6c563c25 100644 --- a/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx +++ b/client/packages/lowcoder/src/pages/editor/bottom/BottomSidebar.tsx @@ -14,6 +14,7 @@ import { } from "lowcoder-design"; import { CSSProperties, useContext, useEffect, useState } from "react"; import { EditorContext } from "comps/editorState"; +import { useEditorStore } from "comps/editorStore"; import { useSelector } from "react-redux"; import { showAppSnapshotSelector } from "redux/selectors/appSnapshotSelector"; import { BottomResComp, BottomResTypeEnum } from "types/bottomRes"; @@ -440,7 +441,8 @@ function BottomSidebarItem(props: BottomSidebarItemProps) { const [editing, setEditing] = useState(false); const editorState = useContext(EditorContext); const readOnly = useSelector(showAppSnapshotSelector); - const { selectedBottomResName, selectedBottomResType } = editorState; + const selectedBottomResName = useEditorStore((state) => state.selectedBottomResName); + const selectedBottomResType = useEditorStore((state) => state.selectedBottomResType); const level = path.length - 1; const type = resComp.type(); const name = resComp.name(); diff --git a/client/packages/lowcoder/src/pages/editor/bottom/BottomTabs.tsx b/client/packages/lowcoder/src/pages/editor/bottom/BottomTabs.tsx index 0813ddfb31..5a150d9465 100644 --- a/client/packages/lowcoder/src/pages/editor/bottom/BottomTabs.tsx +++ b/client/packages/lowcoder/src/pages/editor/bottom/BottomTabs.tsx @@ -10,6 +10,7 @@ import { import React, { ReactNode, useContext, useEffect, useState } from "react"; import styled from "styled-components"; import { EditorContext } from "../../../comps/editorState"; +import { useEditorStore } from "../../../comps/editorStore"; import _ from "lodash"; import { ReadOnlyMask } from "pages/common/styledComponent"; import { useSelector } from "react-redux"; @@ -222,10 +223,11 @@ export function BottomTabs(props: { const [editing, setEditing] = useState(false); const editorState = useContext(EditorContext); const readOnly = useSelector(showAppSnapshotSelector); + const selectedBottomResName = useEditorStore((state) => state.selectedBottomResName); const valueInfoMap = _.fromPairs(tabsConfig.map((c) => [c.key, c])); - useEffect(() => setKey("general"), [editorState.selectedBottomResName]); + useEffect(() => setKey("general"), [selectedBottomResName]); const RunButtonWrapper = () => (