From a5d099bb14ee151fa4e02c12e49fb6bdfff837e7 Mon Sep 17 00:00:00 2001 From: freddysundowner Date: Fri, 29 Dec 2023 11:06:35 +0300 Subject: [PATCH 1/9] initial --- .../lowcoder/src/comps/editorState.tsx | 81 ++++++++-- .../lowcoder/src/pages/common/header.tsx | 153 ++++++++++++++---- .../src/pages/editor/editorSkeletonView.tsx | 18 ++- .../lowcoder/src/pages/editor/editorView.tsx | 100 +++++++++--- .../src/pages/tutorials/tutorialsConstant.tsx | 5 + .../lowcoder/src/util/localStorageUtil.ts | 21 ++- 6 files changed, 305 insertions(+), 73 deletions(-) diff --git a/client/packages/lowcoder/src/comps/editorState.tsx b/client/packages/lowcoder/src/comps/editorState.tsx index 35732c075..5e87d3b57 100644 --- a/client/packages/lowcoder/src/comps/editorState.tsx +++ b/client/packages/lowcoder/src/comps/editorState.tsx @@ -2,8 +2,12 @@ import { isContainer } from "comps/comps/containerBase"; import { RootComp as RootCompTmp } from "comps/comps/rootComp"; import { PositionParams } from "layout"; import _ from "lodash"; -import React, { ReactNode } from "react"; -import { BottomResComp, BottomResListComp, BottomResTypeEnum } from "types/bottomRes"; +import React, { ReactNode, useContext } from "react"; +import { + BottomResComp, + BottomResListComp, + BottomResTypeEnum, +} from "types/bottomRes"; import { setFields } from "util/objectUtils"; import { OptionalComp, renameAction } from "lowcoder-core"; import { GridItemComp } from "./comps/gridItemComp"; @@ -13,7 +17,7 @@ import { NameAndExposingInfo } from "./utils/exposingTypes"; import { checkName } from "./utils/rename"; import { trans } from "i18n"; import { UiLayoutType } from "./comps/uiComp"; -import { getEditorModeStatus } from "util/localStorageUtil"; +import { getCollissionStatus, getEditorModeStatus } from "util/localStorageUtil"; type RootComp = InstanceType; @@ -43,6 +47,7 @@ export class EditorState { readonly showPropertyPane: boolean = false; readonly selectedCompNames: Set = new Set(); readonly editorModeStatus: string = ""; + readonly collissionStatus: string = ""; readonly isDragging: boolean = false; readonly draggingCompType: string = "button"; readonly forceShowGrid: boolean = false; // show grid lines @@ -52,16 +57,20 @@ export class EditorState { readonly showResultCompName: string = ""; readonly selectSource?: SelectSourceType; // the source of select type - private readonly setEditorState: (fn: (editorState: EditorState) => EditorState) => void; + private readonly setEditorState: ( + fn: (editorState: EditorState) => EditorState + ) => void; constructor( rootComp: RootComp, setEditorState: (fn: (editorState: EditorState) => EditorState) => void, - initialEditorModeStatus: string = getEditorModeStatus() + initialEditorModeStatus: string = getEditorModeStatus(), + initialCollissionStatus: string = getCollissionStatus() ) { this.rootComp = rootComp; this.setEditorState = setEditorState; this.editorModeStatus = initialEditorModeStatus; + this.collissionStatus = initialCollissionStatus; } /** @@ -79,7 +88,10 @@ export class EditorState { } getAllCompMap() { - return { ...this.getAllHooksCompMap(), ...this.getUIComp().getAllCompItems() }; + return { + ...this.getAllHooksCompMap(), + ...this.getUIComp().getAllCompItems(), + }; } getAllUICompMap() { @@ -104,7 +116,9 @@ export class EditorState { */ getUICompByName(name: string) { const compMap = this.getAllUICompMap(); - return Object.values(compMap).find((item) => item.children.name.getView() === name); + return Object.values(compMap).find( + (item) => item.children.name.getView() === name + ); } getNameGenerator() { @@ -130,7 +144,11 @@ export class EditorState { }); } - getCompInfo(nameAndExposingInfo: NameAndExposingInfo, name: string, type: string): CompInfo { + getCompInfo( + nameAndExposingInfo: NameAndExposingInfo, + name: string, + type: string + ): CompInfo { return { name, type, @@ -157,7 +175,11 @@ export class EditorState { const exposingInfo = listComp.nameAndExposingInfo(); return listComp.getView().map((item) => { const name = item.children.name.getView(); - return this.getCompInfo(exposingInfo, name, BottomResTypeEnum.DateResponder); + return this.getCompInfo( + exposingInfo, + name, + BottomResTypeEnum.DateResponder + ); }); } @@ -175,7 +197,11 @@ export class EditorState { const exposingInfo = listComp.nameAndExposingInfo(); return listComp.getView().map((item) => { const name = item.children.name.getView(); - return this.getCompInfo(exposingInfo, name, BottomResTypeEnum.Transformer); + return this.getCompInfo( + exposingInfo, + name, + BottomResTypeEnum.Transformer + ); }); } @@ -222,7 +248,10 @@ export class EditorState { } selectedQueryComp() { - if (this.selectedBottomResType !== BottomResTypeEnum.Query || !this.selectedBottomResName) { + if ( + this.selectedBottomResType !== BottomResTypeEnum.Query || + !this.selectedBottomResName + ) { return undefined; } return this.getQueriesComp() @@ -233,7 +262,9 @@ export class EditorState { } showResultComp(): BottomResComp | undefined { - const bottomResComps = Object.values(BottomResTypeEnum).reduce((a, b) => { + const bottomResComps = Object.values(BottomResTypeEnum).reduce< + BottomResComp[] + >((a, b) => { const items = this.getBottomResListComp(b).items(); return a.concat(items); }, []); @@ -279,7 +310,10 @@ export class EditorState { return this.getUIComp().getComp(); } const [key, comp] = _.toPairs(selectedComps)[0]; - if (_.size(selectedComps) === 1 && isContainer((comp as GridItemComp)?.children?.comp)) { + if ( + _.size(selectedComps) === 1 && + isContainer((comp as GridItemComp)?.children?.comp) + ) { return comp.children.comp; } @@ -307,7 +341,9 @@ export class EditorState { isCompSelected(compName: string): OptionalComp { const compMap = this.getAllCompMap(); return Object.values(compMap).find( - (item) => item.children.name.getView() === compName && this.selectedCompNames.has(compName) + (item) => + item.children.name.getView() === compName && + this.selectedCompNames.has(compName) ); } @@ -319,6 +355,10 @@ export class EditorState { this.changeState({ editorModeStatus: newEditorModeStatus }); } + setCollissionStatus(newCollissionStatus: string) { + this.changeState({ collissionStatus: newCollissionStatus }); + } + setDragging(dragging: boolean) { if (this.isDragging === dragging) { return; @@ -356,7 +396,10 @@ export class EditorState { }); } - setSelectedCompNames(selectedCompNames: Set, selectSource?: SelectSourceType) { + setSelectedCompNames( + selectedCompNames: Set, + selectSource?: SelectSourceType + ) { if (selectedCompNames.size === 0 && this.selectedCompNames.size === 0) { return; } @@ -406,7 +449,9 @@ export class EditorState { } getBottomResComp(name: string): BottomResComp | undefined { - const bottomResComps = Object.values(BottomResTypeEnum).reduce((a, b) => { + const bottomResComps = Object.values(BottomResTypeEnum).reduce< + BottomResComp[] + >((a, b) => { const items = this.getBottomResListComp(b).items(); return a.concat(items); }, []); @@ -467,8 +512,10 @@ export class EditorState { getAppType(): UiLayoutType { return this.getUIComp().children.compType.getView(); } + getCollissionStatus(): string { + return this.collissionStatus; + } } - export const EditorContext = React.createContext(undefined as any); // current comp name diff --git a/client/packages/lowcoder/src/pages/common/header.tsx b/client/packages/lowcoder/src/pages/common/header.tsx index 0b80f0b73..108076d3e 100644 --- a/client/packages/lowcoder/src/pages/common/header.tsx +++ b/client/packages/lowcoder/src/pages/common/header.tsx @@ -2,8 +2,13 @@ import { Dropdown, Skeleton, Radio, RadioChangeEvent } from "antd"; import LayoutHeader from "components/layout/Header"; import { SHARE_TITLE } from "constants/apiConstants"; import { AppTypeEnum } from "constants/applicationConstants"; -import { ALL_APPLICATIONS_URL, AUTH_LOGIN_URL, preview } from "constants/routesURL"; +import { + ALL_APPLICATIONS_URL, + AUTH_LOGIN_URL, + preview, +} from "constants/routesURL"; import { User } from "constants/userConstants"; +import { Switch } from "antd"; import { CommonTextLabel, CustomModal, @@ -21,8 +26,14 @@ import { trans } from "i18n"; import dayjs from "dayjs"; import { useContext, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { publishApplication, updateAppMetaAction } from "redux/reduxActions/applicationActions"; -import { recoverSnapshotAction, setShowAppSnapshot } from "redux/reduxActions/appSnapshotActions"; +import { + publishApplication, + updateAppMetaAction, +} from "redux/reduxActions/applicationActions"; +import { + recoverSnapshotAction, + setShowAppSnapshot, +} from "redux/reduxActions/appSnapshotActions"; import { currentApplication } from "redux/selectors/applicationSelector"; import { getSelectedAppSnapshot, @@ -40,8 +51,8 @@ import { HeaderStartDropdown } from "./headerStartDropdown"; import { AppPermissionDialog } from "../../components/PermissionDialog/AppPermissionDialog"; import { getBrandingConfig } from "../../redux/selectors/configSelectors"; import { messageInstance } from "lowcoder-design"; -import { EditorContext } from "../../comps/editorState"; - +import { EditorContext } from "../../comps/editorState"; +import { SwitchChangeEventHandler } from "antd/es/switch"; const StyledLink = styled.a` display: flex; @@ -157,13 +168,13 @@ const GrayBtn = styled(TacoButton)` margin-right: 8px; cursor: pointer; --antd-wave-shadow-color: #8b8fa34c; - + &:hover { background: #666666; color: #ffffff; border: none; } - + &:focus { background: #666666; color: #ffffff; @@ -259,7 +270,10 @@ function HeaderProfile(props: { user: User }) { return (
{user.isAnonymous ? ( - history.push(AUTH_LOGIN_URL)}> + history.push(AUTH_LOGIN_URL)} + > {trans("userAuth.login")} ) : ( @@ -271,21 +285,29 @@ function HeaderProfile(props: { user: User }) { export type PanelStatus = { left: boolean; bottom: boolean; right: boolean }; export type TogglePanel = (panel?: keyof PanelStatus) => void; +export type EnabledCollissionStatus = "true" | "false"; export type EditorModeStatus = "layout" | "logic" | "both"; -export type ToggleEditorModeStatus = (editorModeStatus?: EditorModeStatus) => void; +export type ToggleEditorModeStatus = ( + editorModeStatus?: EditorModeStatus +) => void; +export type ToggleCollissionStatus = ( + collissionStatus?: EnabledCollissionStatus +) => void; type HeaderProps = { panelStatus: PanelStatus; togglePanel: TogglePanel; editorModeStatus: EditorModeStatus; toggleEditorModeStatus: ToggleEditorModeStatus; + collissionStatus: EnabledCollissionStatus; + toggleCollissionStatus: ToggleCollissionStatus; }; // header in editor page export default function Header(props: HeaderProps) { const editorState = useContext(EditorContext); const { togglePanel } = props; - const { toggleEditorModeStatus } = props; + const { toggleEditorModeStatus, toggleCollissionStatus } = props; const { left, bottom, right } = props.panelStatus; const user = useSelector(getUser); const application = useSelector(currentApplication); @@ -301,16 +323,49 @@ export default function Header(props: HeaderProps) { const isModule = appType === AppTypeEnum.Module; const editorModeOptions = [ - { label: trans("header.editorMode_layout"), key: "editorModeSelector_layout", value: "layout" }, - { label: trans("header.editorMode_logic"), key: "editorModeSelector_logic", value: "logic" }, - { label: trans("header.editorMode_both"), key: "editorModeSelector_both", value: "both" }, + { + label: trans("header.editorMode_layout"), + key: "editorModeSelector_layout", + value: "layout", + }, + { + label: trans("header.editorMode_logic"), + key: "editorModeSelector_logic", + value: "logic", + }, + { + label: trans("header.editorMode_both"), + key: "editorModeSelector_both", + value: "both", + }, ]; - - const onEditorStateValueChange = ({ target: { value } }: RadioChangeEvent) => { + + // const collissionOptions = [ + // { + // label: trans("header.editorMode_layout"), + // key: "editorModeSelector_layout", + // value: "tru", + // }, + // { + // label: trans("header.editorMode_logic"), + // key: "editorModeSelector_logic", + // value: "logic", + // }, + // { + // label: trans("header.editorMode_both"), + // key: "editorModeSelector_both", + // value: "both", + // }, + // ]; + + const onEditorStateValueChange = ({ + target: { value }, + }: RadioChangeEvent) => { toggleEditorModeStatus(value); editorState.setEditorModeStatus(value); }; - + + const headerStart = ( <> history.push(ALL_APPLICATIONS_URL)}> @@ -330,7 +385,12 @@ export default function Header(props: HeaderProps) { messageInstance.warning(trans("header.nameCheckMessage")); return; } - dispatch(updateAppMetaAction({ applicationId: applicationId, name: value })); + dispatch( + updateAppMetaAction({ + applicationId: applicationId, + name: value, + }) + ); setEditName(false); }} /> @@ -343,15 +403,34 @@ export default function Header(props: HeaderProps) { }} /> )} - {showAppSnapshot && {trans("header.viewOnly")}} + {showAppSnapshot && ( + {trans("header.viewOnly")} + )} ); - // key={option.key} - const headerMiddle = ( - <> - + <> + <> +

+ Layers +

+ { + toggleCollissionStatus(value == true ? "true" : "false"); + editorState.setCollissionStatus(value == true ? "true" : "false"); + }} + /> + + {editorModeOptions.map((option) => ( {option.label} @@ -377,7 +456,9 @@ export default function Header(props: HeaderProps) { CustomModal.confirm({ title: trans("header.recoverAppSnapshotTitle"), content: trans("header.recoverAppSnapshotContent", { - time: dayjs(selectedSnapshot.createTime).format("YYYY-MM-DD HH:mm"), + time: dayjs(selectedSnapshot.createTime).format( + "YYYY-MM-DD HH:mm" + ), }), onConfirm: () => { dispatch( @@ -409,11 +490,15 @@ export default function Header(props: HeaderProps) { !visible && setPermissionDialogVisible(false)} + onVisibleChange={(visible) => + !visible && setPermissionDialogVisible(false) + } /> )} {canManageApp(user, application) && ( - setPermissionDialogVisible(true)}>{SHARE_TITLE} + setPermissionDialogVisible(true)}> + {SHARE_TITLE} + )} preview(applicationId)}> {trans("header.preview")} @@ -436,11 +521,15 @@ export default function Header(props: HeaderProps) { items={[ { key: "deploy", - label: {trans("header.deploy")}, + label: ( + {trans("header.deploy")} + ), }, { key: "snapshot", - label: {trans("header.snapshot")}, + label: ( + {trans("header.snapshot")} + ), }, ]} /> @@ -456,7 +545,11 @@ export default function Header(props: HeaderProps) { ); return ( - + ); } @@ -475,7 +568,9 @@ export function AppHeader() { ); } diff --git a/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx b/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx index e1a1766ad..aa036d57c 100644 --- a/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx @@ -7,7 +7,7 @@ import { LeftPanel, MiddlePanel, } from "pages/common/styledComponent"; -import { getPanelStatus, getEditorModeStatus, getPanelStyle } from "util/localStorageUtil"; +import { getPanelStatus, getEditorModeStatus, getPanelStyle, getCollissionStatus } from "util/localStorageUtil"; import { BottomSkeleton } from "pages/editor/bottom/BottomContent"; import RightPanel from "pages/editor/right/RightPanel"; import _ from "lodash"; @@ -47,6 +47,7 @@ export const EditorLoadingSpin = (props: { height?: string | number }) => { export default function EditorSkeletonView() { const panelStatus = getPanelStatus(); const editorModeStatus = getEditorModeStatus(); + const collissionStatus = getCollissionStatus(); const panelStyle = getPanelStyle(); const isUserViewMode = useUserViewMode(); const isTemplate = useTemplateViewMode(); @@ -58,7 +59,14 @@ export default function EditorSkeletonView() { return ( <> -
+
{panelStatus.left && ( @@ -77,7 +85,11 @@ export default function EditorSkeletonView() { )} {panelStatus.right && ( - + )} diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index a63c2ad2f..d82a0b618 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -8,9 +8,21 @@ import { Layers } from "constants/Layers"; import { TopHeaderHeight } from "constants/style"; import { trans } from "i18n"; import { draggingUtils } from "layout"; -import { LeftPreloadIcon, LeftSettingIcon, LeftStateIcon, ScrollBar } from "lowcoder-design"; +import { + LeftPreloadIcon, + LeftSettingIcon, + LeftStateIcon, + ScrollBar, +} from "lowcoder-design"; import { useTemplateViewMode } from "util/hooks"; -import Header, { PanelStatus, TogglePanel, EditorModeStatus, ToggleEditorModeStatus } from "pages/common/header"; +import Header, { + PanelStatus, + TogglePanel, + EditorModeStatus, + ToggleEditorModeStatus, + EnabledCollissionStatus, + ToggleCollissionStatus, +} from "pages/common/header"; import { HelpDropdown } from "pages/common/help"; import { PreviewHeader } from "pages/common/previewHeader"; import { @@ -28,8 +40,19 @@ import { } from "pages/editor/editorHotKeys"; import RightPanel from "pages/editor/right/RightPanel"; import EditorTutorials from "pages/tutorials/editorTutorials"; -import { editorContentClassName, UserGuideLocationState } from "pages/tutorials/tutorialsConstant"; -import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react"; +import { + CollisionState, + editorContentClassName, + UserGuideLocationState, +} from "pages/tutorials/tutorialsConstant"; +import React, { + useCallback, + useContext, + useEffect, + useLayoutEffect, + useMemo, + useState, +} from "react"; import { Helmet } from "react-helmet"; import { useDispatch, useSelector } from "react-redux"; import { useLocation } from "react-router-dom"; @@ -38,7 +61,17 @@ import { currentApplication } from "redux/selectors/applicationSelector"; import { showAppSnapshotSelector } from "redux/selectors/appSnapshotSelector"; import styled from "styled-components"; import { ExternalEditorContext } from "util/context/ExternalEditorContext"; -import { DefaultPanelStatus, getPanelStatus, savePanelStatus, DefaultEditorModeStatus, getEditorModeStatus, saveEditorModeStatus } from "util/localStorageUtil"; +import { + DefaultPanelStatus, + getPanelStatus, + savePanelStatus, + DefaultEditorModeStatus, + getEditorModeStatus, + saveEditorModeStatus, + saveEnableCollissionStatus, + getCollissionStatus, + DefaultCollissionStatus, +} from "util/localStorageUtil"; import Bottom from "./bottom/BottomPanel"; import { LeftContent } from "./LeftContent"; import { isAggregationApp } from "util/appUtils"; @@ -179,14 +212,15 @@ const items = [ }, ]; - function EditorView(props: EditorViewProps) { const { uiComp } = props; const editorState = useContext(EditorContext); const { readOnly, hideHeader } = useContext(ExternalEditorContext); const application = useSelector(currentApplication); const locationState = useLocation().state; + const collisionState = useLocation().state; const showNewUserGuide = locationState?.showNewUserGuide; + const showCollission = collisionState?.collission; const showAppSnapshot = useSelector(showAppSnapshotSelector); const [showShortcutList, setShowShortcutList] = useState(false); const toggleShortcutList = useCallback( @@ -201,8 +235,9 @@ function EditorView(props: EditorViewProps) { return showNewUserGuide ? DefaultPanelStatus : getPanelStatus(); }); - const [prePanelStatus, setPrePanelStatus] = useState(DefaultPanelStatus); - + const [prePanelStatus, setPrePanelStatus] = + useState(DefaultPanelStatus); + const togglePanel: TogglePanel = useCallback( (key) => { let newPanelStatus; @@ -223,23 +258,32 @@ function EditorView(props: EditorViewProps) { [panelStatus, prePanelStatus] ); + // added by Fred to set comp collision state + const [collisionStatus, setCollisionStatus] = useState(() => { + return showCollission ? DefaultCollissionStatus : getCollissionStatus(); + }); + + const toggleCollissionStatus: ToggleCollissionStatus = useCallback( + (value) => { + setCollisionStatus(value ? value : "false"); + saveEnableCollissionStatus(value ? value : "false"); + }, + [collisionStatus] + ); // added by Falk Wolsky to support a Layout and Logic Mode in Lowcoder const [editorModeStatus, setEditorModeStatus] = useState(() => { return showNewUserGuide ? DefaultEditorModeStatus : getEditorModeStatus(); }); - - const toggleEditorModeStatus: ToggleEditorModeStatus = useCallback( (value) => { - setEditorModeStatus(value ? value : "both" as EditorModeStatus); - saveEditorModeStatus(value ? value : "both" as EditorModeStatus); - + const toggleEditorModeStatus: ToggleEditorModeStatus = useCallback( + (value) => { + setEditorModeStatus(value ? value : ("both" as EditorModeStatus)); + saveEditorModeStatus(value ? value : ("both" as EditorModeStatus)); }, [editorModeStatus] ); - - const onCompDrag = useCallback( (dragCompKey: string) => { editorState.setDraggingCompType(dragCompKey); @@ -267,7 +311,8 @@ function EditorView(props: EditorViewProps) { setHeight(window.innerHeight); } - const eventType = "orientationchange" in window ? "orientationchange" : "resize"; + const eventType = + "orientationchange" in window ? "orientationchange" : "resize"; window.addEventListener(eventType, updateSize); updateSize(); return () => window.removeEventListener(eventType, updateSize); @@ -293,7 +338,9 @@ function EditorView(props: EditorViewProps) { {uiComp.getView()} -
{hookCompViews}
+
+ {hookCompViews} +
); @@ -329,7 +376,14 @@ function EditorView(props: EditorViewProps) { draggingUtils.clearData(); }} > -
+
{application && {application.name}} {showNewUserGuide && } {application && - !isAggregationApp(AppUILayoutType[application.applicationType]) && ( + !isAggregationApp( + AppUILayoutType[application.applicationType] + ) && ( <> {appSettingsComp.getPropertyView()} @@ -379,7 +435,11 @@ function EditorView(props: EditorViewProps) { {props.preloadComp.getPropertyView()} - dispatch(setEditorExternalStateAction({ showScriptsAndStyleModal: true })) + dispatch( + setEditorExternalStateAction({ + showScriptsAndStyleModal: true, + }) + ) } > diff --git a/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx b/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx index 147327178..550f07214 100644 --- a/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx +++ b/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx @@ -31,3 +31,8 @@ export const defaultJoyrideFloaterProps: FloaterType = { export type UserGuideLocationState = { showNewUserGuide?: boolean; }; + +export type CollisionState = { + showNewUserGuide?: boolean; + collission?: boolean; +}; diff --git a/client/packages/lowcoder/src/util/localStorageUtil.ts b/client/packages/lowcoder/src/util/localStorageUtil.ts index 2d06ddd20..c6ee587ad 100644 --- a/client/packages/lowcoder/src/util/localStorageUtil.ts +++ b/client/packages/lowcoder/src/util/localStorageUtil.ts @@ -1,4 +1,4 @@ -import { PanelStatus } from "pages/common/header"; +import { EnabledCollissionStatus, PanelStatus } from "pages/common/header"; import { EditorModeStatus } from "pages/common/header"; import log from "loglevel"; import { JSONValue } from "util/jsonTypes"; @@ -19,7 +19,6 @@ export const DefaultPanelStatus: PanelStatus = { right: true, }; - const DefaultPanelStyle: PanelStyle = { bottom: { h: 285, @@ -42,12 +41,26 @@ export function getPanelStatus(): PanelStatus { return { ...DefaultPanelStatus, ...JSON.parse(str) }; } - export function saveEditorModeStatus(editorModeStatus: EditorModeStatus) { localStorage.setItem("editor_mode_status", editorModeStatus); } +//ADDED BY FRED TO SAVE enabledCollission +export function saveEnableCollissionStatus( + collisionStatus: EnabledCollissionStatus +) { + localStorage.setItem("enable_collission", collisionStatus); +} + +export const DefaultCollissionStatus: EnabledCollissionStatus = "true"; +export function getCollissionStatus(): EnabledCollissionStatus { + const str = localStorage.getItem("enable_collission"); + if (!str) { + return DefaultCollissionStatus; + } + return str as EnabledCollissionStatus; +} -export const DefaultEditorModeStatus: EditorModeStatus = "both"; +export const DefaultEditorModeStatus: EditorModeStatus = "both"; export function getEditorModeStatus(): EditorModeStatus { const str = localStorage.getItem("editor_mode_status"); if (!str) { From a81e175405f05556e6738fa10b44adf8d7953dd0 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Wed, 24 Jan 2024 23:41:54 +0100 Subject: [PATCH 2/9] Pushing from header to left meno bar --- .../lowcoder/src/comps/editorState.tsx | 2 +- .../lowcoder/src/pages/common/header.tsx | 28 ++++----- .../src/pages/editor/editorSkeletonView.tsx | 7 ++- .../lowcoder/src/pages/editor/editorView.tsx | 57 ++++++++++++++++--- .../lowcoder/src/util/localStorageUtil.ts | 3 +- 5 files changed, 66 insertions(+), 31 deletions(-) diff --git a/client/packages/lowcoder/src/comps/editorState.tsx b/client/packages/lowcoder/src/comps/editorState.tsx index 5e87d3b57..9ec9e5415 100644 --- a/client/packages/lowcoder/src/comps/editorState.tsx +++ b/client/packages/lowcoder/src/comps/editorState.tsx @@ -2,7 +2,7 @@ import { isContainer } from "comps/comps/containerBase"; import { RootComp as RootCompTmp } from "comps/comps/rootComp"; import { PositionParams } from "layout"; import _ from "lodash"; -import React, { ReactNode, useContext } from "react"; +import React, { ReactNode } from "react"; import { BottomResComp, BottomResListComp, diff --git a/client/packages/lowcoder/src/pages/common/header.tsx b/client/packages/lowcoder/src/pages/common/header.tsx index e7dd9d9dd..3581a3eaf 100644 --- a/client/packages/lowcoder/src/pages/common/header.tsx +++ b/client/packages/lowcoder/src/pages/common/header.tsx @@ -287,29 +287,34 @@ function HeaderProfile(props: { user: User }) { export type PanelStatus = { left: boolean; bottom: boolean; right: boolean }; export type TogglePanel = (panel?: keyof PanelStatus) => void; -export type EnabledCollissionStatus = "true" | "false"; + export type EditorModeStatus = "layout" | "logic" | "both"; export type ToggleEditorModeStatus = ( editorModeStatus?: EditorModeStatus ) => void; + +/* +// export type EnabledCollissionStatus = "true" | "false"; export type ToggleCollissionStatus = ( collissionStatus?: EnabledCollissionStatus -) => void; +) => void; +*/ type HeaderProps = { panelStatus: PanelStatus; togglePanel: TogglePanel; editorModeStatus: EditorModeStatus; toggleEditorModeStatus: ToggleEditorModeStatus; - collissionStatus: EnabledCollissionStatus; - toggleCollissionStatus: ToggleCollissionStatus; + // collissionStatus: EnabledCollissionStatus; + // toggleCollissionStatus: ToggleCollissionStatus; }; // header in editor page export default function Header(props: HeaderProps) { const editorState = useContext(EditorContext); const { togglePanel } = props; - const { toggleEditorModeStatus, toggleCollissionStatus } = props; + const { toggleEditorModeStatus } = props; + // const { toggleCollissionStatus } = props; const { left, bottom, right } = props.panelStatus; const user = useSelector(getUser); const application = useSelector(currentApplication); @@ -413,18 +418,7 @@ export default function Header(props: HeaderProps) { const headerMiddle = ( <> - <> -

- Layers -

- { - toggleCollissionStatus(value == true ? "true" : "false"); - editorState.setCollissionStatus(value == true ? "true" : "false"); - }} - /> + <> {panelStatus.left && ( - + )} diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index 81b7ec084..e1b966688 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -20,9 +20,7 @@ import Header, { PanelStatus, TogglePanel, EditorModeStatus, - ToggleEditorModeStatus, - EnabledCollissionStatus, - ToggleCollissionStatus, + ToggleEditorModeStatus } from "pages/common/header"; import { HelpDropdown } from "pages/common/help"; import { PreviewHeader } from "pages/common/previewHeader"; @@ -76,6 +74,8 @@ import { import Bottom from "./bottom/BottomPanel"; import { LeftContent } from "./LeftContent"; import { isAggregationApp } from "util/appUtils"; +import { Switch } from "antd"; +import { SwitchChangeEventHandler } from "antd/es/switch"; const HookCompContainer = styled.div` pointer-events: none; @@ -141,6 +141,16 @@ const HelpDiv = styled.div` } } `; + +const LayoutMenuDiv = styled.div` + > div { + left: 6px; + right: auto; + height: 28px; + top: 15px; + } +`; + const SettingsDiv = styled.div` display: flex; flex-direction: column; @@ -200,6 +210,7 @@ interface EditorViewProps { enum SiderKey { State = "state", Setting = "setting", + Layout = "layout", } const items = [ @@ -211,8 +222,18 @@ const items = [ key: SiderKey.Setting, icon: , }, + { + key: SiderKey.Layout, + icon: , + }, ]; + // added by Fred to set comp collision state +export type EnabledCollissionStatus = "true" | "false"; // "true" means collission is enabled, "false" means collission is disabled +export type ToggleCollissionStatus = ( + collissionStatus?: EnabledCollissionStatus + ) => void; + function EditorView(props: EditorViewProps) { const { uiComp } = props; const editorState = useContext(EditorContext); @@ -259,7 +280,7 @@ function EditorView(props: EditorViewProps) { [panelStatus, prePanelStatus] ); - // added by Fred to set comp collision state + const [collisionStatus, setCollisionStatus] = useState(() => { return showCollission ? DefaultCollissionStatus : getCollissionStatus(); }); @@ -381,9 +402,7 @@ function EditorView(props: EditorViewProps) { togglePanel={togglePanel} panelStatus={panelStatus} toggleEditorModeStatus={toggleEditorModeStatus} - toggleCollissionStatus={toggleCollissionStatus} - editorModeStatus={editorModeStatus} - collissionStatus={collisionStatus} + editorModeStatus={editorModeStatus} /> {application && {application.name}} {showNewUserGuide && } @@ -395,7 +414,8 @@ function EditorView(props: EditorViewProps) { > - + + clickMenu(params)} - /> + > + + {!showAppSnapshot && ( )} + + + + {menuKey === SiderKey.Layout && ( + + { + toggleCollissionStatus(value == true ? "true" : "false"); + editorState.setCollissionStatus(value == true ? "true" : "false"); + }} + /> + + + )} + )} diff --git a/client/packages/lowcoder/src/util/localStorageUtil.ts b/client/packages/lowcoder/src/util/localStorageUtil.ts index c6ee587ad..15e951909 100644 --- a/client/packages/lowcoder/src/util/localStorageUtil.ts +++ b/client/packages/lowcoder/src/util/localStorageUtil.ts @@ -1,4 +1,5 @@ -import { EnabledCollissionStatus, PanelStatus } from "pages/common/header"; +import { PanelStatus } from "pages/common/header"; +import { EnabledCollissionStatus } from "pages/editor/editorView"; import { EditorModeStatus } from "pages/common/header"; import log from "loglevel"; import { JSONValue } from "util/jsonTypes"; From 875bdfdc71bad0fff4706aebfea77a3542f61090 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Thu, 25 Jan 2024 19:17:12 +0100 Subject: [PATCH 3/9] Sortable Display of Components --- client/packages/lowcoder-core/lib/index.d.ts | 2 +- .../lowcoder/src/comps/editorState.tsx | 20 ++-- client/packages/lowcoder/src/layout/utils.ts | 11 +- .../lowcoder/src/pages/common/header.tsx | 30 ----- .../lowcoder/src/pages/editor/LeftContent.tsx | 107 +++++++++++++++--- .../src/pages/editor/editorSkeletonView.tsx | 8 +- .../lowcoder/src/pages/editor/editorView.tsx | 34 +++--- .../src/pages/tutorials/tutorialsConstant.tsx | 5 - .../lowcoder/src/util/localStorageUtil.ts | 20 ++-- 9 files changed, 142 insertions(+), 95 deletions(-) diff --git a/client/packages/lowcoder-core/lib/index.d.ts b/client/packages/lowcoder-core/lib/index.d.ts index d0db17798..b29a693f4 100644 --- a/client/packages/lowcoder-core/lib/index.d.ts +++ b/client/packages/lowcoder-core/lib/index.d.ts @@ -454,7 +454,7 @@ declare enum CompActionTypes { * broadcast other actions in comp tree structure. * used for encapsulate MultiBaseComp */ - BROADCAST = "BROADCAST" + BROADCAST = "BROADCAST", } type ExtraActionType = "layout" | "delete" | "add" | "modify" | "rename" | "recover" | "upgrade"; type ActionExtraInfo = { diff --git a/client/packages/lowcoder/src/comps/editorState.tsx b/client/packages/lowcoder/src/comps/editorState.tsx index 9ec9e5415..ee9db5475 100644 --- a/client/packages/lowcoder/src/comps/editorState.tsx +++ b/client/packages/lowcoder/src/comps/editorState.tsx @@ -17,7 +17,7 @@ import { NameAndExposingInfo } from "./utils/exposingTypes"; import { checkName } from "./utils/rename"; import { trans } from "i18n"; import { UiLayoutType } from "./comps/uiComp"; -import { getCollissionStatus, getEditorModeStatus } from "util/localStorageUtil"; +import { getCollisionStatus, getEditorModeStatus } from "util/localStorageUtil"; type RootComp = InstanceType; @@ -47,7 +47,7 @@ export class EditorState { readonly showPropertyPane: boolean = false; readonly selectedCompNames: Set = new Set(); readonly editorModeStatus: string = ""; - readonly collissionStatus: string = ""; + readonly collisionStatus: string = ""; readonly isDragging: boolean = false; readonly draggingCompType: string = "button"; readonly forceShowGrid: boolean = false; // show grid lines @@ -65,12 +65,12 @@ export class EditorState { rootComp: RootComp, setEditorState: (fn: (editorState: EditorState) => EditorState) => void, initialEditorModeStatus: string = getEditorModeStatus(), - initialCollissionStatus: string = getCollissionStatus() + initialCollisionStatus: string = getCollisionStatus() ) { this.rootComp = rootComp; this.setEditorState = setEditorState; this.editorModeStatus = initialEditorModeStatus; - this.collissionStatus = initialCollissionStatus; + this.collisionStatus = initialCollisionStatus; } /** @@ -134,12 +134,13 @@ export class EditorState { uiCompInfoList(): Array { const compMap = this.getAllUICompMap(); - return Object.values(compMap).map((item) => { + return Object.entries(compMap).map(([key, item]) => { return { name: item.children.name.getView(), type: item.children.compType.getView(), data: item.children.comp.exposingValues, dataDesc: item.children.comp.exposingInfo().propertyDesc, + key: key, }; }); } @@ -355,8 +356,8 @@ export class EditorState { this.changeState({ editorModeStatus: newEditorModeStatus }); } - setCollissionStatus(newCollissionStatus: string) { - this.changeState({ collissionStatus: newCollissionStatus }); + setCollisionStatus(newCollisionStatus: string) { + this.changeState({ collisionStatus: newCollisionStatus }); } setDragging(dragging: boolean) { @@ -512,9 +513,10 @@ export class EditorState { getAppType(): UiLayoutType { return this.getUIComp().children.compType.getView(); } - getCollissionStatus(): string { - return this.collissionStatus; + getCollisionStatus(): string { + return this.collisionStatus; } + } export const EditorContext = React.createContext(undefined as any); diff --git a/client/packages/lowcoder/src/layout/utils.ts b/client/packages/lowcoder/src/layout/utils.ts index c55c44dc2..6a9f0494d 100644 --- a/client/packages/lowcoder/src/layout/utils.ts +++ b/client/packages/lowcoder/src/layout/utils.ts @@ -1,11 +1,13 @@ import { UICompType } from "comps/uiCompRegistry"; import _ from "lodash"; -import React, { ReactElement, SyntheticEvent } from "react"; +import React, { ReactElement, SyntheticEvent, useContext } from "react"; import { DraggableEvent } from "react-draggable"; import { PositionParams } from "./calculateUtils"; import { draggingUtils } from "./draggingUtils"; import { GridLayoutProps, ResizeHandleAxis } from "./gridLayoutPropTypes"; +import { getCollisionStatus } from "util/localStorageUtil"; + export type LayoutItem = { w: number; h: number; @@ -169,7 +171,12 @@ export function collides(l1: LayoutItem, l2: LayoutItem): boolean { if (l1.y + l1.h <= l2.y) return false; // l1 is above l2 if (l1.y >= l2.y + l2.h) return false; // l1 is below l2 - return true; // boxes overlap + if (getCollisionStatus() === "true") { + return false; + } + else { + return true; // boxes overlap + } } /** diff --git a/client/packages/lowcoder/src/pages/common/header.tsx b/client/packages/lowcoder/src/pages/common/header.tsx index 3581a3eaf..06a0067ec 100644 --- a/client/packages/lowcoder/src/pages/common/header.tsx +++ b/client/packages/lowcoder/src/pages/common/header.tsx @@ -10,7 +10,6 @@ import { preview, } from "constants/routesURL"; import { User } from "constants/userConstants"; -import { Switch } from "antd"; import { CommonTextLabel, CustomModal, @@ -54,7 +53,6 @@ import { AppPermissionDialog } from "../../components/PermissionDialog/AppPermis import { getBrandingConfig } from "../../redux/selectors/configSelectors"; import { messageInstance } from "lowcoder-design"; import { EditorContext } from "../../comps/editorState"; -import { SwitchChangeEventHandler } from "antd/es/switch"; const StyledLink = styled.a` display: flex; @@ -293,20 +291,11 @@ export type ToggleEditorModeStatus = ( editorModeStatus?: EditorModeStatus ) => void; -/* -// export type EnabledCollissionStatus = "true" | "false"; -export type ToggleCollissionStatus = ( - collissionStatus?: EnabledCollissionStatus -) => void; -*/ - type HeaderProps = { panelStatus: PanelStatus; togglePanel: TogglePanel; editorModeStatus: EditorModeStatus; toggleEditorModeStatus: ToggleEditorModeStatus; - // collissionStatus: EnabledCollissionStatus; - // toggleCollissionStatus: ToggleCollissionStatus; }; // header in editor page @@ -314,7 +303,6 @@ export default function Header(props: HeaderProps) { const editorState = useContext(EditorContext); const { togglePanel } = props; const { toggleEditorModeStatus } = props; - // const { toggleCollissionStatus } = props; const { left, bottom, right } = props.panelStatus; const user = useSelector(getUser); const application = useSelector(currentApplication); @@ -347,24 +335,6 @@ export default function Header(props: HeaderProps) { }, ]; - // const collissionOptions = [ - // { - // label: trans("header.editorMode_layout"), - // key: "editorModeSelector_layout", - // value: "tru", - // }, - // { - // label: trans("header.editorMode_logic"), - // key: "editorModeSelector_logic", - // value: "logic", - // }, - // { - // label: trans("header.editorMode_both"), - // key: "editorModeSelector_both", - // value: "both", - // }, - // ]; - const onEditorStateValueChange = ({ target: { value }, }: RadioChangeEvent) => { diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index 5924d90bf..01de26f67 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -15,7 +15,7 @@ import { UnfoldIcon, UnShow, } from "lowcoder-design"; -import React, { ReactNode, useCallback, useContext, useMemo, useState } from "react"; +import React, { ReactNode, useCallback, useContext, useMemo, useState, useEffect } from "react"; import { hookCompCategory } from "comps/hooks/hookCompTypes"; import _ from "lodash"; import styled from "styled-components"; @@ -32,6 +32,7 @@ import { UICompType } from "comps/uiCompRegistry"; import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; import { DataNode, EventDataNode } from "antd/lib/tree"; import { isAggregationApp } from "util/appUtils"; +import cloneDeep from 'lodash/cloneDeep'; const CollapseTitleWrapper = styled.div` display: flex; @@ -408,17 +409,94 @@ export const LeftContent = (props: LeftContentProps) => { ); }; - const getTreeUI = (type: TreeUIKey) => { - const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]); + const [componentTreeData, setComponentTreeData] = useState([]); + const [modalsTreeData, setModalsTreeData] = useState([]); + + useEffect(() => { + const compData = getTreeUIData(TreeUIKey.Components); + setComponentTreeData(compData); + }, [editorState]); + + useEffect(() => { + const modalsData = getTreeUIData(TreeUIKey.Modals); + setModalsTreeData(modalsData); + }, [editorState]); + + const getTreeUIData = (type: TreeUIKey) => { const tree = type === TreeUIKey.Components ? editorState.getUIComp().getTree() : editorState.getHooksComp().getUITree(); const explorerData: NodeItem[] = getTree(tree, []); + return explorerData; + } + + interface DropInfo { + node: { key: string; pos: string }; + dragNode: { key: string; pos: string }; + } + + const handleDragEnter = (info: { node?: any; expandedKeys?: any; }) => { + // Assuming 'info' has a property 'expandedKeys' which is an array of keys + const { expandedKeys } = info; + if (!expandedKeys.includes(info.node.key)) { + setExpandedKeys(expandedKeys); + } + }; + + const handleDrop = (info: { node: { key: any; pos: string; }; dragNode: { key: any; pos: string; }; }, type: TreeUIKey) => { + const dropPos = info.node.pos.split('-'); + const dragPos = info.dragNode.pos.split('-'); + + if (dropPos.length === dragPos.length) { + setComponentTreeData(prevData => { + let newTreeData = cloneDeep(prevData); + const dropIndex = Number(dropPos[dropPos.length - 1]); + const dragIndex = Number(dragPos[dragPos.length - 1]); + const parentNodePos = dropPos.slice(0, -1).join('-'); + + // TODO: handle drag and drop for childen of root (container components for example) + // findNodeByPos does not work yet + const parentNode = parentNodePos === "0" ? { children: newTreeData } : findNodeByPos(newTreeData, parentNodePos); + + console.log('parentNode', parentNode); + + if (parentNode && parentNode.children) { + const draggedNodeIndex = parentNode.children.findIndex(node => node.key === info.dragNode.key); + if (draggedNodeIndex !== -1) { + const [draggedNode] = parentNode.children.splice(draggedNodeIndex, 1); + parentNode.children.splice(dropIndex > dragIndex ? dropIndex - 1 : dropIndex, 0, draggedNode); + } + } + + return newTreeData; + }); + } + }; + + const findNodeByPos = (nodes: NodeItem[], pos: string): { children: NodeItem[] } => { + const posArr = pos.split('-').map(p => Number(p)); + let currentNode = { children: nodes }; + for (let i = 0; i < posArr.length; i++) { + currentNode = currentNode.children[posArr[i]]; + } + return currentNode; + }; + + const getTreeUI = (type: TreeUIKey) => { + // here the components get sorted by name + // TODO: sort by category + // TODO: sort by Types etc. + const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]); + /* const tree = + type === TreeUIKey.Components + ? editorState.getUIComp().getTree() + : editorState.getHooksComp().getUITree(); + const explorerData: NodeItem[] = getTree(tree, []); */ let selectedKeys = []; if (editorState.selectedCompNames.size === 1) { const key = Object.keys(editorState.selectedComps())[0]; - const parentKeys = getParentNodeKeysByKey(explorerData, key); + const parentKeys = getParentNodeKeysByKey(type === TreeUIKey.Components ? componentTreeData : modalsTreeData, key); if (parentKeys && parentKeys.length) { let needSet = false; parentKeys.forEach((key) => { @@ -433,12 +511,11 @@ export const LeftContent = (props: LeftContentProps) => { return ( props.type && (CompStateIcon[props.type] || )} + draggable={type === TreeUIKey.Components ? true : false} + onDragEnter={handleDragEnter} + onDrop={(info) => handleDrop(info, type)} + treeData={type === TreeUIKey.Components ? componentTreeData : modalsTreeData} icon={(props: any) => props.type && (CompStateIcon[props.type as UICompType] || )} - // switcherIcon={({ expanded }: { expanded: boolean }) => - // expanded ? : - // } switcherIcon={(props: any) => props.expanded ? : } @@ -455,15 +532,15 @@ export const LeftContent = (props: LeftContentProps) => { if (isAggregationApp(editorState.getAppType())) { return; } - return getTreeUI(TreeUIKey.Components); - }, [editorState, uiCollapseClick, expandedKeys, showData]); - + return getTreeUI(TreeUIKey.Components); // Pass componentTreeData + }, [editorState, uiCollapseClick, expandedKeys, showData, componentTreeData]); + const modalsCollapse = useMemo(() => { if (isAggregationApp(editorState.getAppType())) { return; } - return getTreeUI(TreeUIKey.Modals); - }, [editorState, uiCollapseClick, expandedKeys, showData]); + return getTreeUI(TreeUIKey.Modals); // Pass modalsTreeData + }, [editorState, uiCollapseClick, expandedKeys, showData, modalsTreeData]); const bottomResCollapse = useMemo(() => { return editorState @@ -549,4 +626,4 @@ export const LeftContent = (props: LeftContentProps) => { ); -}; +}; \ No newline at end of file diff --git a/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx b/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx index 58d7ffd5a..01ba97c43 100644 --- a/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorSkeletonView.tsx @@ -7,7 +7,7 @@ import { LeftPanel, MiddlePanel, } from "pages/common/styledComponent"; -import { getPanelStatus, getEditorModeStatus, getPanelStyle, getCollissionStatus } from "util/localStorageUtil"; +import { getPanelStatus, getEditorModeStatus, getPanelStyle, getCollisionStatus } from "util/localStorageUtil"; import { BottomSkeleton } from "pages/editor/bottom/BottomContent"; import RightPanel from "pages/editor/right/RightPanel"; import _ from "lodash"; @@ -48,7 +48,7 @@ export const EditorLoadingSpin = (props: { height?: string | number }) => { export default function EditorSkeletonView() { const panelStatus = getPanelStatus(); const editorModeStatus = getEditorModeStatus(); - const collissionStatus = getCollissionStatus(); + const collisionStatus = getCollisionStatus(); const panelStyle = getPanelStyle(); const isUserViewMode = useUserViewMode(); const isTemplate = useTemplateViewMode(); @@ -70,8 +70,8 @@ export default function EditorSkeletonView() { {panelStatus.left && ( diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index e1b966688..8cd7390fe 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -40,7 +40,6 @@ import { import RightPanel from "pages/editor/right/RightPanel"; import EditorTutorials from "pages/tutorials/editorTutorials"; import { - CollisionState, editorContentClassName, UserGuideLocationState, } from "pages/tutorials/tutorialsConstant"; @@ -67,15 +66,13 @@ import { DefaultEditorModeStatus, getEditorModeStatus, saveEditorModeStatus, - saveEnableCollissionStatus, - getCollissionStatus, - DefaultCollissionStatus, + saveCollisionStatus, + getCollisionStatus, } from "util/localStorageUtil"; import Bottom from "./bottom/BottomPanel"; import { LeftContent } from "./LeftContent"; import { isAggregationApp } from "util/appUtils"; import { Switch } from "antd"; -import { SwitchChangeEventHandler } from "antd/es/switch"; const HookCompContainer = styled.div` pointer-events: none; @@ -229,9 +226,9 @@ const items = [ ]; // added by Fred to set comp collision state -export type EnabledCollissionStatus = "true" | "false"; // "true" means collission is enabled, "false" means collission is disabled -export type ToggleCollissionStatus = ( - collissionStatus?: EnabledCollissionStatus +export type DisabledCollisionStatus = "true" | "false"; // "true" means collision is not enabled - Layering works, "false" means collision is enabled - Layering does not work +export type ToggleCollisionStatus = ( + collisionStatus?: DisabledCollisionStatus ) => void; function EditorView(props: EditorViewProps) { @@ -240,9 +237,7 @@ function EditorView(props: EditorViewProps) { const { readOnly, hideHeader } = useContext(ExternalEditorContext); const application = useSelector(currentApplication); const locationState = useLocation().state; - const collisionState = useLocation().state; const showNewUserGuide = locationState?.showNewUserGuide; - const showCollission = collisionState?.collission; const showAppSnapshot = useSelector(showAppSnapshotSelector); const [showShortcutList, setShowShortcutList] = useState(false); const toggleShortcutList = useCallback( @@ -280,22 +275,23 @@ function EditorView(props: EditorViewProps) { [panelStatus, prePanelStatus] ); - + // added by Falk Wolsky to support a Layers in Lowcoder const [collisionStatus, setCollisionStatus] = useState(() => { - return showCollission ? DefaultCollissionStatus : getCollissionStatus(); + return getCollisionStatus(); }); - const toggleCollissionStatus: ToggleCollissionStatus = useCallback( + const toggleCollisionStatus: ToggleCollisionStatus = useCallback( (value) => { - setCollisionStatus(value ? value : "false"); - saveEnableCollissionStatus(value ? value : "false"); + setCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); + saveCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); }, [collisionStatus] ); + // added by Falk Wolsky to support a Layout and Logic Mode in Lowcoder const [editorModeStatus, setEditorModeStatus] = useState(() => { - return showNewUserGuide ? DefaultEditorModeStatus : getEditorModeStatus(); + return getEditorModeStatus(); }); const toggleEditorModeStatus: ToggleEditorModeStatus = useCallback( @@ -479,11 +475,11 @@ function EditorView(props: EditorViewProps) { {menuKey === SiderKey.Layout && ( { - toggleCollissionStatus(value == true ? "true" : "false"); - editorState.setCollissionStatus(value == true ? "true" : "false"); + toggleCollisionStatus(value == true ? "true" : "false"); + editorState.setCollisionStatus(value == true ? "true" : "false"); }} /> diff --git a/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx b/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx index 550f07214..147327178 100644 --- a/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx +++ b/client/packages/lowcoder/src/pages/tutorials/tutorialsConstant.tsx @@ -31,8 +31,3 @@ export const defaultJoyrideFloaterProps: FloaterType = { export type UserGuideLocationState = { showNewUserGuide?: boolean; }; - -export type CollisionState = { - showNewUserGuide?: boolean; - collission?: boolean; -}; diff --git a/client/packages/lowcoder/src/util/localStorageUtil.ts b/client/packages/lowcoder/src/util/localStorageUtil.ts index 15e951909..2f102a696 100644 --- a/client/packages/lowcoder/src/util/localStorageUtil.ts +++ b/client/packages/lowcoder/src/util/localStorageUtil.ts @@ -1,5 +1,5 @@ import { PanelStatus } from "pages/common/header"; -import { EnabledCollissionStatus } from "pages/editor/editorView"; +import { DisabledCollisionStatus as DisabledCollisionStatus } from "pages/editor/editorView"; import { EditorModeStatus } from "pages/common/header"; import log from "loglevel"; import { JSONValue } from "util/jsonTypes"; @@ -45,20 +45,20 @@ export function getPanelStatus(): PanelStatus { export function saveEditorModeStatus(editorModeStatus: EditorModeStatus) { localStorage.setItem("editor_mode_status", editorModeStatus); } -//ADDED BY FRED TO SAVE enabledCollission -export function saveEnableCollissionStatus( - collisionStatus: EnabledCollissionStatus +//ADDED BY FRED TO SAVE enabledCollision +export function saveCollisionStatus( + collisionStatus: DisabledCollisionStatus ) { - localStorage.setItem("enable_collission", collisionStatus); + localStorage.setItem("disable_collision", collisionStatus); } -export const DefaultCollissionStatus: EnabledCollissionStatus = "true"; -export function getCollissionStatus(): EnabledCollissionStatus { - const str = localStorage.getItem("enable_collission"); +export const DefaultCollisionStatus: DisabledCollisionStatus = "true"; +export function getCollisionStatus(): DisabledCollisionStatus { + const str = localStorage.getItem("disable_collision"); if (!str) { - return DefaultCollissionStatus; + return DefaultCollisionStatus; } - return str as EnabledCollissionStatus; + return str as DisabledCollisionStatus; } export const DefaultEditorModeStatus: EditorModeStatus = "both"; From 27079d9161d2390f641a90756e5ecfbc5ec4e4f6 Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Fri, 2 Feb 2024 20:44:25 +0500 Subject: [PATCH 4/9] component sorting and z-index based on sorting position --- .../comps/containerComp/containerView.tsx | 16 +++++- .../lowcoder/src/comps/comps/textComp.tsx | 2 +- .../packages/lowcoder/src/constants/Layers.ts | 2 +- .../packages/lowcoder/src/layout/gridItem.tsx | 12 +++-- .../lowcoder/src/layout/gridLayout.tsx | 12 ++++- client/packages/lowcoder/src/layout/utils.ts | 1 + .../lowcoder/src/pages/editor/LeftContent.tsx | 53 ++++++++++++++++++- .../src/pages/editor/styledComponents.tsx | 5 ++ 8 files changed, 92 insertions(+), 11 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index 26ac5561e..45d8f7d26 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -230,6 +230,14 @@ const onDrop = ( }; const key = genRandomKey(); const layoutItem = Object.values(items)[0]; + // calculate postion of newly added comp + // should have last position in the comps list + let itemPos = 0; + if (!Object.keys(layout).length) { + itemPos = 0; + } else { + itemPos = Math.max(...Object.values(layout).map(l => l.pos || 0)) + 1; + } // log.debug("layout: onDrop. widgetValue: ", widgetValue, " layoutItem: ", layoutItem); dispatch( wrapActionExtraInfo( @@ -237,7 +245,12 @@ const onDrop = ( layout: changeValueAction( { ...layout, - [key]: { ...layoutItem, i: key, placeholder: undefined }, + [key]: { + ...layoutItem, + i: key, + placeholder: undefined, + pos: itemPos, + }, }, true ), @@ -463,6 +476,7 @@ export function InnerGrid(props: ViewPropsWithSelect) { layout={props.layout} extraLayout={extraLayout} onDropDragOver={(e) => { + const compType = draggingUtils.getData("compType"); const compLayout = draggingUtils.getData("compLayout"); if (compType) { diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx index 671e67354..aec1f8af1 100644 --- a/client/packages/lowcoder/src/comps/comps/textComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx @@ -37,7 +37,7 @@ const getStyle = (style: TextStyleType) => { margin: ${style.margin} !important; padding: ${style.padding}; width: ${widthCalculator(style.margin)}; - height: ${heightCalculator(style.margin)}; + // height: ${heightCalculator(style.margin)}; h1 { line-height: 1.5; } diff --git a/client/packages/lowcoder/src/constants/Layers.ts b/client/packages/lowcoder/src/constants/Layers.ts index fcd2a361b..358f52dcc 100644 --- a/client/packages/lowcoder/src/constants/Layers.ts +++ b/client/packages/lowcoder/src/constants/Layers.ts @@ -30,7 +30,7 @@ export const Layers = { // comp selection wrapper dragSelectBox: 399, // - compHover: 300, + compHover: 100, // compSelected: 200, // diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx index ba80c46f5..d3fec9367 100644 --- a/client/packages/lowcoder/src/layout/gridItem.tsx +++ b/client/packages/lowcoder/src/layout/gridItem.tsx @@ -34,6 +34,7 @@ type GridItemCallback = ( arg3: Data ) => void; export type GridItemProps = { + zIndex: number; children: ReactElement; cols: number; containerWidth: number; @@ -81,7 +82,8 @@ export type GridItemProps = { export const IsDroppable = React.createContext(true); -const ResizableStyled = styled(Resizable)` +const ResizableStyled = styled(Resizable)<{ $zIndex: number }>` + z-index: ${props => props.$zIndex}; &:hover { z-index: 1; } @@ -151,7 +153,8 @@ export function GridItem(props: GridItemProps) { const mixinResizable = ( child: ReactElement, position: Position, - isResizable: boolean + isResizable: boolean, + zIndex: number, ): ReactElement => { const { cols, x, minW, minH, maxW, maxH, resizeHandles } = props; // This is the max possible width - doesn't go to infinity because of the width of the window @@ -179,6 +182,7 @@ export function GridItem(props: GridItemProps) { onResizeStop={onResizeStop} resizeHandles={resizeHandles} handle={Handle} + $zIndex={zIndex} > {child} @@ -397,7 +401,7 @@ export function GridItem(props: GridItemProps) { return { width, height, top, left }; }, [dragging, position.height, position.left, position.top, position.width, resizing]); - const { isDraggable, isResizable, layoutHide, children, isSelected, clickItem } = props; + const { isDraggable, isResizable, layoutHide, children, isSelected, clickItem, zIndex } = props; const pos = calcPosition(); const render = () => { let child = React.Children.only(children); @@ -433,7 +437,7 @@ export function GridItem(props: GridItemProps) { }, }); // Resizable support. This is usually on but the user can toggle it off. - newChild = mixinResizable(newChild, pos, isResizable); + newChild = mixinResizable(newChild, pos, isResizable, zIndex); // Draggable support. This is always on, except for with placeholders. newChild = mixinDraggable(newChild, isDraggable); return newChild; diff --git a/client/packages/lowcoder/src/layout/gridLayout.tsx b/client/packages/lowcoder/src/layout/gridLayout.tsx index 328ff1ab6..0058ff71f 100644 --- a/client/packages/lowcoder/src/layout/gridLayout.tsx +++ b/client/packages/lowcoder/src/layout/gridLayout.tsx @@ -393,6 +393,7 @@ class GridLayout extends React.Component { }; processGridItem( + zIndex: number, item: LayoutItem, childrenMap: _.Dictionary ): React.ReactElement | undefined { @@ -464,6 +465,7 @@ class GridLayout extends React.Component { top: showName?.top ?? 0, bottom: (showName?.bottom ?? 0) + (this.ref.current?.scrollHeight ?? 0), }} + zIndex={zIndex} > {child} @@ -863,7 +865,6 @@ class GridLayout extends React.Component { // move the logic to onDragEnd function when dragging from the canvas return; } - let layout = this.getUILayout(); const ops = layoutOpUtils.push(this.state.ops, deleteItemOp(droppingKey)); const items = _.pick(layout, droppingKey); @@ -1001,6 +1002,7 @@ class GridLayout extends React.Component { this.ref = this.props.innerRef ?? this.innerRef; // log.debug("GridLayout render. layout: ", layout, " oriLayout: ", this.state.layout, " extraLayout: ", this.props.extraLayout); + const layouts = Object.values(layout); return ( {
{showGridLines && this.gridLines()} {mounted && - Object.values(layout).map((item) => this.processGridItem(item, childrenMap))} + layouts.map((item) => { + const zIndex = item.pos !== undefined + ? layouts.length - item.pos + : 1; + return this.processGridItem(zIndex, item, childrenMap) + }) + } {this.hintPlaceholder()}
diff --git a/client/packages/lowcoder/src/layout/utils.ts b/client/packages/lowcoder/src/layout/utils.ts index 6a9f0494d..40ee0ed72 100644 --- a/client/packages/lowcoder/src/layout/utils.ts +++ b/client/packages/lowcoder/src/layout/utils.ts @@ -14,6 +14,7 @@ export type LayoutItem = { x: number; y: number; i: string; + pos?: number; minW?: number; minH?: number; maxW?: number; diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index 01de26f67..66f32141c 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -33,6 +33,9 @@ import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; import { DataNode, EventDataNode } from "antd/lib/tree"; import { isAggregationApp } from "util/appUtils"; import cloneDeep from 'lodash/cloneDeep'; +import { useDispatch } from "react-redux"; +import { useApplicationId } from "util/hooks"; +import { updateApplication } from "redux/reduxActions/applicationActions"; const CollapseTitleWrapper = styled.div` display: flex; @@ -209,6 +212,8 @@ type NodeItem = { title: string; type?: UICompType; children: NodeItem[]; + pos?: number; + disabled?: boolean; }; type NodeInfo = { @@ -243,12 +248,14 @@ export const LeftContent = (props: LeftContentProps) => { const editorState = useContext(EditorContext); const [expandedKeys, setExpandedKeys] = useState>([]); const [showData, setShowData] = useState([]); + const dispatch = useDispatch(); + const applicationId = useApplicationId(); const getTree = (tree: CompTree, result: NodeItem[], key?: string) => { const { items, children } = tree; if (Object.keys(items).length) { for (const i in items) { - const info = { + const info: NodeItem = { title: items[i].children.name.getView(), type: items[i].children.compType.getView() as UICompType, key: i, @@ -256,12 +263,13 @@ export const LeftContent = (props: LeftContentProps) => { }; if (key) { const parent = getTreeNodeByKey(result, key); + info.disabled = true; parent?.children.push(info); } else { result.push(info); } } - result = _.sortBy(result, [(x) => x.title]); + // result = _.sortBy(result, [(x) => x.title]); } if (Object.keys(children).length) { for (const i in children) { @@ -428,6 +436,20 @@ export const LeftContent = (props: LeftContentProps) => { ? editorState.getUIComp().getTree() : editorState.getHooksComp().getUITree(); const explorerData: NodeItem[] = getTree(tree, []); + // TODO: handle sorting inside modals/drawers + if(type === TreeUIKey.Modals) return explorerData; + + const dsl = editorState.rootComp.toJsonValue(); + explorerData.forEach(data => { + data['pos'] = dsl.ui.layout[data.key].pos; + }) + explorerData.sort((a, b) => { + const aPos = a?.pos || 0; + const bPos = b?.pos || 0; + if (aPos < bPos) return -1; + if (aPos > bPos) return 1; + return 0; + }); return explorerData; } @@ -468,7 +490,34 @@ export const LeftContent = (props: LeftContentProps) => { parentNode.children.splice(dropIndex > dragIndex ? dropIndex - 1 : dropIndex, 0, draggedNode); } } + + const dsl = editorState.rootComp.toJsonValue(); + let layout: any = {}; + parentNode.children.forEach((data, index) => { + layout[data.key] = { + ...dsl.ui.layout[data.key], + pos: index, + }; + }) + if ( type === TreeUIKey.Modals) return newTreeData; + + dispatch( + updateApplication({ + applicationId: applicationId, + editingApplicationDSL: { + ...dsl, + ui: { + ...dsl.ui, + layout, + } + } as object, + }) + ); + editorState.rootComp.children.ui.dispatchChangeValueAction({ + ...dsl.ui, + layout, + }) return newTreeData; }); } diff --git a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx index c0d68b2f9..6df7b9d7a 100644 --- a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx +++ b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx @@ -58,6 +58,11 @@ export const DirectoryTreeStyle = styled(DirectoryTree)` .ant-tree-node-content-wrapper.ant-tree-node-selected { color: #333; } + .ant-tree-treenode-disabled { + .ant-tree-node-content-wrapper { + color: inherit; + } + } } `; From 5323da554b3256a8231bc84d9245611a0cf030ba Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Sat, 3 Feb 2024 14:16:50 +0100 Subject: [PATCH 5/9] [WIP] Own Display for Layers --- .../lowcoder-design/src/icons/index.ts | 312 ++++++++--------- .../packages/lowcoder/src/layout/gridItem.tsx | 18 +- .../lowcoder/src/pages/editor/LeftContent.tsx | 115 ++----- .../src/pages/editor/LeftLayersContent.tsx | 325 ++++++++++++++++++ .../lowcoder/src/pages/editor/editorView.tsx | 41 +-- .../src/pages/editor/styledComponents.tsx | 12 +- .../lowcoder/src/util/localStorageUtil.ts | 2 +- 7 files changed, 532 insertions(+), 293 deletions(-) create mode 100644 client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index 2afd2a739..8b80e30bf 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -298,104 +298,106 @@ export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg"; export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg"; export { ReactComponent as WidthIcon } from "icons/icon-width.svg"; export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/icon-responsive-layout-comp.svg"; -export { ReactComponent as TextSizeIcon } from "./remix/font-size-2.svg"; -export { ReactComponent as FontFamilyIcon } from "./remix/font-sans-serif.svg"; -export { ReactComponent as TextWeigthIcon } from "./remix/bold.svg"; -export { ReactComponent as BorderWidthIcon } from "./remix/expand-width-line.svg"; +export { ReactComponent as TextSizeIcon } from "icons/remix/font-size-2.svg"; +export { ReactComponent as FontFamilyIcon } from "icons/remix/font-sans-serif.svg"; +export { ReactComponent as TextWeigthIcon } from "icons/remix/bold.svg"; +export { ReactComponent as BorderWidthIcon } from "icons/remix/expand-width-line.svg"; +export { ReactComponent as LeftInfoLine } from "icons/remix/information-line.svg"; +export { ReactComponent as LeftInfoFill } from "icons/remix/information-fill.svg"; // new -/* export { ReactComponent as AppSnapshotIcon } from "./remix/screenshot-2-line.svg"; // Closest match for app snapshot +/* export { ReactComponent as AppSnapshotIcon } from "icons/remix/screenshot-2-line.svg"; // Closest match for app snapshot export { ReactComponent as HookCompDropIcon } from "./hook-comp-drop.svg"; export { ReactComponent as HookCompIcon } from "./hook-comp.svg"; -export { ReactComponent as IconsIcon } from "./remix/apps-line.svg"; // Closest match for icons +export { ReactComponent as IconsIcon } from "icons/remix/apps-line.svg"; // Closest match for icons export { ReactComponent as JsGrayIcon } from "./icon-Js-Gray.svg"; export { ReactComponent as JsColorsIcon } from "./icon-Js-colors.svg"; -export { ReactComponent as AdminIcon } from "./remix/admin-line.svg"; // Exact match +export { ReactComponent as AdminIcon } from "icons/remix/admin-line.svg"; // Exact match export { ReactComponent as AlignVerticalCent } from "./icon-align-vertical-center.svg"; -export { ReactComponent as AppEditIcon } from "./remix/edit-box-line.svg"; // Closest match for app edit +export { ReactComponent as AppEditIcon } from "icons/remix/edit-box-line.svg"; // Closest match for app edit export { ReactComponent as AuditAppIcon } from "./icon-audit-app.svg"; export { ReactComponent as AuditDbIcon } from "./icon-audit-db.svg"; -export { ReactComponent as TreeFoldIcon } from "./remix/tree-line.svg"; // Closest match for tree fold +export { ReactComponent as TreeFoldIcon } from "icons/remix/tree-line.svg"; // Closest match for tree fold export { ReactComponent as AuditQueryIcon } from "./icon-audit-query.svg"; export { ReactComponent as AuditUserIcon } from "./icon-audit-user.svg"; -export { ReactComponent as AuditFolderIcon } from "./remix/folder-line.svg"; // Closest match for audit folder +export { ReactComponent as AuditFolderIcon } from "icons/remix/folder-line.svg"; // Closest match for audit folder export { ReactComponent as AutoCompleteIcon } from "./icon-autoComplete.svg"; -export { ReactComponent as CalendarIcon } from "./remix/calendar-2-line.svg"; // Exact match +export { ReactComponent as CalendarIcon } from "icons/remix/calendar-2-line.svg"; // Exact match export { ReactComponent as CaptchaIcon } from "./icon-captcha.svg"; -export { ReactComponent as CheckboxIcon } from "./remix/checkbox-line.svg"; // Exact match +export { ReactComponent as CheckboxIcon } from "icons/remix/checkbox-line.svg"; // Exact match export { ReactComponent as CheckoutIcon } from "./icon-checkout.svg"; export { ReactComponent as ClickLinkIcon } from "./icon-clickLink.svg"; -export { ReactComponent as CloseEyeIcon } from "./remix/eye-off-line.svg"; // Closest match for close eye +export { ReactComponent as CloseEyeIcon } from "icons/remix/eye-off-line.svg"; // Closest match for close eye export { ReactComponent as CodeEditorCloseIcon } from "./icon-code-editor-close.svg"; export { ReactComponent as CodeEditorOpenIcon } from "./icon-code-editor-open.svg"; -export { ReactComponent as ColorHexIcon } from "./remix/palette-line.svg"; // Closest match for color hex +export { ReactComponent as ColorHexIcon } from "icons/remix/palette-line.svg"; // Closest match for color hex export { ReactComponent as ContainerDragIcon } from "./icon-container-drag.svg"; -export { ReactComponent as CopyIcon } from "./remix/file-copy-line.svg"; // Closest match for copy +export { ReactComponent as CopyIcon } from "icons/remix/file-copy-line.svg"; // Closest match for copy export { ReactComponent as CreateModuleIcon } from "./icon-create-module.svg"; export { ReactComponent as PrevIcon } from "./icon-date-prev.svg"; export { ReactComponent as SuperPrevIcon } from "./icon-date-super-prev.svg"; export { ReactComponent as DragWhiteIcon } from "./icon-drag-white.svg"; -export { ReactComponent as EmptyDataIcon } from "./remix/emotion-unhappy-line.svg"; // Closest match for empty data +export { ReactComponent as EmptyDataIcon } from "icons/remix/emotion-unhappy-line.svg"; // Closest match for empty data export { ReactComponent as FlokcloseIcon } from "./icon-flokclose.svg"; export { ReactComponent as FoldedIcon } from "./icon-folded.svg"; -export { ReactComponent as GridIcon } from "./remix/grid-line.svg"; // Exact match -export { ReactComponent as GroupIcon } from "./remix/group-line.svg"; // Closest match for group -export { ReactComponent as HelpIcon } from "./remix/question-answer-line.svg"; // Closest match for help -export { ReactComponent as LayoutIcon } from "./remix/layout-6-line.svg"; // Closest match for layout -export { ReactComponent as LockIcon } from "./remix/lock-line.svg"; // Exact match -export { ReactComponent as MembersIcon } from "./remix/group-line.svg"; // Closest match for members -export { ReactComponent as MoreActionIcon } from "./remix/more-2-line.svg"; // Closest match for more action -export { ReactComponent as MultiselectTagIcon } from "./remix/price-tag-3-line.svg"; // Closest match for multiselect tag -export { ReactComponent as MustFillStarIcon } from "./remix/star-line.svg"; // Closest match for must fill star -export { ReactComponent as NofileIcon } from "./remix/file-unknow-line.svg"; // Closest match for nofile +export { ReactComponent as GridIcon } from "icons/remix/grid-line.svg"; // Exact match +export { ReactComponent as GroupIcon } from "icons/remix/group-line.svg"; // Closest match for group +export { ReactComponent as HelpIcon } from "icons/remix/question-answer-line.svg"; // Closest match for help +export { ReactComponent as LayoutIcon } from "icons/remix/layout-6-line.svg"; // Closest match for layout +export { ReactComponent as LockIcon } from "icons/remix/lock-line.svg"; // Exact match +export { ReactComponent as MembersIcon } from "icons/remix/group-line.svg"; // Closest match for members +export { ReactComponent as MoreActionIcon } from "icons/remix/more-2-line.svg"; // Closest match for more action +export { ReactComponent as MultiselectTagIcon } from "icons/remix/price-tag-3-line.svg"; // Closest match for multiselect tag +export { ReactComponent as MustFillStarIcon } from "icons/remix/star-line.svg"; // Closest match for must fill star +export { ReactComponent as NofileIcon } from "icons/remix/file-unknow-line.svg"; // Closest match for nofile export { ReactComponent as OmitIcon } from "./icon-omit.svg"; -export { ReactComponent as OpenEyeIcon } from "./remix/eye-line.svg"; // Closest match for open eye -export { ReactComponent as PasswordIcon } from "./remix/lock-password-line.svg"; // Closest match for password -export { ReactComponent as RadioCheckedIcon } from "./remix/list-radio.svg"; // Closest match for radio checked -export { ReactComponent as RequiredIcon } from "./remix/alert-line.svg"; // Closest match for required +export { ReactComponent as OpenEyeIcon } from "icons/remix/eye-line.svg"; // Closest match for open eye +export { ReactComponent as PasswordIcon } from "icons/remix/lock-password-line.svg"; // Closest match for password +export { ReactComponent as RadioCheckedIcon } from "icons/remix/list-radio.svg"; // Closest match for radio checked +export { ReactComponent as RequiredIcon } from "icons/remix/alert-line.svg"; // Closest match for required export { ReactComponent as AttributeIcon } from "./icon-right-attribute.svg"; export { ReactComponent as InsertIcon } from "./icon-right-insert.svg"; export { ReactComponent as ShowBorderIcon } from "./icon-show-border.svg"; export { ReactComponent as SpaceIcon } from "./icon-space.svg"; -export { ReactComponent as StarIcon } from "./remix/star-line.svg"; // Exact match +export { ReactComponent as StarIcon } from "icons/remix/star-line.svg"; // Exact match export { ReactComponent as SuperUserIcon } from "./icon-super-user.svg"; -export { ReactComponent as SwitchCheckedIcon } from "./remix/toggle-line.svg"; // Closest match for switch checked +export { ReactComponent as SwitchCheckedIcon } from "icons/remix/toggle-line.svg"; // Closest match for switch checked export { ReactComponent as TextEditIcon } from "./icon-text-edit.svg"; export { ReactComponent as TextboxIcon } from "./icon-textbox.svg"; -export { ReactComponent as TriangleIcon } from "./remix/arrow-up-line.svg"; // Closest match for triangle -export { ReactComponent as TypographyIcon } from "./remix/file-text-line.svg"; // Closest match for typography +export { ReactComponent as TriangleIcon } from "icons/remix/arrow-up-line.svg"; // Closest match for triangle +export { ReactComponent as TypographyIcon } from "icons/remix/file-text-line.svg"; // Closest match for typography export { ReactComponent as UnfoldWhiteIcon } from "./icon-unfold-white.svg"; export { ReactComponent as UnfoldIcon } from "./icon-unfold.svg"; export { ReactComponent as WarningWhiteIcon } from "./icon-warning-white.svg"; -export { ReactComponent as WarningIcon } from "./remix/error-warning-line.svg"; // Closest match for warning +export { ReactComponent as WarningIcon } from "icons/remix/error-warning-line.svg"; // Closest match for warning export { ReactComponent as WidthDragIcon } from "./icon-widthDrag.svg"; -export { ReactComponent as ManyCheckboxIcon } from "./remix/checkbox-multiple-line.svg"; // Closest match for many checkbox -export { ReactComponent as Layout } from "./remix/layout-2-line.svg"; // Closest match for layout -export { ReactComponent as Left } from "./remix/arrow-left-s-line.svg"; // Closest match for left -export { ReactComponent as Middle } from "./remix/align-vertically.svg"; // Closest match for middle -export { ReactComponent as Right } from "./remix/arrow-right-s-line.svg"; // Closest match for right +export { ReactComponent as ManyCheckboxIcon } from "icons/remix/checkbox-multiple-line.svg"; // Closest match for many checkbox +export { ReactComponent as Layout } from "icons/remix/layout-2-line.svg"; // Closest match for layout +export { ReactComponent as Left } from "icons/remix/arrow-left-s-line.svg"; // Closest match for left +export { ReactComponent as Middle } from "icons/remix/align-vertically.svg"; // Closest match for middle +export { ReactComponent as Right } from "icons/remix/arrow-right-s-line.svg"; // Closest match for right export { ReactComponent as DeployIcon } from "./icon-rocket.svg"; -export { ReactComponent as ExportIcon } from "./remix/share-forward-line.svg"; // Closest match for export +export { ReactComponent as ExportIcon } from "icons/remix/share-forward-line.svg"; // Closest match for export export { ReactComponent as BluePlusIcon } from "./icon-blue-add.svg"; -export { ReactComponent as PencilIcon } from "./remix/pencil-line.svg"; // Closest match for pencil -export { ReactComponent as DragIcon } from "./remix/drag-move-2-line.svg"; // Closest match for drag +export { ReactComponent as PencilIcon } from "icons/remix/pencil-line.svg"; // Closest match for pencil +export { ReactComponent as DragIcon } from "icons/remix/drag-move-2-line.svg"; // Closest match for drag export { ReactComponent as PointIcon } from "./icon-three-point.svg"; -export { ReactComponent as AlignJustify } from "./remix/align-justify.svg"; // Exact match -export { ReactComponent as AlignRight } from "./remix/align-right.svg"; // Exact match -export { ReactComponent as AlignCenter } from "./remix/align-center.svg"; // Exact match -export { ReactComponent as AlignLeft } from "./remix/align-left.svg"; // Exact match +export { ReactComponent as AlignJustify } from "icons/remix/align-justify.svg"; // Exact match +export { ReactComponent as AlignRight } from "icons/remix/align-right.svg"; // Exact match +export { ReactComponent as AlignCenter } from "icons/remix/align-center.svg"; // Exact match +export { ReactComponent as AlignLeft } from "icons/remix/align-left.svg"; // Exact match export { ReactComponent as AlignClose } from "./icon-align-close.svg"; export { ReactComponent as AlignTop } from "./icon-align-top.svg"; export { ReactComponent as AlignBottom } from "./icon-align-bottom.svg"; -export { ReactComponent as AddIcon } from "./remix/add-line.svg"; // Closest match for add +export { ReactComponent as AddIcon } from "icons/remix/add-line.svg"; // Closest match for add export { ReactComponent as ApplicationDocIcon } from "./icon-application-doc.svg"; -export { ReactComponent as ImportAppIcon } from "./remix/download-cloud-line.svg"; // Closest match for import app +export { ReactComponent as ImportAppIcon } from "icons/remix/download-cloud-line.svg"; // Closest match for import app export { ReactComponent as ModuleDocIcon } from "./icon-module-doc.svg"; -export { ReactComponent as ImportIcon } from "./remix/download-cloud-line.svg"; // Closest match for import -export { ReactComponent as ImportIconV2 } from "./remix/download-cloud-line.svg"; // Closest match for import v2 +export { ReactComponent as ImportIcon } from "icons/remix/download-cloud-line.svg"; // Closest match for import +export { ReactComponent as ImportIconV2 } from "icons/remix/download-cloud-line.svg"; // Closest match for import v2 export { ReactComponent as ModuleMenuIcon } from "./icon-module-menu.svg"; -export { ReactComponent as ModuleIcon } from "./remix/stack-line.svg"; // Closest match for module +export { ReactComponent as ModuleIcon } from "icons/remix/stack-line.svg"; // Closest match for module export { ReactComponent as AllAppIcon } from "./icon-all-app.svg"; export { ReactComponent as DatasourceIcon } from "./icon-datasource.svg"; export { ReactComponent as QueryLibraryIcon } from "./icon-query-library.svg"; @@ -403,82 +405,82 @@ export { ReactComponent as TransformerIcon } from "./icon-transformer.svg"; // O export { ReactComponent as TempStateIcon } from "./icon-temp-state.svg"; export { ReactComponent as IconDep } from "./icon-style-dep.svg"; export { ReactComponent as IconRadius } from "./icon-style-border-radius.svg"; -export { ReactComponent as IconReset } from "./remix/refresh-line.svg"; +export { ReactComponent as IconReset } from "icons/remix/refresh-line.svg"; export { ReactComponent as NavComIcon } from "./icon-nav.svg"; export { ReactComponent as ButtonCompIcon } from "./icon-button.svg"; -export { ReactComponent as InputCompIcon } from "./remix/input-method-line.svg"; // Closest match for input component -export { ReactComponent as TextCompIcon } from "./remix/file-text-line.svg"; // Closest match for text component -export { ReactComponent as SwitchCompIcon } from "./remix/switch-line.svg"; // Closest match for switch component -export { ReactComponent as TableCompIcon } from "./remix/table-line.svg"; // Closest match for table component -export { ReactComponent as SelectCompIcon } from "./remix/dropdown-list.svg"; // Closest match for select component -export { ReactComponent as CheckboxCompIcon } from "./remix/checkbox-line.svg"; // Closest match for checkbox component -export { ReactComponent as RadioCompIcon } from "./remix/radio-line.svg"; // Closest match for radio component -export { ReactComponent as TimeCompIcon } from "./remix/time-line.svg"; // Closest match for time component +export { ReactComponent as InputCompIcon } from "icons/remix/input-method-line.svg"; // Closest match for input component +export { ReactComponent as TextCompIcon } from "icons/remix/file-text-line.svg"; // Closest match for text component +export { ReactComponent as SwitchCompIcon } from "icons/remix/switch-line.svg"; // Closest match for switch component +export { ReactComponent as TableCompIcon } from "icons/remix/table-line.svg"; // Closest match for table component +export { ReactComponent as SelectCompIcon } from "icons/remix/dropdown-list.svg"; // Closest match for select component +export { ReactComponent as CheckboxCompIcon } from "icons/remix/checkbox-line.svg"; // Closest match for checkbox component +export { ReactComponent as RadioCompIcon } from "icons/remix/radio-line.svg"; // Closest match for radio component +export { ReactComponent as TimeCompIcon } from "icons/remix/time-line.svg"; // Closest match for time component export { ReactComponent as TimeRangeCompIcon } from "./icon-timeRange.svg"; -export { ReactComponent as DateCompIcon } from "./remix/calendar-2-line.svg"; // Closest match for date component +export { ReactComponent as DateCompIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for date component export { ReactComponent as DateRangeCompIcon } from "./icon-dateRange.svg"; -export { ReactComponent as UploadCompIcon } from "./remix/upload-cloud-2-line.svg"; // Closest match for upload component -export { ReactComponent as ImageCompIcon } from "./remix/image-line.svg"; // Closest match for image component -export { ReactComponent as RatingCompIcon } from "./remix/star-line.svg"; // Closest match for rating component -export { ReactComponent as SliderCompIcon } from "./remix/git-commit-line.svg"; // Closest match for slider component +export { ReactComponent as UploadCompIcon } from "icons/remix/upload-cloud-2-line.svg"; // Closest match for upload component +export { ReactComponent as ImageCompIcon } from "icons/remix/image-line.svg"; // Closest match for image component +export { ReactComponent as RatingCompIcon } from "icons/remix/star-line.svg"; // Closest match for rating component +export { ReactComponent as SliderCompIcon } from "icons/remix/git-commit-line.svg"; // Closest match for slider component export { ReactComponent as ProcessCircleCompIcon } from "./icon-sliderCircle.svg"; -export { ReactComponent as ChartCompIcon } from "./remix/pie-chart-2-line.svg"; // Closest match for chart component +export { ReactComponent as ChartCompIcon } from "icons/remix/pie-chart-2-line.svg"; // Closest match for chart component export { ReactComponent as JsonFormCompIcon } from "./icon-jsonForm.svg"; -export { ReactComponent as FormCompIcon } from "./remix/draft-line.svg"; // Closest match for form component +export { ReactComponent as FormCompIcon } from "icons/remix/draft-line.svg"; // Closest match for form component export { ReactComponent as ProgressCompIcon } from "./icon-progress.svg"; export { ReactComponent as FileViewerCompIcon } from "./icon-fileViewer.svg"; export { ReactComponent as ContainerCompIcon } from "./icon-container.svg"; -export { ReactComponent as LinkCompIcon } from "./remix/links-line.svg"; // Closest match for link component -export { ReactComponent as MultiSelectCompIcon } from "./remix/list-check-3.svg"; // Closest match for multiselect component -export { ReactComponent as PasswordCompIcon } from "./remix/key-2-line.svg"; // Closest match for password component -export { ReactComponent as RangeSliderCompIcon } from "./remix/switch-line.svg"; // Closest match for range slider component +export { ReactComponent as LinkCompIcon } from "icons/remix/links-line.svg"; // Closest match for link component +export { ReactComponent as MultiSelectCompIcon } from "icons/remix/list-check-3.svg"; // Closest match for multiselect component +export { ReactComponent as PasswordCompIcon } from "icons/remix/key-2-line.svg"; // Closest match for password component +export { ReactComponent as RangeSliderCompIcon } from "icons/remix/switch-line.svg"; // Closest match for range slider component export { ReactComponent as SegmentedCompIcon } from "./icon-insert-segmentedControl.svg"; -export { ReactComponent as TextAreaCompIcon } from "./remix/text-block.svg"; // Closest match for textarea component +export { ReactComponent as TextAreaCompIcon } from "icons/remix/text-block.svg"; // Closest match for textarea component export { ReactComponent as NumberInputCompIcon } from "./icon-insert-numberInput.svg"; -export { ReactComponent as DropdownCompIcon } from "./remix/dropdown-list.svg"; // Closest match for dropdown component +export { ReactComponent as DropdownCompIcon } from "icons/remix/dropdown-list.svg"; // Closest match for dropdown component export { ReactComponent as DividerCompIcon } from "./icon-insert-divider.svg"; export { ReactComponent as RichTextEditorCompIcon } from "./icon-insert-rich-text-editor.svg"; -export { ReactComponent as ModalCompIcon } from "./remix/window-line.svg"; // Closest match for modal component -export { ReactComponent as TabbedContainerCompIcon } from "./remix/layout-column-line.svg"; // Closest match for tabbed container +export { ReactComponent as ModalCompIcon } from "icons/remix/window-line.svg"; // Closest match for modal component +export { ReactComponent as TabbedContainerCompIcon } from "icons/remix/layout-column-line.svg"; // Closest match for tabbed container export { ReactComponent as CascaderCompIcon } from "./icon-cascader.svg"; -export { ReactComponent as TreeIcon } from "./remix/node-tree.svg"; // Closest match for tree icon +export { ReactComponent as TreeIcon } from "icons/remix/node-tree.svg"; // Closest match for tree icon export { ReactComponent as TreeSelectIcon } from "./icon-tree-select.svg"; export { ReactComponent as CustomCompIcon } from "./icon-custom-component.svg"; -export { ReactComponent as IFrameCompIcon } from "./remix/window-2-line.svg"; // Closest match for iframe component +export { ReactComponent as IFrameCompIcon } from "icons/remix/window-2-line.svg"; // Closest match for iframe component export { ReactComponent as ListViewIcon } from "./icon-insert-listView.svg"; -export { ReactComponent as GridCompIcon } from "./remix/grid-line.svg"; // Closest match for grid component +export { ReactComponent as GridCompIcon } from "icons/remix/grid-line.svg"; // Closest match for grid component export { ReactComponent as PackUpIcon } from "./icon-Pack-up.svg"; -export { ReactComponent as SearchIcon } from "./remix/search-line.svg"; // Closest match for search icon -export { ReactComponent as SearchOutlinedIcon } from "./remix/search-eye-line.svg"; // Closest match for search outlined icon -export { ReactComponent as FilterIcon } from "./remix/filter-line.svg"; // Closest match for filter icon -export { ReactComponent as DownloadIcon } from "./remix/download-line.svg"; // Closest match for download icon -export { ReactComponent as DownloadBoldIcon } from "./remix/download-2-line.svg"; // Closest match for download bold icon +export { ReactComponent as SearchIcon } from "icons/remix/search-line.svg"; // Closest match for search icon +export { ReactComponent as SearchOutlinedIcon } from "icons/remix/search-eye-line.svg"; // Closest match for search outlined icon +export { ReactComponent as FilterIcon } from "icons/remix/filter-line.svg"; // Closest match for filter icon +export { ReactComponent as DownloadIcon } from "icons/remix/download-line.svg"; // Closest match for download icon +export { ReactComponent as DownloadBoldIcon } from "icons/remix/download-2-line.svg"; // Closest match for download bold icon export { ReactComponent as DownloadedIcon } from "./icon-downloaded.svg"; -export { ReactComponent as SettingIcon } from "./remix/settings-line.svg"; // Closest match for setting icon -export { ReactComponent as RefreshIcon } from "./remix/refresh-line.svg"; // Closest match for refresh icon -export { ReactComponent as DeleteIcon } from "./remix/delete-bin-6-line.svg"; // Closest match for delete icon +export { ReactComponent as SettingIcon } from "icons/remix/settings-line.svg"; // Closest match for setting icon +export { ReactComponent as RefreshIcon } from "icons/remix/refresh-line.svg"; // Closest match for refresh icon +export { ReactComponent as DeleteIcon } from "icons/remix/delete-bin-6-line.svg"; // Closest match for delete icon export { ReactComponent as DeleteInputIcon } from "./icon-deleteinput.svg"; export { ReactComponent as UpgradeIcon } from "./icon-upgrade.svg"; -export { ReactComponent as QuestionIcon } from "./remix/question-line.svg"; // Closest match for question icon -export { ReactComponent as CloseIcon } from "./remix/close-line.svg"; // Closest match for close icon -export { ReactComponent as SuccessIcon } from "./remix/check-double-line.svg"; // Closest match for success icon -export { ReactComponent as ErrorIcon } from "./remix/error-warning-line.svg"; // Closest match for error icon -export { ReactComponent as DocIcon } from "./remix/file-text-line.svg"; // Closest match for document icon +export { ReactComponent as QuestionIcon } from "icons/remix/question-line.svg"; // Closest match for question icon +export { ReactComponent as CloseIcon } from "icons/remix/close-line.svg"; // Closest match for close icon +export { ReactComponent as SuccessIcon } from "icons/remix/check-double-line.svg"; // Closest match for success icon +export { ReactComponent as ErrorIcon } from "icons/remix/error-warning-line.svg"; // Closest match for error icon +export { ReactComponent as DocIcon } from "icons/remix/file-text-line.svg"; // Closest match for document icon export { ReactComponent as DocBoldIcon } from "./icon-tutorial-bold.svg"; -export { ReactComponent as QRCodeCompIcon } from "./remix/qr-code-line.svg"; // Closest match for QR code component +export { ReactComponent as QRCodeCompIcon } from "icons/remix/qr-code-line.svg"; // Closest match for QR code component export { ReactComponent as NavDocIcon } from "./icon-nav-doc.svg"; export { ReactComponent as LabIcon } from "./icon-laboratory.svg"; export { ReactComponent as JsonExplorerCompIcon } from "./icon-json-explorer.svg"; export { ReactComponent as JsonEditorCompIcon } from "./icon-json-editor.svg"; -export { ReactComponent as ArrowIcon } from "./remix/arrow-right-s-line.svg"; // Closest generic match for arrow icon -export { ReactComponent as ArrowSolidIcon } from "./remix/arrow-right-line.svg"; // Closest match for solid arrow icon -export { ReactComponent as AudioCompIcon } from "./remix/speaker-3-line.svg"; // Closest match for audio component -export { ReactComponent as VideoCompIcon } from "./remix/video-line.svg"; // Closest match for video component +export { ReactComponent as ArrowIcon } from "icons/remix/arrow-right-s-line.svg"; // Closest generic match for arrow icon +export { ReactComponent as ArrowSolidIcon } from "icons/remix/arrow-right-line.svg"; // Closest match for solid arrow icon +export { ReactComponent as AudioCompIcon } from "icons/remix/speaker-3-line.svg"; // Closest match for audio component +export { ReactComponent as VideoCompIcon } from "icons/remix/video-line.svg"; // Closest match for video component export { ReactComponent as videoPlayTriangle } from "./icon-video-play-triangle.svg"; -export { ReactComponent as DrawerCompIcon } from "./remix/sidebar-unfold-line.svg"; // Closest match for drawer component -export { ReactComponent as LeftMeetingIcon } from "./remix/team-line.svg"; // Closest match for left meeting icon -export { ReactComponent as PlusIcon } from "./remix/add-line.svg"; // Closest match for plus icon -export { ReactComponent as HomeIcon } from "./remix/home-2-line.svg"; // Closest match for home icon +export { ReactComponent as DrawerCompIcon } from "icons/remix/sidebar-unfold-line.svg"; // Closest match for drawer component +export { ReactComponent as LeftMeetingIcon } from "icons/remix/team-line.svg"; // Closest match for left meeting icon +export { ReactComponent as PlusIcon } from "icons/remix/add-line.svg"; // Closest match for plus icon +export { ReactComponent as HomeIcon } from "icons/remix/home-2-line.svg"; // Closest match for home icon export { ReactComponent as HomeModuleIcon } from "./icon-application-module.svg"; export { ReactComponent as HomeQueryLibraryIcon } from "./icon-application-query-library.svg"; export { ReactComponent as HomeDataSourceIcon } from "./icon-application-datasource.svg"; @@ -489,8 +491,8 @@ export { ReactComponent as HomeQueryLibraryActiveIcon } from "./icon-application export { ReactComponent as HomeDataSourceActiveIcon } from "./icon-application-datasource-active.svg"; export { ReactComponent as RecyclerActiveIcon } from "./icon-application-recycler-active.svg"; export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg"; -export { ReactComponent as HomeSettingIcon } from "./remix/settings-5-line.svg"; // Closest match for home setting icon -export { ReactComponent as FolderIcon } from "./remix/folder-4-line.svg"; // Closest match for folder icon +export { ReactComponent as HomeSettingIcon } from "icons/remix/settings-5-line.svg"; // Closest match for home setting icon +export { ReactComponent as FolderIcon } from "icons/remix/folder-4-line.svg"; // Closest match for folder icon export { ReactComponent as AllTypesIcon } from "./icon-application-all.svg"; export { ReactComponent as InviteUserIcon } from "./icon-application-invite-user.svg"; export { ReactComponent as HomeEmptyIcon } from "./icon-application-empty.svg"; @@ -518,15 +520,15 @@ export { ReactComponent as MSSQLIcon } from "./icon-query-mssql.svg"; export { ReactComponent as SMTPIcon } from "./icon-query-SMTP.svg"; export { ReactComponent as OracleIcon } from "./icon-query-OracleDB.svg"; export { ReactComponent as ClickHouseIcon } from "./icon-query-ClickHouse.svg"; -export { ReactComponent as ResetIcon } from "./remix/refresh-line.svg"; // Closest match for reset icon -export { ReactComponent as EditIcon } from "./remix/edit-2-line.svg"; // Closest match for edit icon +export { ReactComponent as ResetIcon } from "icons/remix/refresh-line.svg"; // Closest match for reset icon +export { ReactComponent as EditIcon } from "icons/remix/edit-2-line.svg"; // Closest match for edit icon export { ReactComponent as EditableIcon } from "./icon-editable.svg"; export { ReactComponent as LeftStateIcon } from "./icon-left-state.svg"; export { ReactComponent as LeftSettingIcon } from "./icon-left-setting.svg"; export { ReactComponent as LeftHelpIcon } from "./icon-left-help.svg"; export { ReactComponent as LeftPreloadIcon } from "./icon-left-preload.svg"; export { ReactComponent as CollapsibleContainerCompIcon } from "./icon-collapsible-container.svg"; -export { ReactComponent as ToggleButtonCompIcon } from "./remix/switch-line.svg"; // Closest match for toggle button component +export { ReactComponent as ToggleButtonCompIcon } from "icons/remix/switch-line.svg"; // Closest match for toggle button component export { ReactComponent as GoogleSheetsIcon } from "./icon-query-GoogleSheets.svg"; export { ReactComponent as GraphqlIcon } from "./icon-query-Graphql.svg"; export { ReactComponent as SnowflakeIcon } from "./icon-query-snowflake.svg"; @@ -538,71 +540,71 @@ export { ReactComponent as HomeSettingsActiveIcon } from "./icon-home-settings-a export { ReactComponent as HelpGithubIcon } from "./icon-help-github.svg"; export { ReactComponent as HelpDiscordIcon } from "./icon-help-discord.svg"; export { ReactComponent as LeftAudio } from "./icon-left-comp-audio.svg"; -export { ReactComponent as LeftButton } from "./remix/rectangle-line.svg"; // Closest match for left button -export { ReactComponent as LeftChart } from "./remix/bar-chart-2-line.svg"; // Closest match for left chart -export { ReactComponent as LeftCheckbox } from "./remix/checkbox-line.svg"; // Closest match for left checkbox +export { ReactComponent as LeftButton } from "icons/remix/rectangle-line.svg"; // Closest match for left button +export { ReactComponent as LeftChart } from "icons/remix/bar-chart-2-line.svg"; // Closest match for left chart +export { ReactComponent as LeftCheckbox } from "icons/remix/checkbox-line.svg"; // Closest match for left checkbox export { ReactComponent as LeftCommon } from "./icon-left-comp-common.svg"; export { ReactComponent as LeftContainer } from "./icon-left-comp-container.svg"; -export { ReactComponent as LeftDate } from "./remix/calendar-2-line.svg"; // Closest match for left date +export { ReactComponent as LeftDate } from "icons/remix/calendar-2-line.svg"; // Closest match for left date export { ReactComponent as LeftDivider } from "./icon-left-comp-divider.svg"; -export { ReactComponent as LeftDrawer } from "./remix/sidebar-unfold-line.svg"; // Closest match for drawer component -export { ReactComponent as LeftMeeting } from "./remix/team-line.svg"; // Closest match for left meeting icon -export { ReactComponent as LeftFile } from "./remix/file-line.svg"; // Closest match for left file +export { ReactComponent as LeftDrawer } from "icons/remix/sidebar-unfold-line.svg"; // Closest match for drawer component +export { ReactComponent as LeftMeeting } from "icons/remix/team-line.svg"; // Closest match for left meeting icon +export { ReactComponent as LeftFile } from "icons/remix/file-line.svg"; // Closest match for left file export { ReactComponent as LeftFileViewer } from "./icon-left-comp-fileViewer.svg"; -export { ReactComponent as LeftForm } from "./remix/draft-line.svg"; // Closest match for left form -export { ReactComponent as LeftIframe } from "./remix/window-2-line.svg"; // Closest match for left iframe -export { ReactComponent as LeftImage } from "./remix/image-line.svg"; // Closest match for left image -export { ReactComponent as LeftInput } from "./remix/input-method-line.svg"; // Closest match for left input +export { ReactComponent as LeftForm } from "icons/remix/draft-line.svg"; // Closest match for left form +export { ReactComponent as LeftIframe } from "icons/remix/window-2-line.svg"; // Closest match for left iframe +export { ReactComponent as LeftImage } from "icons/remix/image-line.svg"; // Closest match for left image +export { ReactComponent as LeftInput } from "icons/remix/input-method-line.svg"; // Closest match for left input export { ReactComponent as LeftJsonEditor } from "./icon-left-comp-jsonEditor.svg"; -export { ReactComponent as LeftLink } from "./remix/links-line.svg"; // Closest match for left link +export { ReactComponent as LeftLink } from "icons/remix/links-line.svg"; // Closest match for left link export { ReactComponent as LeftListView } from "./icon-left-comp-listView.svg"; -export { ReactComponent as LeftModal } from "./remix/shadow-line.svg"; // Closest match for left modal -export { ReactComponent as LeftNavigation } from "./remix/compass-line.svg"; // Closest match for left navigation +export { ReactComponent as LeftModal } from "icons/remix/shadow-line.svg"; // Closest match for left modal +export { ReactComponent as LeftNavigation } from "icons/remix/compass-line.svg"; // Closest match for left navigation export { ReactComponent as LeftNumberInput } from "./icon-insert-numberInput.svg"; -export { ReactComponent as LeftPassword } from "./remix/key-2-line.svg"; // Closest match for left password +export { ReactComponent as LeftPassword } from "icons/remix/key-2-line.svg"; // Closest match for left password export { ReactComponent as LeftProgress } from "./icon-progress.svg"; -export { ReactComponent as LeftQrCode } from "./remix/qr-code-line.svg"; // Closest match for left QR code -export { ReactComponent as LeftRadio } from "./remix/radio-line.svg"; // Closest match for left radio -export { ReactComponent as LeftRating } from "./remix/star-line.svg"; // Closest match for left rating +export { ReactComponent as LeftQrCode } from "icons/remix/qr-code-line.svg"; // Closest match for left QR code +export { ReactComponent as LeftRadio } from "icons/remix/radio-line.svg"; // Closest match for left radio +export { ReactComponent as LeftRating } from "icons/remix/star-line.svg"; // Closest match for left rating export { ReactComponent as LeftSegmentedControl } from "./icon-left-comp-segmentedControl.svg"; -export { ReactComponent as LeftSelect } from "./remix/dropdown-list.svg"; // Closest match for left select -export { ReactComponent as LeftSlider } from "./remix/git-commit-line.svg"; // Closest match for left slider -export { ReactComponent as LeftSwitch } from "./remix/switch-line.svg"; // Closest match for left switch -export { ReactComponent as LeftTable } from "./remix/table-line.svg"; // Closest match for left table -export { ReactComponent as LeftText } from "./remix/file-text-line.svg"; // Closest match for left text -export { ReactComponent as LeftTime } from "./remix/time-line.svg"; // Closest match for left time -export { ReactComponent as LeftTree } from "./remix/node-tree.svg"; // Closest match for left tree -export { ReactComponent as LeftVideo } from "./remix/video-line.svg"; // Closest match for left video -export { ReactComponent as LeftOpen } from "./remix/door-open-line.svg"; // Closest match for left open -export { ReactComponent as LeftClose } from "./remix/door-closed-line.svg"; // Closest match for left close -export { ReactComponent as ScannerIcon } from "./remix/scan-line.svg"; // Closest match for scanner icon +export { ReactComponent as LeftSelect } from "icons/remix/dropdown-list.svg"; // Closest match for left select +export { ReactComponent as LeftSlider } from "icons/remix/git-commit-line.svg"; // Closest match for left slider +export { ReactComponent as LeftSwitch } from "icons/remix/switch-line.svg"; // Closest match for left switch +export { ReactComponent as LeftTable } from "icons/remix/table-line.svg"; // Closest match for left table +export { ReactComponent as LeftText } from "icons/remix/file-text-line.svg"; // Closest match for left text +export { ReactComponent as LeftTime } from "icons/remix/time-line.svg"; // Closest match for left time +export { ReactComponent as LeftTree } from "icons/remix/node-tree.svg"; // Closest match for left tree +export { ReactComponent as LeftVideo } from "icons/remix/video-line.svg"; // Closest match for left video +export { ReactComponent as LeftOpen } from "icons/remix/door-open-line.svg"; // Closest match for left open +export { ReactComponent as LeftClose } from "icons/remix/door-closed-line.svg"; // Closest match for left close +export { ReactComponent as ScannerIcon } from "icons/remix/scan-line.svg"; // Closest match for scanner icon export { ReactComponent as MaterialUploadIcon } from "./icon-material-upload.svg"; -export { ReactComponent as CalendarCompIcon } from "./remix/calendar-2-line.svg"; // Closest match for calendar component +export { ReactComponent as CalendarCompIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for calendar component export { ReactComponent as LeftSignature } from "./icon-left-signature.svg"; -export { ReactComponent as UndoIcon } from "./remix/arrow-go-back-line.svg"; // Closest match for undo icon -export { ReactComponent as SignatureIcon } from "./remix/pen-nib-line.svg"; // Closest match for signature icon +export { ReactComponent as UndoIcon } from "icons/remix/arrow-go-back-line.svg"; // Closest match for undo icon +export { ReactComponent as SignatureIcon } from "icons/remix/pen-nib-line.svg"; // Closest match for signature icon export { ReactComponent as ManualIcon } from "./icon-manual.svg"; -export { ReactComponent as WarnIcon } from "./remix/alert-line.svg"; // Closest match for warn icon +export { ReactComponent as WarnIcon } from "icons/remix/alert-line.svg"; // Closest match for warn icon export { ReactComponent as SyncManualIcon } from "./icon-sync-manual.svg"; -export { ReactComponent as DangerIcon } from "./remix/error-warning-line.svg"; // Closest match for danger icon -export { ReactComponent as TableMinusIcon } from "./remix/checkbox-indeterminate-line.svg"; // Closest match for table minus icon -export { ReactComponent as TablePlusIcon } from "./remix/add-box-line.svg"; // Closest match for table plus icon -export { ReactComponent as MobileAppIcon } from "./remix/smartphone-line.svg"; // Closest match for mobile app icon -export { ReactComponent as MobileNavIcon } from "./remix/navigation-line.svg"; // Closest match for mobile navigation icon -export { ReactComponent as PcNavIcon } from "./remix/computer-line.svg"; // Closest match for PC navigation icon -export { ReactComponent as UnLockIcon } from "./remix/lock-unlock-line.svg"; // Closest match for unlock icon -export { ReactComponent as CalendarDeleteIcon } from "./remix/calendar-2-line.svg"; // Closest match for calendar delete icon -export { ReactComponent as TableCheckedIcon } from "./remix/checkbox-circle-line.svg"; // Closest match for table checked icon -export { ReactComponent as TableUnCheckedIcon } from "./remix/checkbox-blank-circle-line.svg"; // Closest match for table unchecked icon -export { ReactComponent as FileFolderIcon } from "./remix/folder-2-line.svg"; // Closest match for file folder icon -export { ReactComponent as ExpandIcon } from "./remix/menu-unfold-line.svg"; // Closest match for expand icon -export { ReactComponent as CompressIcon } from "./remix/menu-fold-line.svg"; // Closest match for compress icon -export { ReactComponent as TableCellsIcon } from "./remix/grid-line.svg"; // Closest match for table cells icon -export { ReactComponent as TimeLineIcon } from "./remix/time-line.svg"; // Closest match for timeline icon +export { ReactComponent as DangerIcon } from "icons/remix/error-warning-line.svg"; // Closest match for danger icon +export { ReactComponent as TableMinusIcon } from "icons/remix/checkbox-indeterminate-line.svg"; // Closest match for table minus icon +export { ReactComponent as TablePlusIcon } from "icons/remix/add-box-line.svg"; // Closest match for table plus icon +export { ReactComponent as MobileAppIcon } from "icons/remix/smartphone-line.svg"; // Closest match for mobile app icon +export { ReactComponent as MobileNavIcon } from "icons/remix/navigation-line.svg"; // Closest match for mobile navigation icon +export { ReactComponent as PcNavIcon } from "icons/remix/computer-line.svg"; // Closest match for PC navigation icon +export { ReactComponent as UnLockIcon } from "icons/remix/lock-unlock-line.svg"; // Closest match for unlock icon +export { ReactComponent as CalendarDeleteIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for calendar delete icon +export { ReactComponent as TableCheckedIcon } from "icons/remix/checkbox-circle-line.svg"; // Closest match for table checked icon +export { ReactComponent as TableUnCheckedIcon } from "icons/remix/checkbox-blank-circle-line.svg"; // Closest match for table unchecked icon +export { ReactComponent as FileFolderIcon } from "icons/remix/folder-2-line.svg"; // Closest match for file folder icon +export { ReactComponent as ExpandIcon } from "icons/remix/menu-unfold-line.svg"; // Closest match for expand icon +export { ReactComponent as CompressIcon } from "icons/remix/menu-fold-line.svg"; // Closest match for compress icon +export { ReactComponent as TableCellsIcon } from "icons/remix/grid-line.svg"; // Closest match for table cells icon +export { ReactComponent as TimeLineIcon } from "icons/remix/time-line.svg"; // Closest match for timeline icon export { ReactComponent as LottieIcon } from "./icon-lottie.svg"; export { ReactComponent as CommentIcon } from "./icon-comment-comp.svg"; export { ReactComponent as MentionIcon } from "./icon-mention-comp.svg"; export { ReactComponent as AutoCompleteCompIcon } from "./icon-autocomplete-comp.svg"; export { ReactComponent as WidthIcon } from "./icon-width.svg"; -export { ReactComponent as ResponsiveLayoutCompIcon } from "./remix/layout-column-line.svg"; // Closest match for responsive layout component +export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/remix/layout-column-line.svg"; // Closest match for responsive layout component export { ReactComponent as TextSizeIcon } from "./icon-text-size.svg"; */ \ No newline at end of file diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx index d3fec9367..ad1f8ae06 100644 --- a/client/packages/lowcoder/src/layout/gridItem.tsx +++ b/client/packages/lowcoder/src/layout/gridItem.tsx @@ -82,11 +82,18 @@ export type GridItemProps = { export const IsDroppable = React.createContext(true); -const ResizableStyled = styled(Resizable)<{ $zIndex: number }>` - z-index: ${props => props.$zIndex}; - &:hover { - z-index: 1; - } +/* const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boolean}>` + z-index: ${props => props.$zIndex * 10}; + ${props => props.isDroppable && ` + &:hover { + z-index: 1; + } + `} +`; */ + +// changed to remove &:hover { z-index: 1; as it lead into flickering +const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boolean}>` + z-index: ${props => props.$zIndex * 10}; `; /** @@ -183,6 +190,7 @@ export function GridItem(props: GridItemProps) { resizeHandles={resizeHandles} handle={Handle} $zIndex={zIndex} + isDroppable={draggingUtils.getData("i") !== props.i} > {child} diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index 66f32141c..faee2cea3 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -6,9 +6,9 @@ import { CollapseTitle as Title, CopyTextButton, FoldedIcon, - LeftClose, LeftCommon, - LeftOpen, + LeftInfoFill, + LeftInfoLine, PadDiv, ScrollBar, Tooltip, @@ -36,6 +36,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { useDispatch } from "react-redux"; import { useApplicationId } from "util/hooks"; import { updateApplication } from "redux/reduxActions/applicationActions"; +import { Divider } from "antd"; const CollapseTitleWrapper = styled.div` display: flex; @@ -195,6 +196,8 @@ const CollapseView = React.memo( interface LeftContentProps { uiComp: InstanceType; + checkable?: boolean; + isDraggable?: boolean; } enum LeftTabKey { @@ -250,6 +253,8 @@ export const LeftContent = (props: LeftContentProps) => { const [showData, setShowData] = useState([]); const dispatch = useDispatch(); const applicationId = useApplicationId(); + const checkable = props.checkable || false; + const isDraggable = props.isDraggable || false; const getTree = (tree: CompTree, result: NodeItem[], key?: string) => { const { items, children } = tree; @@ -362,7 +367,7 @@ export const LeftContent = (props: LeftContentProps) => { setShowData(newData); }} > - +
) : ( @@ -395,7 +400,7 @@ export const LeftContent = (props: LeftContentProps) => { setShowData(newData); }} > - + ))} @@ -438,7 +443,7 @@ export const LeftContent = (props: LeftContentProps) => { const explorerData: NodeItem[] = getTree(tree, []); // TODO: handle sorting inside modals/drawers if(type === TreeUIKey.Modals) return explorerData; - + const dsl = editorState.rootComp.toJsonValue(); explorerData.forEach(data => { data['pos'] = dsl.ui.layout[data.key].pos; @@ -452,85 +457,7 @@ export const LeftContent = (props: LeftContentProps) => { }); return explorerData; } - - interface DropInfo { - node: { key: string; pos: string }; - dragNode: { key: string; pos: string }; - } - - const handleDragEnter = (info: { node?: any; expandedKeys?: any; }) => { - // Assuming 'info' has a property 'expandedKeys' which is an array of keys - const { expandedKeys } = info; - if (!expandedKeys.includes(info.node.key)) { - setExpandedKeys(expandedKeys); - } - }; - - const handleDrop = (info: { node: { key: any; pos: string; }; dragNode: { key: any; pos: string; }; }, type: TreeUIKey) => { - const dropPos = info.node.pos.split('-'); - const dragPos = info.dragNode.pos.split('-'); - - if (dropPos.length === dragPos.length) { - setComponentTreeData(prevData => { - let newTreeData = cloneDeep(prevData); - const dropIndex = Number(dropPos[dropPos.length - 1]); - const dragIndex = Number(dragPos[dragPos.length - 1]); - const parentNodePos = dropPos.slice(0, -1).join('-'); - - // TODO: handle drag and drop for childen of root (container components for example) - // findNodeByPos does not work yet - const parentNode = parentNodePos === "0" ? { children: newTreeData } : findNodeByPos(newTreeData, parentNodePos); - - console.log('parentNode', parentNode); - - if (parentNode && parentNode.children) { - const draggedNodeIndex = parentNode.children.findIndex(node => node.key === info.dragNode.key); - if (draggedNodeIndex !== -1) { - const [draggedNode] = parentNode.children.splice(draggedNodeIndex, 1); - parentNode.children.splice(dropIndex > dragIndex ? dropIndex - 1 : dropIndex, 0, draggedNode); - } - } - - const dsl = editorState.rootComp.toJsonValue(); - let layout: any = {}; - parentNode.children.forEach((data, index) => { - layout[data.key] = { - ...dsl.ui.layout[data.key], - pos: index, - }; - }) - - if ( type === TreeUIKey.Modals) return newTreeData; - - dispatch( - updateApplication({ - applicationId: applicationId, - editingApplicationDSL: { - ...dsl, - ui: { - ...dsl.ui, - layout, - } - } as object, - }) - ); - editorState.rootComp.children.ui.dispatchChangeValueAction({ - ...dsl.ui, - layout, - }) - return newTreeData; - }); - } - }; - const findNodeByPos = (nodes: NodeItem[], pos: string): { children: NodeItem[] } => { - const posArr = pos.split('-').map(p => Number(p)); - let currentNode = { children: nodes }; - for (let i = 0; i < posArr.length; i++) { - currentNode = currentNode.children[posArr[i]]; - } - return currentNode; - }; const getTreeUI = (type: TreeUIKey) => { // here the components get sorted by name @@ -559,21 +486,22 @@ export const LeftContent = (props: LeftContentProps) => { } return ( - handleDrop(info, type)} + <> props.type && (CompStateIcon[props.type as UICompType] || )} - switcherIcon={(props: any) => - props.expanded ? : - } + icon={(props: any) => props.type && ( +
{/* Adjust the margin as needed */} + {CompStateIcon[props.type as UICompType] || } +
+ )} + switcherIcon={(props: any) => props.expanded ? : } expandedKeys={expandedKeys} onExpand={(keys) => setExpandedKeys(keys)} onClick={(e, node) => handleNodeClick(e, node, uiCompInfos)} selectedKeys={selectedKeys} - titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} - /> + titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} /> + +
+ ); }; @@ -623,6 +551,7 @@ export const LeftContent = (props: LeftContentProps) => { }, [editorState]); const moduleLayoutComp = uiComp.getModuleLayoutComp(); + const stateContent = (
diff --git a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx new file mode 100644 index 000000000..bba1dfbd3 --- /dev/null +++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx @@ -0,0 +1,325 @@ +import { CompInfo, EditorContext } from "comps/editorState"; +import { + BaseSection, + Collapse, + CollapseLabel as Label, + CollapseTitle as Title, + CopyTextButton, + FoldedIcon, + LeftCommon, + LeftInfoFill, + LeftInfoLine, + PadDiv, + ScrollBar, + Tooltip, + UnfoldIcon, + UnShow, +} from "lowcoder-design"; +import React, { ReactNode, useCallback, useContext, useMemo, useState, useEffect } from "react"; +import { hookCompCategory } from "comps/hooks/hookCompTypes"; +import _ from "lodash"; +import styled from "styled-components"; +import { leftCompListClassName } from "pages/tutorials/tutorialsConstant"; +import UIComp from "comps/comps/uiComp"; +import { BottomResTypeEnum } from "types/bottomRes"; +import { getParentNodeKeysByKey, getTreeNodeByKey, safeJSONStringify } from "util/objectUtils"; +import { Tabs, TabTitle } from "components/Tabs"; +import { BackgroundColor, TopHeaderHeight } from "constants/style"; +import { trans } from "i18n"; +import { CompTree } from "comps/comps/containerBase"; +import { CompStateIcon } from "./editorConstants"; +import { UICompType } from "comps/uiCompRegistry"; +import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; +import { DataNode, EventDataNode } from "antd/lib/tree"; +import { isAggregationApp } from "util/appUtils"; +import cloneDeep from 'lodash/cloneDeep'; +import { useDispatch } from "react-redux"; +import { useApplicationId } from "util/hooks"; +import { updateApplication } from "redux/reduxActions/applicationActions"; +import { Divider } from "antd"; +import { Switch } from "antd"; +import { + saveCollisionStatus, + getCollisionStatus, +} from "util/localStorageUtil"; + + +export type DisabledCollisionStatus = "true" | "false"; // "true" means collision is not enabled - Layering works, "false" means collision is enabled - Layering does not work +export type ToggleCollisionStatus = ( + collisionStatus?: DisabledCollisionStatus + ) => void; + +interface LeftLayersContentProps { + uiComp: InstanceType; +} + +type NodeItem = { + key: string; + title: string; + type?: UICompType; + children: NodeItem[]; + pos?: number; + disabled?: boolean; + fixed?: boolean; +}; + +const LeftLayersContentWrapper = styled.div` + height: calc(100vh - ${TopHeaderHeight}); +`; + +export const LeftLayersContent = (props: LeftLayersContentProps) => { + const { uiComp } = props; + const editorState = useContext(EditorContext); + const [expandedKeys, setExpandedKeys] = useState>([]); + const dispatch = useDispatch(); + const applicationId = useApplicationId(); + + // added by Falk Wolsky to support a Layers in Lowcoder + const [collisionStatus, setCollisionStatus] = useState(() => { + return getCollisionStatus(); + }); + + const toggleCollisionStatus: ToggleCollisionStatus = useCallback( + (value) => { + setCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); + saveCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); + }, + [collisionStatus] + ); + + const getTree = (tree: CompTree, result: NodeItem[], key?: string) => { + const { items, children } = tree; + if (Object.keys(items).length) { + for (const i in items) { + const info: NodeItem = { + title: items[i].children.name.getView(), + type: items[i].children.compType.getView() as UICompType, + key: i, + children: [], + }; + if (key) { + const parent = getTreeNodeByKey(result, key); + info.disabled = true; + parent?.children.push(info); + } else { + result.push(info); + } + } + // result = _.sortBy(result, [(x) => x.title]); + } + if (Object.keys(children).length) { + for (const i in children) { + getTree(children[i], result, i); + } + } + return result; + }; + + const uiCollapseClick = useCallback( + (compName: string) => { + editorState.setSelectedCompNames(new Set([compName]), "leftPanel"); + }, + [editorState] + ); + + const getTreeNode = (node: NodeItem, uiCompInfos: CompInfo[]) => { + const data = uiCompInfos.find((item) => item.name === node.title); + return ( + + + {node.title} + + + + ); + }; + + const [componentTreeData, setComponentTreeData] = useState([]); + + useEffect(() => { + const compData = getTreeUIData(); + setComponentTreeData(compData); + }, [editorState]); + + const getTreeUIData = () => { + const tree = editorState.getUIComp().getTree(); + const explorerData: NodeItem[] = getTree(tree, []); + const dsl = editorState.rootComp.toJsonValue(); + explorerData.forEach(data => { + data['pos'] = dsl.ui.layout[data.key].pos; + }) + explorerData.sort((a, b) => { + const aPos = a?.pos || 0; + const bPos = b?.pos || 0; + if (aPos < bPos) return -1; + if (aPos > bPos) return 1; + return 0; + }); + return explorerData; + } + + interface DropInfo { + node: { key: string; pos: string }; + dragNode: { key: string; pos: string }; + } + + const handleDragEnter = (info: { node?: any; expandedKeys?: any; }) => { + // Assuming 'info' has a property 'expandedKeys' which is an array of keys + const { expandedKeys } = info; + if (!expandedKeys.includes(info.node.key)) { + setExpandedKeys(expandedKeys); + } + }; + + const handleDrop = (info: { node: { key: any; pos: string; }; dragNode: { key: any; pos: string; }; }) => { + const dropPos = info.node.pos.split('-'); + const dragPos = info.dragNode.pos.split('-'); + + if (dropPos.length === dragPos.length) { + setComponentTreeData(prevData => { + let newTreeData = cloneDeep(prevData); + const dropIndex = Number(dropPos[dropPos.length - 1]); + const dragIndex = Number(dragPos[dragPos.length - 1]); + const parentNodePos = dropPos.slice(0, -1).join('-'); + + // TODO: handle drag and drop for childen of root (container components for example) + // findNodeByPos does not work yet + const parentNode = parentNodePos === "0" ? { children: newTreeData } : findNodeByPos(newTreeData, parentNodePos); + + if (parentNode && parentNode.children) { + const draggedNodeIndex = parentNode.children.findIndex(node => node.key === info.dragNode.key); + if (draggedNodeIndex !== -1) { + const [draggedNode] = parentNode.children.splice(draggedNodeIndex, 1); + parentNode.children.splice(dropIndex > dragIndex ? dropIndex - 1 : dropIndex, 0, draggedNode); + } + } + + const dsl = editorState.rootComp.toJsonValue(); + let layout: any = {}; + parentNode.children.forEach((data, index) => { + layout[data.key] = { + ...dsl.ui.layout[data.key], + pos: index, + }; + }) + + dispatch( + updateApplication({ + applicationId: applicationId, + editingApplicationDSL: { + ...dsl, + ui: { + ...dsl.ui, + layout, + } + } as object, + }) + ); + editorState.rootComp.children.ui.dispatchChangeValueAction({ + ...dsl.ui, + layout, + }) + return newTreeData; + }); + } + }; + + const findNodeByPos = (nodes: NodeItem[], pos: string): { children: NodeItem[] } => { + const posArr = pos.split('-').map(p => Number(p)); + let currentNode = { children: nodes }; + for (let i = 0; i < posArr.length; i++) { + currentNode = currentNode.children[posArr[i]]; + } + return currentNode; + }; + + const [checkedKeys, setCheckedKeys] = useState([]); + + const onCheck = (checkedKeys: any, e: any) => { + setCheckedKeys(checkedKeys); + console.log('onCheck', checkedKeys); + } + + const getTreeUI = () => { + // here the components get sorted by name + // TODO: sort by category + // TODO: sort by Types etc. + const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]); + + /* const tree = + type === TreeUIKey.Components + ? editorState.getUIComp().getTree() + : editorState.getHooksComp().getUITree(); + const explorerData: NodeItem[] = getTree(tree, []); */ + + let selectedKeys = []; + if (editorState.selectedCompNames.size === 1) { + const key = Object.keys(editorState.selectedComps())[0]; + const parentKeys = getParentNodeKeysByKey(componentTreeData, key); + if (parentKeys && parentKeys.length) { + let needSet = false; + parentKeys.forEach((key) => { + if (!expandedKeys.includes(key)) { + needSet = true; + } + }); + needSet && setExpandedKeys(_.union(expandedKeys, parentKeys)); + } + selectedKeys.push(key); + } + + const isDraggable = editorState.collisionStatus === "true" ? true : false; + + return ( + <> + { + toggleCollisionStatus(value == true ? "true" : "false"); + editorState.setCollisionStatus(value == true ? "true" : "false"); + } } /> + handleDrop(info)} + treeData={componentTreeData} + icon={(props: any) => props.type && ( +
{/* Adjust the margin as needed */} + {CompStateIcon[props.type as UICompType] || } +
+ )} + switcherIcon={(props: any) => props.expanded ? : } + expandedKeys={expandedKeys} + onExpand={(keys) => setExpandedKeys(keys)} + // onClick={(e, node) => handleNodeClick(e, node, uiCompInfos)} + selectedKeys={selectedKeys} + titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} /> + +
+ + ); + }; + + const uiCollapse = useMemo(() => { + if (isAggregationApp(editorState.getAppType())) { + return; + } + return getTreeUI(); + }, [editorState, uiCollapseClick, expandedKeys, componentTreeData]); + + const layerControlContent = ( + +
+ + {uiCollapse} + +
+
+ ); + + return {layerControlContent}; + +}; \ No newline at end of file diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index 8cd7390fe..45a512214 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -66,13 +66,12 @@ import { DefaultEditorModeStatus, getEditorModeStatus, saveEditorModeStatus, - saveCollisionStatus, - getCollisionStatus, } from "util/localStorageUtil"; import Bottom from "./bottom/BottomPanel"; import { LeftContent } from "./LeftContent"; +import { LeftLayersContent } from "./LeftLayersContent"; import { isAggregationApp } from "util/appUtils"; -import { Switch } from "antd"; + const HookCompContainer = styled.div` pointer-events: none; @@ -225,11 +224,6 @@ const items = [ }, ]; - // added by Fred to set comp collision state -export type DisabledCollisionStatus = "true" | "false"; // "true" means collision is not enabled - Layering works, "false" means collision is enabled - Layering does not work -export type ToggleCollisionStatus = ( - collisionStatus?: DisabledCollisionStatus - ) => void; function EditorView(props: EditorViewProps) { const { uiComp } = props; @@ -275,20 +269,6 @@ function EditorView(props: EditorViewProps) { [panelStatus, prePanelStatus] ); - // added by Falk Wolsky to support a Layers in Lowcoder - const [collisionStatus, setCollisionStatus] = useState(() => { - return getCollisionStatus(); - }); - - const toggleCollisionStatus: ToggleCollisionStatus = useCallback( - (value) => { - setCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); - saveCollisionStatus(value ? value : ("false" as DisabledCollisionStatus)); - }, - [collisionStatus] - ); - - // added by Falk Wolsky to support a Layout and Logic Mode in Lowcoder const [editorModeStatus, setEditorModeStatus] = useState(() => { return getEditorModeStatus(); @@ -386,6 +366,7 @@ function EditorView(props: EditorViewProps) { setMenuKey(params.key); }; const appSettingsComp = editorState.getAppSettingsComp(); + return ( { @@ -437,7 +418,7 @@ function EditorView(props: EditorViewProps) { {panelStatus.left && editorModeStatus !== "layout" && ( - {menuKey === SiderKey.State && } + {menuKey === SiderKey.State && } {menuKey === SiderKey.Setting && ( @@ -470,20 +451,8 @@ function EditorView(props: EditorViewProps) { )} - - {menuKey === SiderKey.Layout && ( - - { - toggleCollisionStatus(value == true ? "true" : "false"); - editorState.setCollisionStatus(value == true ? "true" : "false"); - }} - /> - - + )} diff --git a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx index 6df7b9d7a..2188b4ea1 100644 --- a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx +++ b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx @@ -31,6 +31,9 @@ export const DirectoryTreeStyle = styled(DirectoryTree)` align-items: center; } } + .ant-tree-checkbox+span { + padding-left: 0; + } .ant-tree-treenode { padding: 0; max-width: 288px; @@ -110,14 +113,17 @@ export const Node = styled.span` } `; +// margin: 4px -16px 4px ${(props) => props.$clientX && `calc(-${props.$clientX}px + 16px)`}; +// width: 256px; export const CollapseWrapper = styled.div<{ $clientX?: number }>` - width: 256px; + width: 100%; border: 1px solid #E1E3EB; border-radius: 4px; overflow: hidden; background: #fff; - padding: 4px 0; - margin: 4px -16px 4px ${(props) => props.$clientX && `calc(-${props.$clientX}px + 16px)`}; + padding: 0px; + position: relative; + margin: 4px 0px 4px 0}; .simplebar-content > div { > .ant-collapse > .ant-collapse-item { > .ant-collapse-header { diff --git a/client/packages/lowcoder/src/util/localStorageUtil.ts b/client/packages/lowcoder/src/util/localStorageUtil.ts index 2f102a696..1c468be88 100644 --- a/client/packages/lowcoder/src/util/localStorageUtil.ts +++ b/client/packages/lowcoder/src/util/localStorageUtil.ts @@ -1,5 +1,5 @@ import { PanelStatus } from "pages/common/header"; -import { DisabledCollisionStatus as DisabledCollisionStatus } from "pages/editor/editorView"; +import { DisabledCollisionStatus as DisabledCollisionStatus } from "pages/editor/LeftLayersContent"; import { EditorModeStatus } from "pages/common/header"; import log from "loglevel"; import { JSONValue } from "util/jsonTypes"; From 9ac931aec3b0adfabe9f85a54c84057de4d011e9 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Sun, 4 Feb 2024 17:45:08 +0100 Subject: [PATCH 6/9] [WIP] Own Display for Layers --- .../lowcoder-design/src/icons/index.ts | 365 +++++++++--------- .../packages/lowcoder/src/i18n/locales/en.ts | 7 +- .../packages/lowcoder/src/i18n/locales/zh.ts | 5 +- .../lowcoder/src/pages/editor/LeftContent.tsx | 111 ++---- .../src/pages/editor/LeftLayersContent.tsx | 249 ++++++++---- .../lowcoder/src/pages/editor/editorView.tsx | 21 +- 6 files changed, 420 insertions(+), 338 deletions(-) diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index 8b80e30bf..938cd1fc4 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -214,8 +214,9 @@ export { ReactComponent as ClickHouseIcon } from "./icon-query-ClickHouse.svg"; export { ReactComponent as ResetIcon } from "./icon-style-reset.svg"; export { ReactComponent as EditIcon } from "./icon-edit.svg"; export { ReactComponent as EditableIcon } from "./icon-editable.svg"; -export { ReactComponent as LeftStateIcon } from "./icon-left-state.svg"; -export { ReactComponent as LeftSettingIcon } from "./icon-left-setting.svg"; +export { ReactComponent as LeftStateIcon } from "./remix/node-tree.svg"; +export { ReactComponent as LeftSettingIcon } from "./remix/tools-fill.svg"; +export { ReactComponent as LeftLayersIcon } from "./remix/stack-line.svg"; export { ReactComponent as LeftHelpIcon } from "./icon-left-help.svg"; export { ReactComponent as LeftPreloadIcon } from "./icon-left-preload.svg"; export { ReactComponent as CollapsibleContainerCompIcon } from "./icon-collapsible-container.svg"; @@ -277,127 +278,131 @@ export { ReactComponent as SignatureIcon } from "./icon-signature.svg"; export { ReactComponent as ManualIcon } from "./icon-manual.svg"; export { ReactComponent as WarnIcon } from "./icon-warn.svg"; export { ReactComponent as SyncManualIcon } from "./icon-sync-manual.svg"; -export { ReactComponent as DangerIcon } from "icons/icon-danger.svg"; -export { ReactComponent as TableMinusIcon } from "icons/icon-table-minus.svg"; -export { ReactComponent as TablePlusIcon } from "icons/icon-table-plus.svg"; -export { ReactComponent as MobileAppIcon } from "icons/icon-mobile-app.svg"; -export { ReactComponent as MobileNavIcon } from "icons/icon-navigation-mobile.svg"; -export { ReactComponent as PcNavIcon } from "icons/icon-navigation-pc.svg"; -export { ReactComponent as UnLockIcon } from "icons/icon-unlock.svg"; -export { ReactComponent as CalendarDeleteIcon } from "icons/icon-calendar-delete.svg"; -export { ReactComponent as TableCheckedIcon } from "icons/icon-table-checked.svg"; -export { ReactComponent as TableUnCheckedIcon } from "icons/icon-table-boolean-false.svg"; -export { ReactComponent as FileFolderIcon } from "icons/icon-editor-folder.svg"; -export { ReactComponent as ExpandIcon } from "icons/icon-expand.svg"; -export { ReactComponent as CompressIcon } from "icons/icon-compress.svg"; -export { ReactComponent as TableCellsIcon } from "icons/icon-table-cells.svg"; // Added By Aqib Mirza -export { ReactComponent as TimeLineIcon } from "icons/icon-timeline-comp.svg" -export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg"; -export { ReactComponent as CommentIcon } from "icons/icon-comment-comp.svg"; -export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg"; -export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg"; -export { ReactComponent as WidthIcon } from "icons/icon-width.svg"; -export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/icon-responsive-layout-comp.svg"; -export { ReactComponent as TextSizeIcon } from "icons/remix/font-size-2.svg"; -export { ReactComponent as FontFamilyIcon } from "icons/remix/font-sans-serif.svg"; -export { ReactComponent as TextWeigthIcon } from "icons/remix/bold.svg"; -export { ReactComponent as BorderWidthIcon } from "icons/remix/expand-width-line.svg"; -export { ReactComponent as LeftInfoLine } from "icons/remix/information-line.svg"; -export { ReactComponent as LeftInfoFill } from "icons/remix/information-fill.svg"; +export { ReactComponent as DangerIcon } from "./icon-danger.svg"; +export { ReactComponent as TableMinusIcon } from "./icon-table-minus.svg"; +export { ReactComponent as TablePlusIcon } from "./icon-table-plus.svg"; +export { ReactComponent as MobileAppIcon } from "./icon-mobile-app.svg"; +export { ReactComponent as MobileNavIcon } from "./icon-navigation-mobile.svg"; +export { ReactComponent as PcNavIcon } from "./icon-navigation-pc.svg"; +export { ReactComponent as UnLockIcon } from "./icon-unlock.svg"; +export { ReactComponent as CalendarDeleteIcon } from "./icon-calendar-delete.svg"; +export { ReactComponent as TableCheckedIcon } from "./icon-table-checked.svg"; +export { ReactComponent as TableUnCheckedIcon } from "./icon-table-boolean-false.svg"; +export { ReactComponent as FileFolderIcon } from "./icon-editor-folder.svg"; +export { ReactComponent as ExpandIcon } from "./icon-expand.svg"; +export { ReactComponent as CompressIcon } from "./icon-compress.svg"; +export { ReactComponent as TableCellsIcon } from "./icon-table-cells.svg"; // Added By Aqib Mirza +export { ReactComponent as TimeLineIcon } from "./icon-timeline-comp.svg" +export { ReactComponent as LottieIcon } from "./icon-lottie.svg"; +export { ReactComponent as CommentIcon } from "./icon-comment-comp.svg"; +export { ReactComponent as MentionIcon } from "./icon-mention-comp.svg"; +export { ReactComponent as AutoCompleteCompIcon } from "./icon-autocomplete-comp.svg"; +export { ReactComponent as WidthIcon } from "./icon-width.svg"; +export { ReactComponent as ResponsiveLayoutCompIcon } from "./icon-responsive-layout-comp.svg"; +export { ReactComponent as TextSizeIcon } from "./remix/font-size-2.svg"; +export { ReactComponent as FontFamilyIcon } from "./remix/font-sans-serif.svg"; +export { ReactComponent as TextWeigthIcon } from "./remix/bold.svg"; +export { ReactComponent as BorderWidthIcon } from "./remix/expand-width-line.svg"; +export { ReactComponent as LeftInfoLine } from "./remix/information-line.svg"; +export { ReactComponent as LeftInfoFill } from "./remix/information-fill.svg"; +export { ReactComponent as LeftShow } from "./remix/eye-off-line.svg"; +export { ReactComponent as LeftHide } from "./remix/eye-line.svg"; +export { ReactComponent as LeftLock } from "./remix/lock-line.svg"; +export { ReactComponent as LeftUnlock } from "./remix/lock-unlock-line.svg"; // new -/* export { ReactComponent as AppSnapshotIcon } from "icons/remix/screenshot-2-line.svg"; // Closest match for app snapshot +/* export { ReactComponent as AppSnapshotIcon } from "./remix/screenshot-2-line.svg"; // Closest match for app snapshot export { ReactComponent as HookCompDropIcon } from "./hook-comp-drop.svg"; export { ReactComponent as HookCompIcon } from "./hook-comp.svg"; -export { ReactComponent as IconsIcon } from "icons/remix/apps-line.svg"; // Closest match for icons +export { ReactComponent as IconsIcon } from "./remix/apps-line.svg"; // Closest match for icons export { ReactComponent as JsGrayIcon } from "./icon-Js-Gray.svg"; export { ReactComponent as JsColorsIcon } from "./icon-Js-colors.svg"; -export { ReactComponent as AdminIcon } from "icons/remix/admin-line.svg"; // Exact match +export { ReactComponent as AdminIcon } from "./remix/admin-line.svg"; // Exact match export { ReactComponent as AlignVerticalCent } from "./icon-align-vertical-center.svg"; -export { ReactComponent as AppEditIcon } from "icons/remix/edit-box-line.svg"; // Closest match for app edit +export { ReactComponent as AppEditIcon } from "./remix/edit-box-line.svg"; // Closest match for app edit export { ReactComponent as AuditAppIcon } from "./icon-audit-app.svg"; export { ReactComponent as AuditDbIcon } from "./icon-audit-db.svg"; -export { ReactComponent as TreeFoldIcon } from "icons/remix/tree-line.svg"; // Closest match for tree fold +export { ReactComponent as TreeFoldIcon } from "./remix/tree-line.svg"; // Closest match for tree fold export { ReactComponent as AuditQueryIcon } from "./icon-audit-query.svg"; export { ReactComponent as AuditUserIcon } from "./icon-audit-user.svg"; -export { ReactComponent as AuditFolderIcon } from "icons/remix/folder-line.svg"; // Closest match for audit folder +export { ReactComponent as AuditFolderIcon } from "./remix/folder-line.svg"; // Closest match for audit folder export { ReactComponent as AutoCompleteIcon } from "./icon-autoComplete.svg"; -export { ReactComponent as CalendarIcon } from "icons/remix/calendar-2-line.svg"; // Exact match +export { ReactComponent as CalendarIcon } from "./remix/calendar-2-line.svg"; // Exact match export { ReactComponent as CaptchaIcon } from "./icon-captcha.svg"; -export { ReactComponent as CheckboxIcon } from "icons/remix/checkbox-line.svg"; // Exact match +export { ReactComponent as CheckboxIcon } from "./remix/checkbox-line.svg"; // Exact match export { ReactComponent as CheckoutIcon } from "./icon-checkout.svg"; export { ReactComponent as ClickLinkIcon } from "./icon-clickLink.svg"; -export { ReactComponent as CloseEyeIcon } from "icons/remix/eye-off-line.svg"; // Closest match for close eye +export { ReactComponent as CloseEyeIcon } from "./remix/eye-off-line.svg"; // Closest match for close eye export { ReactComponent as CodeEditorCloseIcon } from "./icon-code-editor-close.svg"; export { ReactComponent as CodeEditorOpenIcon } from "./icon-code-editor-open.svg"; -export { ReactComponent as ColorHexIcon } from "icons/remix/palette-line.svg"; // Closest match for color hex +export { ReactComponent as ColorHexIcon } from "./remix/palette-line.svg"; // Closest match for color hex export { ReactComponent as ContainerDragIcon } from "./icon-container-drag.svg"; -export { ReactComponent as CopyIcon } from "icons/remix/file-copy-line.svg"; // Closest match for copy +export { ReactComponent as CopyIcon } from "./remix/file-copy-line.svg"; // Closest match for copy export { ReactComponent as CreateModuleIcon } from "./icon-create-module.svg"; export { ReactComponent as PrevIcon } from "./icon-date-prev.svg"; export { ReactComponent as SuperPrevIcon } from "./icon-date-super-prev.svg"; export { ReactComponent as DragWhiteIcon } from "./icon-drag-white.svg"; -export { ReactComponent as EmptyDataIcon } from "icons/remix/emotion-unhappy-line.svg"; // Closest match for empty data +export { ReactComponent as EmptyDataIcon } from "./remix/emotion-unhappy-line.svg"; // Closest match for empty data export { ReactComponent as FlokcloseIcon } from "./icon-flokclose.svg"; export { ReactComponent as FoldedIcon } from "./icon-folded.svg"; -export { ReactComponent as GridIcon } from "icons/remix/grid-line.svg"; // Exact match -export { ReactComponent as GroupIcon } from "icons/remix/group-line.svg"; // Closest match for group -export { ReactComponent as HelpIcon } from "icons/remix/question-answer-line.svg"; // Closest match for help -export { ReactComponent as LayoutIcon } from "icons/remix/layout-6-line.svg"; // Closest match for layout -export { ReactComponent as LockIcon } from "icons/remix/lock-line.svg"; // Exact match -export { ReactComponent as MembersIcon } from "icons/remix/group-line.svg"; // Closest match for members -export { ReactComponent as MoreActionIcon } from "icons/remix/more-2-line.svg"; // Closest match for more action -export { ReactComponent as MultiselectTagIcon } from "icons/remix/price-tag-3-line.svg"; // Closest match for multiselect tag -export { ReactComponent as MustFillStarIcon } from "icons/remix/star-line.svg"; // Closest match for must fill star -export { ReactComponent as NofileIcon } from "icons/remix/file-unknow-line.svg"; // Closest match for nofile +export { ReactComponent as GridIcon } from "./remix/grid-line.svg"; // Exact match +export { ReactComponent as GroupIcon } from "./remix/group-line.svg"; // Closest match for group +export { ReactComponent as HelpIcon } from "./remix/question-answer-line.svg"; // Closest match for help +export { ReactComponent as LayoutIcon } from "./remix/layout-6-line.svg"; // Closest match for layout +export { ReactComponent as LockIcon } from "./remix/lock-line.svg"; // Exact match +export { ReactComponent as MembersIcon } from "./remix/group-line.svg"; // Closest match for members +export { ReactComponent as MoreActionIcon } from "./remix/more-2-line.svg"; // Closest match for more action +export { ReactComponent as MultiselectTagIcon } from "./remix/price-tag-3-line.svg"; // Closest match for multiselect tag +export { ReactComponent as MustFillStarIcon } from "./remix/star-line.svg"; // Closest match for must fill star +export { ReactComponent as NofileIcon } from "./remix/file-unknow-line.svg"; // Closest match for nofile export { ReactComponent as OmitIcon } from "./icon-omit.svg"; -export { ReactComponent as OpenEyeIcon } from "icons/remix/eye-line.svg"; // Closest match for open eye -export { ReactComponent as PasswordIcon } from "icons/remix/lock-password-line.svg"; // Closest match for password -export { ReactComponent as RadioCheckedIcon } from "icons/remix/list-radio.svg"; // Closest match for radio checked -export { ReactComponent as RequiredIcon } from "icons/remix/alert-line.svg"; // Closest match for required +export { ReactComponent as OpenEyeIcon } from "./remix/eye-line.svg"; // Closest match for open eye +export { ReactComponent as PasswordIcon } from "./remix/lock-password-line.svg"; // Closest match for password +export { ReactComponent as RadioCheckedIcon } from "./remix/list-radio.svg"; // Closest match for radio checked +export { ReactComponent as RequiredIcon } from "./remix/alert-line.svg"; // Closest match for required export { ReactComponent as AttributeIcon } from "./icon-right-attribute.svg"; export { ReactComponent as InsertIcon } from "./icon-right-insert.svg"; export { ReactComponent as ShowBorderIcon } from "./icon-show-border.svg"; export { ReactComponent as SpaceIcon } from "./icon-space.svg"; -export { ReactComponent as StarIcon } from "icons/remix/star-line.svg"; // Exact match +export { ReactComponent as StarIcon } from "./remix/star-line.svg"; // Exact match export { ReactComponent as SuperUserIcon } from "./icon-super-user.svg"; -export { ReactComponent as SwitchCheckedIcon } from "icons/remix/toggle-line.svg"; // Closest match for switch checked +export { ReactComponent as SwitchCheckedIcon } from "./remix/toggle-line.svg"; // Closest match for switch checked export { ReactComponent as TextEditIcon } from "./icon-text-edit.svg"; export { ReactComponent as TextboxIcon } from "./icon-textbox.svg"; -export { ReactComponent as TriangleIcon } from "icons/remix/arrow-up-line.svg"; // Closest match for triangle -export { ReactComponent as TypographyIcon } from "icons/remix/file-text-line.svg"; // Closest match for typography +export { ReactComponent as TriangleIcon } from "./remix/arrow-up-line.svg"; // Closest match for triangle +export { ReactComponent as TypographyIcon } from "./remix/file-text-line.svg"; // Closest match for typography export { ReactComponent as UnfoldWhiteIcon } from "./icon-unfold-white.svg"; export { ReactComponent as UnfoldIcon } from "./icon-unfold.svg"; export { ReactComponent as WarningWhiteIcon } from "./icon-warning-white.svg"; -export { ReactComponent as WarningIcon } from "icons/remix/error-warning-line.svg"; // Closest match for warning +export { ReactComponent as WarningIcon } from "./remix/error-warning-line.svg"; // Closest match for warning export { ReactComponent as WidthDragIcon } from "./icon-widthDrag.svg"; -export { ReactComponent as ManyCheckboxIcon } from "icons/remix/checkbox-multiple-line.svg"; // Closest match for many checkbox -export { ReactComponent as Layout } from "icons/remix/layout-2-line.svg"; // Closest match for layout -export { ReactComponent as Left } from "icons/remix/arrow-left-s-line.svg"; // Closest match for left -export { ReactComponent as Middle } from "icons/remix/align-vertically.svg"; // Closest match for middle -export { ReactComponent as Right } from "icons/remix/arrow-right-s-line.svg"; // Closest match for right +export { ReactComponent as ManyCheckboxIcon } from "./remix/checkbox-multiple-line.svg"; // Closest match for many checkbox +export { ReactComponent as Layout } from "./remix/layout-2-line.svg"; // Closest match for layout +export { ReactComponent as Left } from "./remix/arrow-left-s-line.svg"; // Closest match for left +export { ReactComponent as Middle } from "./remix/align-vertically.svg"; // Closest match for middle +export { ReactComponent as Right } from "./remix/arrow-right-s-line.svg"; // Closest match for right export { ReactComponent as DeployIcon } from "./icon-rocket.svg"; -export { ReactComponent as ExportIcon } from "icons/remix/share-forward-line.svg"; // Closest match for export +export { ReactComponent as ExportIcon } from "./remix/share-forward-line.svg"; // Closest match for export export { ReactComponent as BluePlusIcon } from "./icon-blue-add.svg"; -export { ReactComponent as PencilIcon } from "icons/remix/pencil-line.svg"; // Closest match for pencil -export { ReactComponent as DragIcon } from "icons/remix/drag-move-2-line.svg"; // Closest match for drag +export { ReactComponent as PencilIcon } from "./remix/pencil-line.svg"; // Closest match for pencil +export { ReactComponent as DragIcon } from "./remix/drag-move-2-line.svg"; // Closest match for drag export { ReactComponent as PointIcon } from "./icon-three-point.svg"; -export { ReactComponent as AlignJustify } from "icons/remix/align-justify.svg"; // Exact match -export { ReactComponent as AlignRight } from "icons/remix/align-right.svg"; // Exact match -export { ReactComponent as AlignCenter } from "icons/remix/align-center.svg"; // Exact match -export { ReactComponent as AlignLeft } from "icons/remix/align-left.svg"; // Exact match +export { ReactComponent as AlignJustify } from "./remix/align-justify.svg"; // Exact match +export { ReactComponent as AlignRight } from "./remix/align-right.svg"; // Exact match +export { ReactComponent as AlignCenter } from "./remix/align-center.svg"; // Exact match +export { ReactComponent as AlignLeft } from "./remix/align-left.svg"; // Exact match export { ReactComponent as AlignClose } from "./icon-align-close.svg"; export { ReactComponent as AlignTop } from "./icon-align-top.svg"; export { ReactComponent as AlignBottom } from "./icon-align-bottom.svg"; -export { ReactComponent as AddIcon } from "icons/remix/add-line.svg"; // Closest match for add +export { ReactComponent as AddIcon } from "./remix/add-line.svg"; // Closest match for add export { ReactComponent as ApplicationDocIcon } from "./icon-application-doc.svg"; -export { ReactComponent as ImportAppIcon } from "icons/remix/download-cloud-line.svg"; // Closest match for import app +export { ReactComponent as ImportAppIcon } from "./remix/download-cloud-line.svg"; // Closest match for import app export { ReactComponent as ModuleDocIcon } from "./icon-module-doc.svg"; -export { ReactComponent as ImportIcon } from "icons/remix/download-cloud-line.svg"; // Closest match for import -export { ReactComponent as ImportIconV2 } from "icons/remix/download-cloud-line.svg"; // Closest match for import v2 +export { ReactComponent as ImportIcon } from "./remix/download-cloud-line.svg"; // Closest match for import +export { ReactComponent as ImportIconV2 } from "./remix/download-cloud-line.svg"; // Closest match for import v2 export { ReactComponent as ModuleMenuIcon } from "./icon-module-menu.svg"; -export { ReactComponent as ModuleIcon } from "icons/remix/stack-line.svg"; // Closest match for module +export { ReactComponent as ModuleIcon } from "./remix/stack-line.svg"; // Closest match for module export { ReactComponent as AllAppIcon } from "./icon-all-app.svg"; export { ReactComponent as DatasourceIcon } from "./icon-datasource.svg"; export { ReactComponent as QueryLibraryIcon } from "./icon-query-library.svg"; @@ -405,82 +410,82 @@ export { ReactComponent as TransformerIcon } from "./icon-transformer.svg"; // O export { ReactComponent as TempStateIcon } from "./icon-temp-state.svg"; export { ReactComponent as IconDep } from "./icon-style-dep.svg"; export { ReactComponent as IconRadius } from "./icon-style-border-radius.svg"; -export { ReactComponent as IconReset } from "icons/remix/refresh-line.svg"; +export { ReactComponent as IconReset } from "./remix/refresh-line.svg"; export { ReactComponent as NavComIcon } from "./icon-nav.svg"; export { ReactComponent as ButtonCompIcon } from "./icon-button.svg"; -export { ReactComponent as InputCompIcon } from "icons/remix/input-method-line.svg"; // Closest match for input component -export { ReactComponent as TextCompIcon } from "icons/remix/file-text-line.svg"; // Closest match for text component -export { ReactComponent as SwitchCompIcon } from "icons/remix/switch-line.svg"; // Closest match for switch component -export { ReactComponent as TableCompIcon } from "icons/remix/table-line.svg"; // Closest match for table component -export { ReactComponent as SelectCompIcon } from "icons/remix/dropdown-list.svg"; // Closest match for select component -export { ReactComponent as CheckboxCompIcon } from "icons/remix/checkbox-line.svg"; // Closest match for checkbox component -export { ReactComponent as RadioCompIcon } from "icons/remix/radio-line.svg"; // Closest match for radio component -export { ReactComponent as TimeCompIcon } from "icons/remix/time-line.svg"; // Closest match for time component +export { ReactComponent as InputCompIcon } from "./remix/input-method-line.svg"; // Closest match for input component +export { ReactComponent as TextCompIcon } from "./remix/file-text-line.svg"; // Closest match for text component +export { ReactComponent as SwitchCompIcon } from "./remix/switch-line.svg"; // Closest match for switch component +export { ReactComponent as TableCompIcon } from "./remix/table-line.svg"; // Closest match for table component +export { ReactComponent as SelectCompIcon } from "./remix/dropdown-list.svg"; // Closest match for select component +export { ReactComponent as CheckboxCompIcon } from "./remix/checkbox-line.svg"; // Closest match for checkbox component +export { ReactComponent as RadioCompIcon } from "./remix/radio-line.svg"; // Closest match for radio component +export { ReactComponent as TimeCompIcon } from "./remix/time-line.svg"; // Closest match for time component export { ReactComponent as TimeRangeCompIcon } from "./icon-timeRange.svg"; -export { ReactComponent as DateCompIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for date component +export { ReactComponent as DateCompIcon } from "./remix/calendar-2-line.svg"; // Closest match for date component export { ReactComponent as DateRangeCompIcon } from "./icon-dateRange.svg"; -export { ReactComponent as UploadCompIcon } from "icons/remix/upload-cloud-2-line.svg"; // Closest match for upload component -export { ReactComponent as ImageCompIcon } from "icons/remix/image-line.svg"; // Closest match for image component -export { ReactComponent as RatingCompIcon } from "icons/remix/star-line.svg"; // Closest match for rating component -export { ReactComponent as SliderCompIcon } from "icons/remix/git-commit-line.svg"; // Closest match for slider component +export { ReactComponent as UploadCompIcon } from "./remix/upload-cloud-2-line.svg"; // Closest match for upload component +export { ReactComponent as ImageCompIcon } from "./remix/image-line.svg"; // Closest match for image component +export { ReactComponent as RatingCompIcon } from "./remix/star-line.svg"; // Closest match for rating component +export { ReactComponent as SliderCompIcon } from "./remix/git-commit-line.svg"; // Closest match for slider component export { ReactComponent as ProcessCircleCompIcon } from "./icon-sliderCircle.svg"; -export { ReactComponent as ChartCompIcon } from "icons/remix/pie-chart-2-line.svg"; // Closest match for chart component +export { ReactComponent as ChartCompIcon } from "./remix/pie-chart-2-line.svg"; // Closest match for chart component export { ReactComponent as JsonFormCompIcon } from "./icon-jsonForm.svg"; -export { ReactComponent as FormCompIcon } from "icons/remix/draft-line.svg"; // Closest match for form component +export { ReactComponent as FormCompIcon } from "./remix/draft-line.svg"; // Closest match for form component export { ReactComponent as ProgressCompIcon } from "./icon-progress.svg"; export { ReactComponent as FileViewerCompIcon } from "./icon-fileViewer.svg"; export { ReactComponent as ContainerCompIcon } from "./icon-container.svg"; -export { ReactComponent as LinkCompIcon } from "icons/remix/links-line.svg"; // Closest match for link component -export { ReactComponent as MultiSelectCompIcon } from "icons/remix/list-check-3.svg"; // Closest match for multiselect component -export { ReactComponent as PasswordCompIcon } from "icons/remix/key-2-line.svg"; // Closest match for password component -export { ReactComponent as RangeSliderCompIcon } from "icons/remix/switch-line.svg"; // Closest match for range slider component +export { ReactComponent as LinkCompIcon } from "./remix/links-line.svg"; // Closest match for link component +export { ReactComponent as MultiSelectCompIcon } from "./remix/list-check-3.svg"; // Closest match for multiselect component +export { ReactComponent as PasswordCompIcon } from "./remix/key-2-line.svg"; // Closest match for password component +export { ReactComponent as RangeSliderCompIcon } from "./remix/switch-line.svg"; // Closest match for range slider component export { ReactComponent as SegmentedCompIcon } from "./icon-insert-segmentedControl.svg"; -export { ReactComponent as TextAreaCompIcon } from "icons/remix/text-block.svg"; // Closest match for textarea component +export { ReactComponent as TextAreaCompIcon } from "./remix/text-block.svg"; // Closest match for textarea component export { ReactComponent as NumberInputCompIcon } from "./icon-insert-numberInput.svg"; -export { ReactComponent as DropdownCompIcon } from "icons/remix/dropdown-list.svg"; // Closest match for dropdown component +export { ReactComponent as DropdownCompIcon } from "./remix/dropdown-list.svg"; // Closest match for dropdown component export { ReactComponent as DividerCompIcon } from "./icon-insert-divider.svg"; export { ReactComponent as RichTextEditorCompIcon } from "./icon-insert-rich-text-editor.svg"; -export { ReactComponent as ModalCompIcon } from "icons/remix/window-line.svg"; // Closest match for modal component -export { ReactComponent as TabbedContainerCompIcon } from "icons/remix/layout-column-line.svg"; // Closest match for tabbed container +export { ReactComponent as ModalCompIcon } from "./remix/window-line.svg"; // Closest match for modal component +export { ReactComponent as TabbedContainerCompIcon } from "./remix/layout-column-line.svg"; // Closest match for tabbed container export { ReactComponent as CascaderCompIcon } from "./icon-cascader.svg"; -export { ReactComponent as TreeIcon } from "icons/remix/node-tree.svg"; // Closest match for tree icon +export { ReactComponent as TreeIcon } from "./remix/node-tree.svg"; // Closest match for tree icon export { ReactComponent as TreeSelectIcon } from "./icon-tree-select.svg"; export { ReactComponent as CustomCompIcon } from "./icon-custom-component.svg"; -export { ReactComponent as IFrameCompIcon } from "icons/remix/window-2-line.svg"; // Closest match for iframe component +export { ReactComponent as IFrameCompIcon } from "./remix/window-2-line.svg"; // Closest match for iframe component export { ReactComponent as ListViewIcon } from "./icon-insert-listView.svg"; -export { ReactComponent as GridCompIcon } from "icons/remix/grid-line.svg"; // Closest match for grid component +export { ReactComponent as GridCompIcon } from "./remix/grid-line.svg"; // Closest match for grid component export { ReactComponent as PackUpIcon } from "./icon-Pack-up.svg"; -export { ReactComponent as SearchIcon } from "icons/remix/search-line.svg"; // Closest match for search icon -export { ReactComponent as SearchOutlinedIcon } from "icons/remix/search-eye-line.svg"; // Closest match for search outlined icon -export { ReactComponent as FilterIcon } from "icons/remix/filter-line.svg"; // Closest match for filter icon -export { ReactComponent as DownloadIcon } from "icons/remix/download-line.svg"; // Closest match for download icon -export { ReactComponent as DownloadBoldIcon } from "icons/remix/download-2-line.svg"; // Closest match for download bold icon +export { ReactComponent as SearchIcon } from "./remix/search-line.svg"; // Closest match for search icon +export { ReactComponent as SearchOutlinedIcon } from "./remix/search-eye-line.svg"; // Closest match for search outlined icon +export { ReactComponent as FilterIcon } from "./remix/filter-line.svg"; // Closest match for filter icon +export { ReactComponent as DownloadIcon } from "./remix/download-line.svg"; // Closest match for download icon +export { ReactComponent as DownloadBoldIcon } from "./remix/download-2-line.svg"; // Closest match for download bold icon export { ReactComponent as DownloadedIcon } from "./icon-downloaded.svg"; -export { ReactComponent as SettingIcon } from "icons/remix/settings-line.svg"; // Closest match for setting icon -export { ReactComponent as RefreshIcon } from "icons/remix/refresh-line.svg"; // Closest match for refresh icon -export { ReactComponent as DeleteIcon } from "icons/remix/delete-bin-6-line.svg"; // Closest match for delete icon +export { ReactComponent as SettingIcon } from "./remix/settings-line.svg"; // Closest match for setting icon +export { ReactComponent as RefreshIcon } from "./remix/refresh-line.svg"; // Closest match for refresh icon +export { ReactComponent as DeleteIcon } from "./remix/delete-bin-6-line.svg"; // Closest match for delete icon export { ReactComponent as DeleteInputIcon } from "./icon-deleteinput.svg"; export { ReactComponent as UpgradeIcon } from "./icon-upgrade.svg"; -export { ReactComponent as QuestionIcon } from "icons/remix/question-line.svg"; // Closest match for question icon -export { ReactComponent as CloseIcon } from "icons/remix/close-line.svg"; // Closest match for close icon -export { ReactComponent as SuccessIcon } from "icons/remix/check-double-line.svg"; // Closest match for success icon -export { ReactComponent as ErrorIcon } from "icons/remix/error-warning-line.svg"; // Closest match for error icon -export { ReactComponent as DocIcon } from "icons/remix/file-text-line.svg"; // Closest match for document icon +export { ReactComponent as QuestionIcon } from "./remix/question-line.svg"; // Closest match for question icon +export { ReactComponent as CloseIcon } from "./remix/close-line.svg"; // Closest match for close icon +export { ReactComponent as SuccessIcon } from "./remix/check-double-line.svg"; // Closest match for success icon +export { ReactComponent as ErrorIcon } from "./remix/error-warning-line.svg"; // Closest match for error icon +export { ReactComponent as DocIcon } from "./remix/file-text-line.svg"; // Closest match for document icon export { ReactComponent as DocBoldIcon } from "./icon-tutorial-bold.svg"; -export { ReactComponent as QRCodeCompIcon } from "icons/remix/qr-code-line.svg"; // Closest match for QR code component +export { ReactComponent as QRCodeCompIcon } from "./remix/qr-code-line.svg"; // Closest match for QR code component export { ReactComponent as NavDocIcon } from "./icon-nav-doc.svg"; export { ReactComponent as LabIcon } from "./icon-laboratory.svg"; export { ReactComponent as JsonExplorerCompIcon } from "./icon-json-explorer.svg"; export { ReactComponent as JsonEditorCompIcon } from "./icon-json-editor.svg"; -export { ReactComponent as ArrowIcon } from "icons/remix/arrow-right-s-line.svg"; // Closest generic match for arrow icon -export { ReactComponent as ArrowSolidIcon } from "icons/remix/arrow-right-line.svg"; // Closest match for solid arrow icon -export { ReactComponent as AudioCompIcon } from "icons/remix/speaker-3-line.svg"; // Closest match for audio component -export { ReactComponent as VideoCompIcon } from "icons/remix/video-line.svg"; // Closest match for video component +export { ReactComponent as ArrowIcon } from "./remix/arrow-right-s-line.svg"; // Closest generic match for arrow icon +export { ReactComponent as ArrowSolidIcon } from "./remix/arrow-right-line.svg"; // Closest match for solid arrow icon +export { ReactComponent as AudioCompIcon } from "./remix/speaker-3-line.svg"; // Closest match for audio component +export { ReactComponent as VideoCompIcon } from "./remix/video-line.svg"; // Closest match for video component export { ReactComponent as videoPlayTriangle } from "./icon-video-play-triangle.svg"; -export { ReactComponent as DrawerCompIcon } from "icons/remix/sidebar-unfold-line.svg"; // Closest match for drawer component -export { ReactComponent as LeftMeetingIcon } from "icons/remix/team-line.svg"; // Closest match for left meeting icon -export { ReactComponent as PlusIcon } from "icons/remix/add-line.svg"; // Closest match for plus icon -export { ReactComponent as HomeIcon } from "icons/remix/home-2-line.svg"; // Closest match for home icon +export { ReactComponent as DrawerCompIcon } from "./remix/sidebar-unfold-line.svg"; // Closest match for drawer component +export { ReactComponent as LeftMeetingIcon } from "./remix/team-line.svg"; // Closest match for left meeting icon +export { ReactComponent as PlusIcon } from "./remix/add-line.svg"; // Closest match for plus icon +export { ReactComponent as HomeIcon } from "./remix/home-2-line.svg"; // Closest match for home icon export { ReactComponent as HomeModuleIcon } from "./icon-application-module.svg"; export { ReactComponent as HomeQueryLibraryIcon } from "./icon-application-query-library.svg"; export { ReactComponent as HomeDataSourceIcon } from "./icon-application-datasource.svg"; @@ -491,8 +496,8 @@ export { ReactComponent as HomeQueryLibraryActiveIcon } from "./icon-application export { ReactComponent as HomeDataSourceActiveIcon } from "./icon-application-datasource-active.svg"; export { ReactComponent as RecyclerActiveIcon } from "./icon-application-recycler-active.svg"; export { ReactComponent as FavoritesIcon } from "./icon-application-favorites.svg"; -export { ReactComponent as HomeSettingIcon } from "icons/remix/settings-5-line.svg"; // Closest match for home setting icon -export { ReactComponent as FolderIcon } from "icons/remix/folder-4-line.svg"; // Closest match for folder icon +export { ReactComponent as HomeSettingIcon } from "./remix/settings-5-line.svg"; // Closest match for home setting icon +export { ReactComponent as FolderIcon } from "./remix/folder-4-line.svg"; // Closest match for folder icon export { ReactComponent as AllTypesIcon } from "./icon-application-all.svg"; export { ReactComponent as InviteUserIcon } from "./icon-application-invite-user.svg"; export { ReactComponent as HomeEmptyIcon } from "./icon-application-empty.svg"; @@ -520,15 +525,15 @@ export { ReactComponent as MSSQLIcon } from "./icon-query-mssql.svg"; export { ReactComponent as SMTPIcon } from "./icon-query-SMTP.svg"; export { ReactComponent as OracleIcon } from "./icon-query-OracleDB.svg"; export { ReactComponent as ClickHouseIcon } from "./icon-query-ClickHouse.svg"; -export { ReactComponent as ResetIcon } from "icons/remix/refresh-line.svg"; // Closest match for reset icon -export { ReactComponent as EditIcon } from "icons/remix/edit-2-line.svg"; // Closest match for edit icon +export { ReactComponent as ResetIcon } from "./remix/refresh-line.svg"; // Closest match for reset icon +export { ReactComponent as EditIcon } from "./remix/edit-2-line.svg"; // Closest match for edit icon export { ReactComponent as EditableIcon } from "./icon-editable.svg"; export { ReactComponent as LeftStateIcon } from "./icon-left-state.svg"; export { ReactComponent as LeftSettingIcon } from "./icon-left-setting.svg"; export { ReactComponent as LeftHelpIcon } from "./icon-left-help.svg"; export { ReactComponent as LeftPreloadIcon } from "./icon-left-preload.svg"; export { ReactComponent as CollapsibleContainerCompIcon } from "./icon-collapsible-container.svg"; -export { ReactComponent as ToggleButtonCompIcon } from "icons/remix/switch-line.svg"; // Closest match for toggle button component +export { ReactComponent as ToggleButtonCompIcon } from "./remix/switch-line.svg"; // Closest match for toggle button component export { ReactComponent as GoogleSheetsIcon } from "./icon-query-GoogleSheets.svg"; export { ReactComponent as GraphqlIcon } from "./icon-query-Graphql.svg"; export { ReactComponent as SnowflakeIcon } from "./icon-query-snowflake.svg"; @@ -540,71 +545,71 @@ export { ReactComponent as HomeSettingsActiveIcon } from "./icon-home-settings-a export { ReactComponent as HelpGithubIcon } from "./icon-help-github.svg"; export { ReactComponent as HelpDiscordIcon } from "./icon-help-discord.svg"; export { ReactComponent as LeftAudio } from "./icon-left-comp-audio.svg"; -export { ReactComponent as LeftButton } from "icons/remix/rectangle-line.svg"; // Closest match for left button -export { ReactComponent as LeftChart } from "icons/remix/bar-chart-2-line.svg"; // Closest match for left chart -export { ReactComponent as LeftCheckbox } from "icons/remix/checkbox-line.svg"; // Closest match for left checkbox +export { ReactComponent as LeftButton } from "./remix/rectangle-line.svg"; // Closest match for left button +export { ReactComponent as LeftChart } from "./remix/bar-chart-2-line.svg"; // Closest match for left chart +export { ReactComponent as LeftCheckbox } from "./remix/checkbox-line.svg"; // Closest match for left checkbox export { ReactComponent as LeftCommon } from "./icon-left-comp-common.svg"; export { ReactComponent as LeftContainer } from "./icon-left-comp-container.svg"; -export { ReactComponent as LeftDate } from "icons/remix/calendar-2-line.svg"; // Closest match for left date +export { ReactComponent as LeftDate } from "./remix/calendar-2-line.svg"; // Closest match for left date export { ReactComponent as LeftDivider } from "./icon-left-comp-divider.svg"; -export { ReactComponent as LeftDrawer } from "icons/remix/sidebar-unfold-line.svg"; // Closest match for drawer component -export { ReactComponent as LeftMeeting } from "icons/remix/team-line.svg"; // Closest match for left meeting icon -export { ReactComponent as LeftFile } from "icons/remix/file-line.svg"; // Closest match for left file +export { ReactComponent as LeftDrawer } from "./remix/sidebar-unfold-line.svg"; // Closest match for drawer component +export { ReactComponent as LeftMeeting } from "./remix/team-line.svg"; // Closest match for left meeting icon +export { ReactComponent as LeftFile } from "./remix/file-line.svg"; // Closest match for left file export { ReactComponent as LeftFileViewer } from "./icon-left-comp-fileViewer.svg"; -export { ReactComponent as LeftForm } from "icons/remix/draft-line.svg"; // Closest match for left form -export { ReactComponent as LeftIframe } from "icons/remix/window-2-line.svg"; // Closest match for left iframe -export { ReactComponent as LeftImage } from "icons/remix/image-line.svg"; // Closest match for left image -export { ReactComponent as LeftInput } from "icons/remix/input-method-line.svg"; // Closest match for left input +export { ReactComponent as LeftForm } from "./remix/draft-line.svg"; // Closest match for left form +export { ReactComponent as LeftIframe } from "./remix/window-2-line.svg"; // Closest match for left iframe +export { ReactComponent as LeftImage } from "./remix/image-line.svg"; // Closest match for left image +export { ReactComponent as LeftInput } from "./remix/input-method-line.svg"; // Closest match for left input export { ReactComponent as LeftJsonEditor } from "./icon-left-comp-jsonEditor.svg"; -export { ReactComponent as LeftLink } from "icons/remix/links-line.svg"; // Closest match for left link +export { ReactComponent as LeftLink } from "./remix/links-line.svg"; // Closest match for left link export { ReactComponent as LeftListView } from "./icon-left-comp-listView.svg"; -export { ReactComponent as LeftModal } from "icons/remix/shadow-line.svg"; // Closest match for left modal -export { ReactComponent as LeftNavigation } from "icons/remix/compass-line.svg"; // Closest match for left navigation +export { ReactComponent as LeftModal } from "./remix/shadow-line.svg"; // Closest match for left modal +export { ReactComponent as LeftNavigation } from "./remix/compass-line.svg"; // Closest match for left navigation export { ReactComponent as LeftNumberInput } from "./icon-insert-numberInput.svg"; -export { ReactComponent as LeftPassword } from "icons/remix/key-2-line.svg"; // Closest match for left password +export { ReactComponent as LeftPassword } from "./remix/key-2-line.svg"; // Closest match for left password export { ReactComponent as LeftProgress } from "./icon-progress.svg"; -export { ReactComponent as LeftQrCode } from "icons/remix/qr-code-line.svg"; // Closest match for left QR code -export { ReactComponent as LeftRadio } from "icons/remix/radio-line.svg"; // Closest match for left radio -export { ReactComponent as LeftRating } from "icons/remix/star-line.svg"; // Closest match for left rating +export { ReactComponent as LeftQrCode } from "./remix/qr-code-line.svg"; // Closest match for left QR code +export { ReactComponent as LeftRadio } from "./remix/radio-line.svg"; // Closest match for left radio +export { ReactComponent as LeftRating } from "./remix/star-line.svg"; // Closest match for left rating export { ReactComponent as LeftSegmentedControl } from "./icon-left-comp-segmentedControl.svg"; -export { ReactComponent as LeftSelect } from "icons/remix/dropdown-list.svg"; // Closest match for left select -export { ReactComponent as LeftSlider } from "icons/remix/git-commit-line.svg"; // Closest match for left slider -export { ReactComponent as LeftSwitch } from "icons/remix/switch-line.svg"; // Closest match for left switch -export { ReactComponent as LeftTable } from "icons/remix/table-line.svg"; // Closest match for left table -export { ReactComponent as LeftText } from "icons/remix/file-text-line.svg"; // Closest match for left text -export { ReactComponent as LeftTime } from "icons/remix/time-line.svg"; // Closest match for left time -export { ReactComponent as LeftTree } from "icons/remix/node-tree.svg"; // Closest match for left tree -export { ReactComponent as LeftVideo } from "icons/remix/video-line.svg"; // Closest match for left video -export { ReactComponent as LeftOpen } from "icons/remix/door-open-line.svg"; // Closest match for left open -export { ReactComponent as LeftClose } from "icons/remix/door-closed-line.svg"; // Closest match for left close -export { ReactComponent as ScannerIcon } from "icons/remix/scan-line.svg"; // Closest match for scanner icon +export { ReactComponent as LeftSelect } from "./remix/dropdown-list.svg"; // Closest match for left select +export { ReactComponent as LeftSlider } from "./remix/git-commit-line.svg"; // Closest match for left slider +export { ReactComponent as LeftSwitch } from "./remix/switch-line.svg"; // Closest match for left switch +export { ReactComponent as LeftTable } from "./remix/table-line.svg"; // Closest match for left table +export { ReactComponent as LeftText } from "./remix/file-text-line.svg"; // Closest match for left text +export { ReactComponent as LeftTime } from "./remix/time-line.svg"; // Closest match for left time +export { ReactComponent as LeftTree } from "./remix/node-tree.svg"; // Closest match for left tree +export { ReactComponent as LeftVideo } from "./remix/video-line.svg"; // Closest match for left video +export { ReactComponent as LeftOpen } from "./remix/door-open-line.svg"; // Closest match for left open +export { ReactComponent as LeftClose } from "./remix/door-closed-line.svg"; // Closest match for left close +export { ReactComponent as ScannerIcon } from "./remix/scan-line.svg"; // Closest match for scanner icon export { ReactComponent as MaterialUploadIcon } from "./icon-material-upload.svg"; -export { ReactComponent as CalendarCompIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for calendar component +export { ReactComponent as CalendarCompIcon } from "./remix/calendar-2-line.svg"; // Closest match for calendar component export { ReactComponent as LeftSignature } from "./icon-left-signature.svg"; -export { ReactComponent as UndoIcon } from "icons/remix/arrow-go-back-line.svg"; // Closest match for undo icon -export { ReactComponent as SignatureIcon } from "icons/remix/pen-nib-line.svg"; // Closest match for signature icon +export { ReactComponent as UndoIcon } from "./remix/arrow-go-back-line.svg"; // Closest match for undo icon +export { ReactComponent as SignatureIcon } from "./remix/pen-nib-line.svg"; // Closest match for signature icon export { ReactComponent as ManualIcon } from "./icon-manual.svg"; -export { ReactComponent as WarnIcon } from "icons/remix/alert-line.svg"; // Closest match for warn icon +export { ReactComponent as WarnIcon } from "./remix/alert-line.svg"; // Closest match for warn icon export { ReactComponent as SyncManualIcon } from "./icon-sync-manual.svg"; -export { ReactComponent as DangerIcon } from "icons/remix/error-warning-line.svg"; // Closest match for danger icon -export { ReactComponent as TableMinusIcon } from "icons/remix/checkbox-indeterminate-line.svg"; // Closest match for table minus icon -export { ReactComponent as TablePlusIcon } from "icons/remix/add-box-line.svg"; // Closest match for table plus icon -export { ReactComponent as MobileAppIcon } from "icons/remix/smartphone-line.svg"; // Closest match for mobile app icon -export { ReactComponent as MobileNavIcon } from "icons/remix/navigation-line.svg"; // Closest match for mobile navigation icon -export { ReactComponent as PcNavIcon } from "icons/remix/computer-line.svg"; // Closest match for PC navigation icon -export { ReactComponent as UnLockIcon } from "icons/remix/lock-unlock-line.svg"; // Closest match for unlock icon -export { ReactComponent as CalendarDeleteIcon } from "icons/remix/calendar-2-line.svg"; // Closest match for calendar delete icon -export { ReactComponent as TableCheckedIcon } from "icons/remix/checkbox-circle-line.svg"; // Closest match for table checked icon -export { ReactComponent as TableUnCheckedIcon } from "icons/remix/checkbox-blank-circle-line.svg"; // Closest match for table unchecked icon -export { ReactComponent as FileFolderIcon } from "icons/remix/folder-2-line.svg"; // Closest match for file folder icon -export { ReactComponent as ExpandIcon } from "icons/remix/menu-unfold-line.svg"; // Closest match for expand icon -export { ReactComponent as CompressIcon } from "icons/remix/menu-fold-line.svg"; // Closest match for compress icon -export { ReactComponent as TableCellsIcon } from "icons/remix/grid-line.svg"; // Closest match for table cells icon -export { ReactComponent as TimeLineIcon } from "icons/remix/time-line.svg"; // Closest match for timeline icon +export { ReactComponent as DangerIcon } from "./remix/error-warning-line.svg"; // Closest match for danger icon +export { ReactComponent as TableMinusIcon } from "./remix/checkbox-indeterminate-line.svg"; // Closest match for table minus icon +export { ReactComponent as TablePlusIcon } from "./remix/add-box-line.svg"; // Closest match for table plus icon +export { ReactComponent as MobileAppIcon } from "./remix/smartphone-line.svg"; // Closest match for mobile app icon +export { ReactComponent as MobileNavIcon } from "./remix/navigation-line.svg"; // Closest match for mobile navigation icon +export { ReactComponent as PcNavIcon } from "./remix/computer-line.svg"; // Closest match for PC navigation icon +export { ReactComponent as UnLockIcon } from "./remix/lock-unlock-line.svg"; // Closest match for unlock icon +export { ReactComponent as CalendarDeleteIcon } from "./remix/calendar-2-line.svg"; // Closest match for calendar delete icon +export { ReactComponent as TableCheckedIcon } from "./remix/checkbox-circle-line.svg"; // Closest match for table checked icon +export { ReactComponent as TableUnCheckedIcon } from "./remix/checkbox-blank-circle-line.svg"; // Closest match for table unchecked icon +export { ReactComponent as FileFolderIcon } from "./remix/folder-2-line.svg"; // Closest match for file folder icon +export { ReactComponent as ExpandIcon } from "./remix/menu-unfold-line.svg"; // Closest match for expand icon +export { ReactComponent as CompressIcon } from "./remix/menu-fold-line.svg"; // Closest match for compress icon +export { ReactComponent as TableCellsIcon } from "./remix/grid-line.svg"; // Closest match for table cells icon +export { ReactComponent as TimeLineIcon } from "./remix/time-line.svg"; // Closest match for timeline icon export { ReactComponent as LottieIcon } from "./icon-lottie.svg"; export { ReactComponent as CommentIcon } from "./icon-comment-comp.svg"; export { ReactComponent as MentionIcon } from "./icon-mention-comp.svg"; export { ReactComponent as AutoCompleteCompIcon } from "./icon-autocomplete-comp.svg"; export { ReactComponent as WidthIcon } from "./icon-width.svg"; -export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/remix/layout-column-line.svg"; // Closest match for responsive layout component +export { ReactComponent as ResponsiveLayoutCompIcon } from "./remix/layout-column-line.svg"; // Closest match for responsive layout component export { ReactComponent as TextSizeIcon } from "./icon-text-size.svg"; */ \ No newline at end of file diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index b5ec23f09..cd57c9165 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -104,7 +104,12 @@ export const en = { "components": "Active Components", "modals": "in-App Modals", "expandTip": "Click to Expand {component}'s Data", - "collapseTip": "Click to Collapse {component}'s Data" + "collapseTip": "Click to Collapse {component}'s Data", + "layers": "Layers", + "activatelayers": "Use Layers in this App", + "selectedComponents": "Selected Components...", + "displayComponents": "control Display", + "lockComponents": "control Position", }, // second part diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 7ff3d6825..ac6020fd4 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -106,7 +106,10 @@ leftPanel: { components: "组件", modals: "对话框", expandTip: "点击展开 {component} 的数据", - collapseTip: "点击折叠 {component} 的数据" + collapseTip: "点击折叠 {component} 的数据", + layers: "图层", + activatelayers: "激活图层", + selectedComponents: "已选组件", }, bottomPanel: { title: "查询", diff --git a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx index faee2cea3..5924d90bf 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftContent.tsx @@ -6,16 +6,16 @@ import { CollapseTitle as Title, CopyTextButton, FoldedIcon, + LeftClose, LeftCommon, - LeftInfoFill, - LeftInfoLine, + LeftOpen, PadDiv, ScrollBar, Tooltip, UnfoldIcon, UnShow, } from "lowcoder-design"; -import React, { ReactNode, useCallback, useContext, useMemo, useState, useEffect } from "react"; +import React, { ReactNode, useCallback, useContext, useMemo, useState } from "react"; import { hookCompCategory } from "comps/hooks/hookCompTypes"; import _ from "lodash"; import styled from "styled-components"; @@ -32,11 +32,6 @@ import { UICompType } from "comps/uiCompRegistry"; import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; import { DataNode, EventDataNode } from "antd/lib/tree"; import { isAggregationApp } from "util/appUtils"; -import cloneDeep from 'lodash/cloneDeep'; -import { useDispatch } from "react-redux"; -import { useApplicationId } from "util/hooks"; -import { updateApplication } from "redux/reduxActions/applicationActions"; -import { Divider } from "antd"; const CollapseTitleWrapper = styled.div` display: flex; @@ -196,8 +191,6 @@ const CollapseView = React.memo( interface LeftContentProps { uiComp: InstanceType; - checkable?: boolean; - isDraggable?: boolean; } enum LeftTabKey { @@ -215,8 +208,6 @@ type NodeItem = { title: string; type?: UICompType; children: NodeItem[]; - pos?: number; - disabled?: boolean; }; type NodeInfo = { @@ -251,16 +242,12 @@ export const LeftContent = (props: LeftContentProps) => { const editorState = useContext(EditorContext); const [expandedKeys, setExpandedKeys] = useState>([]); const [showData, setShowData] = useState([]); - const dispatch = useDispatch(); - const applicationId = useApplicationId(); - const checkable = props.checkable || false; - const isDraggable = props.isDraggable || false; const getTree = (tree: CompTree, result: NodeItem[], key?: string) => { const { items, children } = tree; if (Object.keys(items).length) { for (const i in items) { - const info: NodeItem = { + const info = { title: items[i].children.name.getView(), type: items[i].children.compType.getView() as UICompType, key: i, @@ -268,13 +255,12 @@ export const LeftContent = (props: LeftContentProps) => { }; if (key) { const parent = getTreeNodeByKey(result, key); - info.disabled = true; parent?.children.push(info); } else { result.push(info); } } - // result = _.sortBy(result, [(x) => x.title]); + result = _.sortBy(result, [(x) => x.title]); } if (Object.keys(children).length) { for (const i in children) { @@ -367,7 +353,7 @@ export const LeftContent = (props: LeftContentProps) => { setShowData(newData); }} > - +
) : ( @@ -400,7 +386,7 @@ export const LeftContent = (props: LeftContentProps) => { setShowData(newData); }} > - + ))} @@ -422,57 +408,17 @@ export const LeftContent = (props: LeftContentProps) => { ); }; - const [componentTreeData, setComponentTreeData] = useState([]); - const [modalsTreeData, setModalsTreeData] = useState([]); - - useEffect(() => { - const compData = getTreeUIData(TreeUIKey.Components); - setComponentTreeData(compData); - }, [editorState]); - - useEffect(() => { - const modalsData = getTreeUIData(TreeUIKey.Modals); - setModalsTreeData(modalsData); - }, [editorState]); - - const getTreeUIData = (type: TreeUIKey) => { - const tree = - type === TreeUIKey.Components - ? editorState.getUIComp().getTree() - : editorState.getHooksComp().getUITree(); - const explorerData: NodeItem[] = getTree(tree, []); - // TODO: handle sorting inside modals/drawers - if(type === TreeUIKey.Modals) return explorerData; - - const dsl = editorState.rootComp.toJsonValue(); - explorerData.forEach(data => { - data['pos'] = dsl.ui.layout[data.key].pos; - }) - explorerData.sort((a, b) => { - const aPos = a?.pos || 0; - const bPos = b?.pos || 0; - if (aPos < bPos) return -1; - if (aPos > bPos) return 1; - return 0; - }); - return explorerData; - } - - const getTreeUI = (type: TreeUIKey) => { - // here the components get sorted by name - // TODO: sort by category - // TODO: sort by Types etc. const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]); - /* const tree = + const tree = type === TreeUIKey.Components ? editorState.getUIComp().getTree() : editorState.getHooksComp().getUITree(); - const explorerData: NodeItem[] = getTree(tree, []); */ + const explorerData: NodeItem[] = getTree(tree, []); let selectedKeys = []; if (editorState.selectedCompNames.size === 1) { const key = Object.keys(editorState.selectedComps())[0]; - const parentKeys = getParentNodeKeysByKey(type === TreeUIKey.Components ? componentTreeData : modalsTreeData, key); + const parentKeys = getParentNodeKeysByKey(explorerData, key); if (parentKeys && parentKeys.length) { let needSet = false; parentKeys.forEach((key) => { @@ -486,22 +432,22 @@ export const LeftContent = (props: LeftContentProps) => { } return ( - <> props.type && ( -
{/* Adjust the margin as needed */} - {CompStateIcon[props.type as UICompType] || } -
- )} - switcherIcon={(props: any) => props.expanded ? : } + props.type && (CompStateIcon[props.type] || )} + icon={(props: any) => props.type && (CompStateIcon[props.type as UICompType] || )} + // switcherIcon={({ expanded }: { expanded: boolean }) => + // expanded ? : + // } + switcherIcon={(props: any) => + props.expanded ? : + } expandedKeys={expandedKeys} onExpand={(keys) => setExpandedKeys(keys)} onClick={(e, node) => handleNodeClick(e, node, uiCompInfos)} selectedKeys={selectedKeys} - titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} /> - -
- + titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} + /> ); }; @@ -509,15 +455,15 @@ export const LeftContent = (props: LeftContentProps) => { if (isAggregationApp(editorState.getAppType())) { return; } - return getTreeUI(TreeUIKey.Components); // Pass componentTreeData - }, [editorState, uiCollapseClick, expandedKeys, showData, componentTreeData]); - + return getTreeUI(TreeUIKey.Components); + }, [editorState, uiCollapseClick, expandedKeys, showData]); + const modalsCollapse = useMemo(() => { if (isAggregationApp(editorState.getAppType())) { return; } - return getTreeUI(TreeUIKey.Modals); // Pass modalsTreeData - }, [editorState, uiCollapseClick, expandedKeys, showData, modalsTreeData]); + return getTreeUI(TreeUIKey.Modals); + }, [editorState, uiCollapseClick, expandedKeys, showData]); const bottomResCollapse = useMemo(() => { return editorState @@ -551,7 +497,6 @@ export const LeftContent = (props: LeftContentProps) => { }, [editorState]); const moduleLayoutComp = uiComp.getModuleLayoutComp(); - const stateContent = (
@@ -604,4 +549,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 bba1dfbd3..9529758dd 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx @@ -4,55 +4,57 @@ import { Collapse, CollapseLabel as Label, CollapseTitle as Title, - CopyTextButton, FoldedIcon, LeftCommon, - LeftInfoFill, - LeftInfoLine, - PadDiv, ScrollBar, Tooltip, UnfoldIcon, - UnShow, + LeftLayersIcon, + GridIcon, + LeftShow, + LeftHide, + LeftLock, + LeftUnlock, } from "lowcoder-design"; import React, { ReactNode, useCallback, useContext, useMemo, useState, useEffect } from "react"; -import { hookCompCategory } from "comps/hooks/hookCompTypes"; -import _ from "lodash"; +import _, { get } from "lodash"; import styled from "styled-components"; import { leftCompListClassName } from "pages/tutorials/tutorialsConstant"; import UIComp from "comps/comps/uiComp"; -import { BottomResTypeEnum } from "types/bottomRes"; import { getParentNodeKeysByKey, getTreeNodeByKey, safeJSONStringify } from "util/objectUtils"; -import { Tabs, TabTitle } from "components/Tabs"; import { BackgroundColor, TopHeaderHeight } from "constants/style"; import { trans } from "i18n"; import { CompTree } from "comps/comps/containerBase"; import { CompStateIcon } from "./editorConstants"; import { UICompType } from "comps/uiCompRegistry"; -import { CollapseWrapper, DirectoryTreeStyle, Node } from "./styledComponents"; -import { DataNode, EventDataNode } from "antd/lib/tree"; +import { DirectoryTreeStyle, Node } from "./styledComponents"; import { isAggregationApp } from "util/appUtils"; import cloneDeep from 'lodash/cloneDeep'; import { useDispatch } from "react-redux"; import { useApplicationId } from "util/hooks"; import { updateApplication } from "redux/reduxActions/applicationActions"; -import { Divider } from "antd"; +import { Button, Divider, Dropdown, Flex, Input, MenuProps, Space } from "antd"; import { Switch } from "antd"; import { saveCollisionStatus, getCollisionStatus, } from "util/localStorageUtil"; +import { check, withViewFn } from "@lowcoder-ee/index.sdk"; +import { DownOutlined } from "@ant-design/icons"; +import { ItemType } from "antd/es/menu/hooks/useItems"; export type DisabledCollisionStatus = "true" | "false"; // "true" means collision is not enabled - Layering works, "false" means collision is enabled - Layering does not work -export type ToggleCollisionStatus = ( - collisionStatus?: DisabledCollisionStatus - ) => void; +export type ToggleCollisionStatus = (collisionStatus?: DisabledCollisionStatus) => void; interface LeftLayersContentProps { uiComp: InstanceType; } +const DropdownLeftShow = () => ( + // Setting custom viewBox +); + type NodeItem = { key: string; title: string; @@ -67,6 +69,14 @@ const LeftLayersContentWrapper = styled.div` height: calc(100vh - ${TopHeaderHeight}); `; +const CustomDropdown = styled(Dropdown)` + .ant-dropdown-menu-item-icon { + width: 14px !important; + height: 14px !important; + max-width: 14px !important; + } +`; + export const LeftLayersContent = (props: LeftLayersContentProps) => { const { uiComp } = props; const editorState = useContext(EditorContext); @@ -129,25 +139,35 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { {node.title} - ); }; const [componentTreeData, setComponentTreeData] = useState([]); + // update component tree data when editor state changes useEffect(() => { const compData = getTreeUIData(); setComponentTreeData(compData); }, [editorState]); - + + const getTreeUIData = () => { const tree = editorState.getUIComp().getTree(); const explorerData: NodeItem[] = getTree(tree, []); const dsl = editorState.rootComp.toJsonValue(); - explorerData.forEach(data => { - data['pos'] = dsl.ui.layout[data.key].pos; - }) + + if (dsl.ui.compType === "module") { + explorerData.forEach(data => { + data['pos'] = dsl.ui.comp.container.layout[data.key].pos; + }) + } + else { + explorerData.forEach(data => { + data['pos'] = dsl.ui.layout[data.key].pos; + }) + } + explorerData.sort((a, b) => { const aPos = a?.pos || 0; const bPos = b?.pos || 0; @@ -155,6 +175,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { if (aPos > bPos) return 1; return 0; }); + return explorerData; } @@ -233,73 +254,163 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { return currentNode; }; + // here we handle the checked keys of the component tree + const [checkedKeys, setCheckedKeys] = useState([]); + const [actionValue, setActionValue] = useState(""); + + const handleActionValueChange = (e: any) => { + setActionValue(e.target.value); + } + + // sync selected components with checked keys + useEffect(() => { + setCheckedKeys([]); + const selectedComponentsOnCanvas: string[] = []; + const compTree = editorState.getUIComp().getTree(); + const explorerData: NodeItem[] = getTree(compTree, []); + for (let value of editorState.selectedCompNames) { + for (let key of explorerData) { + if (key.title === value) { + selectedComponentsOnCanvas.push(key.key); + } + } + } + setCheckedKeys(selectedComponentsOnCanvas); + }, [editorState]); const onCheck = (checkedKeys: any, e: any) => { setCheckedKeys(checkedKeys); - console.log('onCheck', checkedKeys); + const checkedComponents = new Set(); + for (let key of e.checkedNodes){ + checkedComponents.add(key.title); + } + editorState.setSelectedCompNames(checkedComponents, "leftPanel"); + } + + const getCheckedKeys = () => { + return checkedKeys; + } + + const getActionValue = () => { + return actionValue; } + const handleComponentsActions = (actionType : string) => { + for (let key of getCheckedKeys()) { + const node = getTreeNodeByKey(componentTreeData, key); + const comp = editorState.getUICompByName(node.title); + if (comp != undefined) { + if (actionType === "hidden") { + comp.children.comp.dispatchChangeValueAction({ hidden: getActionValue() }); + } + else if (actionType === "disable") { + comp.children.comp.dispatchChangeValueAction({ disabled: getActionValue() }); + } + else if (actionType === "background") { + comp.children.comp.dispatchChangeValueAction({ BackgroundColor : getActionValue() }); + } + else { + comp.children.comp.dispatchChangeValueAction({ actionType: getActionValue() }); + } + } + } + } + + /* + + dispatch( + multiChangeAction({ + width: changeValueAction(width, true), + autoWidth: changeValueAction("fixed", true), + }) + ); + + */ + + const layerActions: ItemType[] = [ + { + label: 'Hide Component', + key: 'hide', + onClick: () => handleComponentsActions("hidden"), + }, + { + label: 'Disable Component', + key: 'background', + onClick: () => handleComponentsActions("disable"), + }, + { + label: 'Component Background', + key: '3', + onClick: () => handleComponentsActions("background"), + }, + { + label: 'Unlock', + key: '4', + }, + ]; + const getTreeUI = () => { // here the components get sorted by name // TODO: sort by category // TODO: sort by Types etc. const uiCompInfos = _.sortBy(editorState.uiCompInfoList(), [(x) => x.name]); - - /* const tree = - type === TreeUIKey.Components - ? editorState.getUIComp().getTree() - : editorState.getHooksComp().getUITree(); - const explorerData: NodeItem[] = getTree(tree, []); */ - - let selectedKeys = []; - if (editorState.selectedCompNames.size === 1) { - const key = Object.keys(editorState.selectedComps())[0]; - const parentKeys = getParentNodeKeysByKey(componentTreeData, key); - if (parentKeys && parentKeys.length) { - let needSet = false; - parentKeys.forEach((key) => { - if (!expandedKeys.includes(key)) { - needSet = true; - } - }); - needSet && setExpandedKeys(_.union(expandedKeys, parentKeys)); - } - selectedKeys.push(key); - } - const isDraggable = editorState.collisionStatus === "true" ? true : false; return ( <> - +
+ {trans("leftPanel.activatelayers")} + { toggleCollisionStatus(value == true ? "true" : "false"); editorState.setCollisionStatus(value == true ? "true" : "false"); - } } /> - handleDrop(info)} - treeData={componentTreeData} - icon={(props: any) => props.type && ( -
{/* Adjust the margin as needed */} - {CompStateIcon[props.type as UICompType] || } -
- )} - switcherIcon={(props: any) => props.expanded ? : } - expandedKeys={expandedKeys} - onExpand={(keys) => setExpandedKeys(keys)} - // onClick={(e, node) => handleNodeClick(e, node, uiCompInfos)} - selectedKeys={selectedKeys} - titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} /> - -
- + } } />
+ + handleDrop(info)} + treeData={componentTreeData} + icon={(props: any) => props.type && ( +
{/* Adjust the margin as needed */} + {CompStateIcon[props.type as UICompType] || } +
+ )} + switcherIcon={(props: any) => props.expanded ? : } + expandedKeys={expandedKeys} + onExpand={(keys) => setExpandedKeys(keys)} + titleRender={(nodeData) => getTreeNode(nodeData as NodeItem, uiCompInfos)} /> + +
+ + {trans("leftPanel.selectedComponents")}
+ {trans("leftPanel.displayComponents")} + + + + +
+
+ +
+ + + ); }; @@ -313,8 +424,8 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { const layerControlContent = (
- - {uiCollapse} + +
{uiCollapse}
diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx index 45a512214..af1981c37 100644 --- a/client/packages/lowcoder/src/pages/editor/editorView.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx @@ -13,6 +13,7 @@ import { LeftPreloadIcon, LeftSettingIcon, LeftStateIcon, + LeftLayersIcon, ScrollBar, } from "lowcoder-design"; import { useTemplateViewMode } from "util/hooks"; @@ -209,7 +210,7 @@ enum SiderKey { Layout = "layout", } -const items = [ +const standardSiderItems = [ { key: SiderKey.State, icon: , @@ -220,10 +221,20 @@ const items = [ }, { key: SiderKey.Layout, - icon: , + icon: , }, ]; +const aggregationSiderItems = [ + { + key: SiderKey.State, + icon: , + }, + { + key: SiderKey.Setting, + icon: , + } +]; function EditorView(props: EditorViewProps) { const { uiComp } = props; @@ -392,13 +403,15 @@ function EditorView(props: EditorViewProps) { - clickMenu(params)} > From 02c1ab338a354fe15fe69f39ee656333009dbffc Mon Sep 17 00:00:00 2001 From: RAHEEL Date: Mon, 5 Feb 2024 21:50:03 +0500 Subject: [PATCH 7/9] bulk actions --- .../src/pages/editor/LeftLayersContent.tsx | 62 +++++++++---------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx index 9529758dd..05470460f 100644 --- a/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx +++ b/client/packages/lowcoder/src/pages/editor/LeftLayersContent.tsx @@ -33,7 +33,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { useDispatch } from "react-redux"; import { useApplicationId } from "util/hooks"; import { updateApplication } from "redux/reduxActions/applicationActions"; -import { Button, Divider, Dropdown, Flex, Input, MenuProps, Space } from "antd"; +import { Button, Divider, Dropdown, Flex, Input, Menu, MenuProps, Space } from "antd"; import { Switch } from "antd"; import { saveCollisionStatus, @@ -223,19 +223,7 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { pos: index, }; }) - - dispatch( - updateApplication({ - applicationId: applicationId, - editingApplicationDSL: { - ...dsl, - ui: { - ...dsl.ui, - layout, - } - } as object, - }) - ); + editorState.rootComp.children.ui.dispatchChangeValueAction({ ...dsl.ui, layout, @@ -292,30 +280,32 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { return checkedKeys; } + useEffect(() => { + console.log('onChange', actionValue); + }, [actionValue]) + const getActionValue = () => { return actionValue; } - const handleComponentsActions = (actionType : string) => { + const handleComponentsActions = useCallback((actionType: string) => { + const value = getActionValue(); for (let key of getCheckedKeys()) { const node = getTreeNodeByKey(componentTreeData, key); const comp = editorState.getUICompByName(node.title); - if (comp != undefined) { - if (actionType === "hidden") { - comp.children.comp.dispatchChangeValueAction({ hidden: getActionValue() }); - } - else if (actionType === "disable") { - comp.children.comp.dispatchChangeValueAction({ disabled: getActionValue() }); - } - else if (actionType === "background") { - comp.children.comp.dispatchChangeValueAction({ BackgroundColor : getActionValue() }); + if(comp) { + const { children } = comp.children.comp + const types = actionType.split('.'); + + if(types.length === 1) { // e.g hidden, disabled + children[types[0]]?.dispatchChangeValueAction(value); } - else { - comp.children.comp.dispatchChangeValueAction({ actionType: getActionValue() }); + else if(types.length === 2) { // nested object e.g. style.background + children[types[0]][types[1]]?.dispatchChangeValueAction(value); } } } - } + }, [getActionValue, getCheckedKeys]); /* @@ -331,18 +321,15 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { const layerActions: ItemType[] = [ { label: 'Hide Component', - key: 'hide', - onClick: () => handleComponentsActions("hidden"), + key: 'hidden', }, { label: 'Disable Component', - key: 'background', - onClick: () => handleComponentsActions("disable"), + key: 'disable', }, { label: 'Component Background', - key: '3', - onClick: () => handleComponentsActions("background"), + key: 'style.background', }, { label: 'Unlock', @@ -396,7 +383,14 @@ export const LeftLayersContent = (props: LeftLayersContentProps) => { {trans("leftPanel.selectedComponents")}
{trans("leftPanel.displayComponents")} - + ( + handleComponentsActions(key)} + /> + )} + > + setActionValue(e.target.value)} // Handle changes to update actionValue + placeholder={placeholderText} + /> + +
+ handleColorChange(params.color, "style.background")} + /> + handleColorChange(params.color, "style.border")} + /> + handleColorChange(params.color, "style.text")} + /> diff --git a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx index 2188b4ea1..b1125d4c9 100644 --- a/client/packages/lowcoder/src/pages/editor/styledComponents.tsx +++ b/client/packages/lowcoder/src/pages/editor/styledComponents.tsx @@ -25,11 +25,17 @@ export const DirectoryTreeStyle = styled(DirectoryTree)` position: unset; .ant-tree-iconEle { width: 16px; - height: 26px; - margin-right: 4px; + height: 16px; + margin: 0px 0px 0px 4px; display: flex; align-items: center; + svg { + width: 16px; + height: 16px; + stroke: #000; + } } + } .ant-tree-checkbox+span { padding-left: 0;