diff --git a/apps/builder/package.json b/apps/builder/package.json index 7749b16239..d89c80757c 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -22,6 +22,8 @@ "@fontsource/fira-code": "^4.5.10", "@illa-design/react": "*", "@reduxjs/toolkit": "^1.8.1", + "@sentry/react": "^7.17.3", + "@sentry/tracing": "^7.17.3", "@uiw/react-color": "^1.1.0", "@uiw/react-color-sketch": "^1.1.0", "@uiw/react-color-swatch": "^1.1.0", @@ -53,7 +55,7 @@ "react-markdown": "^8.0.3", "react-redux": "^8.0.1", "react-rnd": "^10.3.7", - "react-router-dom": "^6.3.0", + "react-router-dom": "^6.4.3", "react-use": "^17.4.0", "react-use-measure": "^2.1.1", "redux": "^4.2.0", @@ -61,9 +63,7 @@ "remark-gfm": "^3.0.1", "tern": "^0.24.3", "toposort": "^2.0.2", - "uuid": "^8.3.2", - "@sentry/react": "^7.17.3", - "@sentry/tracing": "^7.17.3" + "uuid": "^8.3.2" }, "devDependencies": { "@changesets/changelog-github": "^0.4.2", diff --git a/apps/builder/src/App.tsx b/apps/builder/src/App.tsx index a2f7e6b210..38ff044f14 100644 --- a/apps/builder/src/App.tsx +++ b/apps/builder/src/App.tsx @@ -1,5 +1,5 @@ import { css, Global } from "@emotion/react" -import { BrowserRouter } from "react-router-dom" +import { BrowserRouter, RouterProvider } from "react-router-dom" import { globalStyle } from "./style" import { ConfigProvider } from "@illa-design/config-provider" import "@/api/base" @@ -27,16 +27,14 @@ function App() { }, [currentUserLanguage, i18n]) return ( - - - - - - - - - - + + + + + + + + ) } diff --git a/apps/builder/src/assets/change-layout.svg b/apps/builder/src/assets/change-layout.svg new file mode 100644 index 0000000000..9294895599 --- /dev/null +++ b/apps/builder/src/assets/change-layout.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/builder/src/assets/dataWorkspace/homepage.svg b/apps/builder/src/assets/dataWorkspace/homepage.svg new file mode 100644 index 0000000000..da1e1cab70 --- /dev/null +++ b/apps/builder/src/assets/dataWorkspace/homepage.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/builder/src/assets/rightPagePanel/frame-fixed.svg b/apps/builder/src/assets/rightPagePanel/frame-fixed.svg new file mode 100644 index 0000000000..2298559dc6 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/frame-fixed.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/builder/src/assets/rightPagePanel/frame-responsive.svg b/apps/builder/src/assets/rightPagePanel/frame-responsive.svg new file mode 100644 index 0000000000..aa6c483220 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/frame-responsive.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/default.svg b/apps/builder/src/assets/rightPagePanel/layout/default.svg new file mode 100644 index 0000000000..e353b1123d --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/default.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/preset-a.svg b/apps/builder/src/assets/rightPagePanel/layout/preset-a.svg new file mode 100644 index 0000000000..d9a9b884d0 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/preset-a.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/preset-b.svg b/apps/builder/src/assets/rightPagePanel/layout/preset-b.svg new file mode 100644 index 0000000000..b146795950 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/preset-b.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/preset-c.svg b/apps/builder/src/assets/rightPagePanel/layout/preset-c.svg new file mode 100644 index 0000000000..32a33e9db6 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/preset-c.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/preset-d.svg b/apps/builder/src/assets/rightPagePanel/layout/preset-d.svg new file mode 100644 index 0000000000..5cb292da22 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/preset-d.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/assets/rightPagePanel/layout/preset-e.svg b/apps/builder/src/assets/rightPagePanel/layout/preset-e.svg new file mode 100644 index 0000000000..e018d9ba17 --- /dev/null +++ b/apps/builder/src/assets/rightPagePanel/layout/preset-e.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/builder/src/components/PanelBar/index.tsx b/apps/builder/src/components/PanelBar/index.tsx index 36ef3b687b..c0cb9d7e9f 100644 --- a/apps/builder/src/components/PanelBar/index.tsx +++ b/apps/builder/src/components/PanelBar/index.tsx @@ -1,5 +1,6 @@ -import { FC, memo, useCallback, useState } from "react" +import { FC, memo, useCallback, useState, MouseEvent } from "react" import { + addIconHotpotStyle, applyPanelBarOpenedIconStyle, panelBarHeaderStyle, panelBarItemAnimation, @@ -8,7 +9,7 @@ import { } from "./style" import { PanelBarProps } from "./interface" import { AnimatePresence, motion } from "framer-motion" -import { UpIcon } from "@illa-design/icon" +import { PlusIcon, UpIcon } from "@illa-design/icon" export const PanelBar: FC = memo((props: PanelBarProps) => { const { @@ -17,6 +18,8 @@ export const PanelBar: FC = memo((props: PanelBarProps) => { isOpened = true, saveToggleState, onIllaFocus, + isAddIcon = false, + addAction, } = props const [isOpenedState, setIsOpenedState] = useState(isOpened) @@ -25,12 +28,23 @@ export const PanelBar: FC = memo((props: PanelBarProps) => { setIsOpenedState(!isOpenedState) }, [isOpenedState, saveToggleState]) + const handleClickAddIcon = (e: MouseEvent) => { + e.stopPropagation() + addAction?.() + } + return ( <>
{title} - + {isAddIcon ? ( +
+ +
+ ) : ( + + )}
diff --git a/apps/builder/src/components/PanelBar/interface.ts b/apps/builder/src/components/PanelBar/interface.ts index a523082c7b..eb92ab5902 100644 --- a/apps/builder/src/components/PanelBar/interface.ts +++ b/apps/builder/src/components/PanelBar/interface.ts @@ -4,6 +4,8 @@ export interface PanelBarProps { title: string children?: ReactNode isOpened?: boolean + isAddIcon?: boolean + addAction?: () => void saveToggleState?: (value: boolean) => void onIllaFocus?: () => void } diff --git a/apps/builder/src/components/PanelBar/style.ts b/apps/builder/src/components/PanelBar/style.ts index df1d88694e..d9817b59b3 100644 --- a/apps/builder/src/components/PanelBar/style.ts +++ b/apps/builder/src/components/PanelBar/style.ts @@ -48,3 +48,19 @@ export const panelBarItemAnimation: Variants = { enter: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, } + +export const addIconHotpotStyle = css` + font-size: 12px; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; + :hover { + color: ${globalColor(`--${illaPrefix}-grayBlue-02`)}; + } + :active { + color: ${globalColor(`--${illaPrefix}-grayBlue-02`)}; + } +` diff --git a/apps/builder/src/config/newAppConfig.tsx b/apps/builder/src/config/newAppConfig.tsx new file mode 100644 index 0000000000..6c216e88db --- /dev/null +++ b/apps/builder/src/config/newAppConfig.tsx @@ -0,0 +1,107 @@ +import { + CONTAINER_TYPE, + PageNode, + SectionNode, + SECTION_POSITION, +} from "@/redux/currentApp/editor/components/componentsState" +import { v4 } from "uuid" + +export const BASIC_BODY_SECTION_CONFIG: SectionNode = { + displayName: "bodySection1", + parentNode: "page1", + showName: "bodySection", + isDragging: false, + isResizing: false, + type: "SECTION_NODE", + containerType: CONTAINER_TYPE.EDITOR_LAYOUT_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + currentViewIndex: 0, + viewSortedKey: ["bodySection1-bodySectionContainer1"], + defaultViewKey: "View 1", + sectionViewConfigs: [ + { + id: v4(), + viewDisplayName: "bodySection1-bodySectionContainer1", + key: "View 1", + path: "View 1", + }, + ], + }, + childrenNode: [ + { + displayName: "bodySection1-bodySectionContainer1", + parentNode: "bodySection", + showName: "bodySection1-bodySectionContainer1", + isDragging: false, + isResizing: false, + type: "CONTAINER_NODE", + containerType: CONTAINER_TYPE.EDITOR_DOT_PANEL, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + childrenNode: [], + props: {}, + }, + ], +} + +export const BASIC_APP_CONFIG: PageNode[] = [ + { + displayName: "page1", + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "default", + leftPosition: SECTION_POSITION.NONE, + rightPosition: SECTION_POSITION.NONE, + hasFooter: false, + hasHeader: false, + hasLeft: false, + hasRight: false, + leftWidth: 0, + rightWidth: 0, + topHeight: 0, + bottomHeight: 0, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [BASIC_BODY_SECTION_CONFIG], + }, +] diff --git a/apps/builder/src/i18n/locale/en-US.json b/apps/builder/src/i18n/locale/en-US.json index 30120ddf35..3dea468a48 100644 --- a/apps/builder/src/i18n/locale/en-US.json +++ b/apps/builder/src/i18n/locale/en-US.json @@ -6,10 +6,10 @@ "btn": { "choose_file": "Choose a file" }, - "collision_message": "Press D or K and drag the component into container", - "create_fail": "Failed to create", "change_fail": "Failed to change", "change_success": "Change successfully", + "collision_message": "Press D or K and drag the component into container", + "create_fail": "Failed to create", "create_new": "Create New", "create_new_app": "Create New", "created_at": "Created at", @@ -140,9 +140,6 @@ "required": "{name} is required" } }, - "resource_choose": { - "deleted": "Deleted" - }, "panel": { "btn": { "disable": "Disable", @@ -204,30 +201,30 @@ "ca_certificate": "CA Certificate", "client_certificate": "Client Certificate", "client_key": "Client Key", + "config_type": "Config Type", "connect_type": "Connect Type", "connection_format": "Connection format", + "connection_string": "Connection String", "database": "Database", "database_index": "Database Index", "hostname": "Hostname", "hostname_port": "Hostname/Port", - "mongodb_ssl_client": "Client Cert and Key", - "mongodb_ssl_ca": "CA Cert", "mongodb_connection_dns_seed_list": "DNS seed list connection", "mongodb_connection_standard": "Standard Connection", + "mongodb_ssl_ca": "CA Cert", + "mongodb_ssl_client": "Client Cert and Key", "name": "Name", "port": "Port", - "config_type": "Config Type", - "connection_string": "Connection String", "private_key": "Private Key", "ssl_options": "SSL options", "username_password": "Username/Password" }, "placeholder": { - "mongo_certificate": "-----BEGIN RSA PRIVATE KEY-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----\n", "certificate": "-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----", "database": "acme_production", "database_index": "default 0", "hostname": "Hostname", + "mongo_certificate": "-----BEGIN RSA PRIVATE KEY-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----\n", "name": "i.e.\"Users DB(readonly)\" or \"Internal Admin API\"", "password": "Password", "username": "Username" @@ -319,6 +316,9 @@ } } }, + "resource_choose": { + "deleted": "Deleted" + }, "result": { "title": { "api_request": "API Request", @@ -348,7 +348,8 @@ "data_work_space": { "actions_title": "ACTIONS & TRANSFORMERS ", "components_title": "COMPONENTS ", - "globals_title": "GLOBALS " + "globals_title": "GLOBALS ", + "pages_title": "PAGES" }, "deploy": { "fail": "Failed to deploy" @@ -377,12 +378,6 @@ "prefabricated": "Prefabricated color", "title": "Edit color" }, - "view_setter": { - "views": "Views" - }, - "tabs_setter": { - "tabs": "Tabs" - }, "column_setter": { "label": "Columns ({{number}})", "new": "New", @@ -407,6 +402,18 @@ "new": "New", "title": "Options" }, + "select_page_setter": { + "placeholder": "Select a page" + }, + "select_view_setter": { + "placeholder": "" + }, + "tabs_setter": { + "tabs": "Tabs" + }, + "view_setter": { + "views": "Views" + }, "widget_action_type_name": { "blur": "Blur", "change": "Change", @@ -494,9 +501,9 @@ "decimal_places": "Decimal places", "default_sort_key": "Column", "default_sort_order": "Direction", + "default_tab": "Default tab", "default_value": "Default value", "default_views": "Default view", - "default_tab": "Default tab", "description": "Description", "direction": "Direction", "disable_submit": "Disable submit", @@ -535,8 +542,8 @@ "label_width": "Width(%)", "layout": "Layout", "legend_position": "Legend position", - "links_color": "Links", "link_to_container": "Link to a container", + "links_color": "Links", "loading": "Loading", "loop_back_to_start": "Loop back to start", "loop_start_to_back": "Loop start to back", @@ -553,9 +560,10 @@ "new_tab": "New tab", "only_run_when": "Only run when", "overFlow": "OverFlow", + "page": "Page", + "pageSize": "PageSize", "pattern": "Pattern", "pending": "Pending", - "pageSize": "PageSize", "placeholder": "Placeholder", "prefix_text": "Prefix text", "radius": "Radius", @@ -563,8 +571,10 @@ "regex": "Regex", "required_field": "Required field", "reset_after_successful_submit": "Reset after successful submit", + "router": "Router", "run_script": "Run script", "scale_type": "Scale Type", + "set_router": "Set router", "set_temporary_state": "Set temporary state", "shadow": "Shadow", "show_character_count": "Show character count", @@ -637,6 +647,40 @@ "unselected_tip1": "No components selected.", "unselected_tip2": "Click on a component to select it." }, + "page": { + "label_name": { + "add": "Add", + "body": "Body", + "default_view": "Default view", + "edit_view": "Edit view", + "footer": "Footer", + "header": "Header", + "key": "Key", + "left_panel": "Left panel", + "preset": "Preset", + "right_panel": "Right panel", + "set_as_homepage": "Set as homepage", + "show_fold_icon": "Show fold icon", + "view_path": "View path", + "views": "Views", + "width": "Width" + }, + "model_tips": { + "cancel_button": "Cancel", + "change_layout_message": "After changing the layout, all data will be cleared. This operation can't be recovered, please confirm", + "delete_section_message": "After hiding, the data in this section will be cleared. This operation can't be recovered, please confirm", + "ok_button": "Confirm" + }, + "panel_bar_title": { + "basic": "BASIC", + "frame": "FRAME" + }, + "tab_title": "Page", + "tooltips": { + "show_fold_icon": "Show a button to collapse or expand the panel", + "view_path": "A string after example.com/app/page/ to access this view" + } + }, "validate_message": { "email": "Please enter a valid email address", "max_length": "Must be no more than {{ number }} characters", @@ -934,9 +978,9 @@ "text_model": "Plain text" }, "timeline": { + "horizontal": "horizontal", "name": "Timeline", - "vertical": "vertical", - "horizontal": "horizontal" + "vertical": "vertical" } } } diff --git a/apps/builder/src/i18n/locale/zh-CN.json b/apps/builder/src/i18n/locale/zh-CN.json index b6b65aec7a..5c1030bca1 100644 --- a/apps/builder/src/i18n/locale/zh-CN.json +++ b/apps/builder/src/i18n/locale/zh-CN.json @@ -6,10 +6,10 @@ "btn": { "choose_file": "选择文件" }, - "collision_message": "按住D或K键,将组件拖进容器", - "create_fail": "创建失败", "change_fail": "修改失败", "change_success": "修改成功", + "collision_message": "按住D或K键,将组件拖进容器", + "create_fail": "创建失败", "create_new": "创建", "create_new_app": "创建项目", "created_at": "创建于", @@ -140,9 +140,6 @@ "required": "请输入{name}" } }, - "resource_choose": { - "deleted": "已删除" - }, "panel": { "btn": { "disable": "关闭", @@ -204,30 +201,30 @@ "ca_certificate": "服务端证书", "client_certificate": "客户端证书", "client_key": "客户端密钥", + "config_type": "配置类型", "connect_type": "连接类型", "connection_format": "连接方式", + "connection_string": "连接地址", "database": "数据库名称", "database_index": "数据库索引", "hostname": "主机", "hostname_port": "主机/端口", - "mongodb_ssl_client": "Client Cert and Key", - "mongodb_ssl_ca": "CA Cert", "mongodb_connection_dns_seed_list": "DNS seed list connection", "mongodb_connection_standard": "Standard Connection", + "mongodb_ssl_ca": "CA Cert", + "mongodb_ssl_client": "Client Cert and Key", "name": "名称", "port": "端口", - "config_type": "配置类型", - "connection_string": "连接地址", "private_key": "私钥", "ssl_options": "SSL 选项", "username_password": "用户名/密码" }, "placeholder": { - "mongo_certificate": "-----BEGIN RSA PRIVATE KEY-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----\n", "certificate": "-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----", "database": "acme_production", "database_index": "默认 0", "hostname": "主机名", + "mongo_certificate": "-----BEGIN RSA PRIVATE KEY-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIEMDCCApigAwIBAgIDI2GWMA0GCSqGSIb3DQEBDAUAMDoxODA2BgNVBAMML2Fm\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\nDTE5MDQwODAzNDIyMloXDTI5MDQwNTAzNDIyMlowOjE4MDYGA1UEAwwvYWY1ZjU4\n...\n-----END CERTIFICATE-----\n", "name": "i.e.\"Users DB(readonly)\" or \"Internal Admin API\"", "password": "密码", "username": "用户名" @@ -319,6 +316,9 @@ } } }, + "resource_choose": { + "deleted": "已删除" + }, "result": { "title": { "api_request": "API 请求参数", @@ -348,7 +348,8 @@ "data_work_space": { "actions_title": "ACTIONS 列表 ", "components_title": "组件列表 ", - "globals_title": "全局变量 " + "globals_title": "全局变量 ", + "pages_title": "页面列表" }, "deploy": { "fail": "部署失败" @@ -377,12 +378,6 @@ "prefabricated": "Prefabricated color", "title": "Edit color" }, - "view_setter": { - "views": "Views" - }, - "tabs_setter": { - "tabs": "Tabs" - }, "column_setter": { "label": "列 ({{number}})", "new": "新建", @@ -407,6 +402,18 @@ "new": "新建", "title": "选项" }, + "select_page_setter": { + "placeholder": "选择一个页面" + }, + "select_view_setter": { + "placeholder": "选择一个视图路径" + }, + "tabs_setter": { + "tabs": "Tabs" + }, + "view_setter": { + "views": "Views" + }, "widget_action_type_name": { "blur": "失焦", "change": "内容改变", @@ -494,9 +501,9 @@ "decimal_places": "小数位数", "default_sort_key": "排序列", "default_sort_order": "顺序", + "default_tab": "默认值", "default_value": "默认值", "default_views": "默认视图", - "default_tab": "默认值", "description": "描述", "direction": "方向", "disable_submit": "禁用提交", @@ -535,8 +542,8 @@ "label_width": "宽度(%)", "layout": "布局", "legend_position": "图例位置", - "links_color": "链接色", "link_to_container": "是否关联到容器", + "links_color": "链接色", "loading": "加载中", "loop_back_to_start": "列表循环", "loop_start_to_back": "列表循环", @@ -553,9 +560,10 @@ "new_tab": "在新标签中打开", "only_run_when": "先决条件", "overFlow": "翻页方式", + "page": "页面", + "pageSize": "每页行数", "pattern": "模式", "pending": "待处理节点", - "pageSize": "每页行数", "placeholder": "占位符", "prefix_text": "前缀文本", "radius": "圆角", @@ -563,8 +571,10 @@ "regex": "正则表达式", "required_field": "必需字段", "reset_after_successful_submit": "提交成功后重置输入内容", + "router": "路由", "run_script": "运行脚本", "scale_type": "缩放类型", + "set_router": "设置路由", "set_temporary_state": "设置临时状态", "shadow": "阴影", "show_character_count": "展示字符数量", @@ -637,6 +647,40 @@ "unselected_tip1": "没有组件被选中。", "unselected_tip2": "请点击组件以选中。" }, + "page": { + "label_name": { + "add": "新建", + "body": "主体", + "default_view": "默认视图", + "edit_view": "编辑视图", + "footer": "页脚", + "header": "页头", + "key": "键值", + "left_panel": "左侧面板", + "preset": "预设", + "right_panel": "右侧面板", + "set_as_homepage": "设置为主页", + "show_fold_icon": "显示折叠按钮", + "view_path": "视图路径", + "views": "视图", + "width": "宽度" + }, + "model_tips": { + "cancel_button": "取消", + "change_layout_message": "改变布局将清空所有数据,该操作无法恢复,请确认", + "delete_section_message": "隐藏后,这个部分的数据会被清空,该操作无法恢复,请确认", + "ok_button": "确认" + }, + "panel_bar_title": { + "basic": "基础", + "frame": "画布" + }, + "tab_title": "页面", + "tooltips": { + "show_fold_icon": "显示一个按钮,用于收起或展开面板", + "view_path": "一个拼example.com/app/page/后面的字符串,用来访问这个视图" + } + }, "validate_message": { "email": "请输入有效的电子邮箱", "max_length": "请输入最多{{ number }}个字符", @@ -934,9 +978,9 @@ "text_model": "纯文本" }, "timeline": { + "horizontal": "横向", "name": "时间线", - "vertical": "纵向", - "horizontal": "横向" + "vertical": "纵向" } } } diff --git a/apps/builder/src/middleware/redux/reduxAsync.ts b/apps/builder/src/middleware/redux/reduxAsync.ts index c8d8ea0374..b44f02b5a1 100644 --- a/apps/builder/src/middleware/redux/reduxAsync.ts +++ b/apps/builder/src/middleware/redux/reduxAsync.ts @@ -17,6 +17,14 @@ import { ComponentNode, CopyComponentPayload, sortComponentNodeChildrenPayload, + UpdateTargetPageLayoutPayload, + AddTargetPageSectionPayload, + DeleteTargetPageSectionPayload, + UpdateTargetPagePropsPayload, + DeletePageNodePayload, + AddSectionViewPayload, + DeleteSectionViewPayload, + UpdateSectionViewPropsPayload, } from "@/redux/currentApp/editor/components/componentsState" import { UpdateComponentContainerPayload, @@ -65,9 +73,9 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { ) break case "copyComponentReducer": - const copyComponentPayload = ( - payload as CopyComponentPayload[] - ).map((copyShape) => copyShape.newComponentNode) + const copyComponentPayload = (payload as CopyComponentPayload[]).map( + (copyShape) => copyShape.newComponentNode, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_CREATE_STATE, @@ -83,10 +91,9 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { break case "updateComponentsShape": const singleComponentPayload: UpdateComponentsShapePayload = payload - const singleComponentWSPayload = - transformComponentReduxPayloadToWsPayload( - singleComponentPayload.components, - ) + const singleComponentWSPayload = transformComponentReduxPayloadToWsPayload( + singleComponentPayload.components, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( singleComponentPayload.isMove @@ -103,12 +110,10 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { ) break case "updateComponentReflowReducer": - const updateComponentReflowPayload: UpdateComponentReflowPayload = - payload - const updateComponentReflowWSPayload = - transformComponentReduxPayloadToWsPayload( - updateComponentReflowPayload.childNodes, - ) + const updateComponentReflowPayload: UpdateComponentReflowPayload = payload + const updateComponentReflowWSPayload = transformComponentReduxPayloadToWsPayload( + updateComponentReflowPayload.childNodes, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_UPDATE_STATE, @@ -123,12 +128,10 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { ) break case "sortComponentNodeChildrenReducer": - const sortComponentNodeChildrenPayload: sortComponentNodeChildrenPayload = - payload - const sortComponentNodeChildrenWSPayload = - transformComponentReduxPayloadToWsPayload( - sortComponentNodeChildrenPayload.newChildrenNode, - ) + const sortComponentNodeChildrenPayload: sortComponentNodeChildrenPayload = payload + const sortComponentNodeChildrenWSPayload = transformComponentReduxPayloadToWsPayload( + sortComponentNodeChildrenPayload.newChildrenNode, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_MOVE_STATE, @@ -143,12 +146,10 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { ) break case "updateComponentContainerReducer": - const updateComponentContainerPayload: UpdateComponentContainerPayload = - payload - const componentNodes = - updateComponentContainerPayload.updateSlice.map( - (slice) => slice.component, - ) + const updateComponentContainerPayload: UpdateComponentContainerPayload = payload + const componentNodes = updateComponentContainerPayload.updateSlice.map( + (slice) => slice.component, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_MOVE_STATE, @@ -199,8 +200,9 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { }) .filter((node) => node !== null) as ComponentNode[] if (Array.isArray(finalNodes)) { - const wsPayload = - transformComponentReduxPayloadToWsPayload(finalNodes) + const wsPayload = transformComponentReduxPayloadToWsPayload( + finalNodes, + ) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_UPDATE_STATE, @@ -249,13 +251,19 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { ) break case "updateComponentDisplayNameReducer": - const { displayName, newDisplayName } = - action.payload as UpdateComponentDisplayNamePayload - const findOldNode = searchDsl( - getCanvas(store.getState()), + const { + displayName, newDisplayName, - ) + } = action.payload as UpdateComponentDisplayNamePayload + const canvasNode = getCanvas(store.getState()) + const findOldNode = searchDsl(canvasNode, newDisplayName) if (!findOldNode) break + const parentNode = searchDsl(canvasNode, findOldNode.parentNode) + if (!parentNode) break + const WSPayload = transformComponentReduxPayloadToWsPayload([ + parentNode, + ...findOldNode.childrenNode, + ]) Connection.getRoom("app", currentAppID)?.send( getPayload( Signal.SIGNAL_UPDATE_STATE, @@ -275,9 +283,306 @@ export const reduxAsync: Redux.Middleware = (store) => (next) => (action) => { displayName: newDisplayName, }, }, + ...WSPayload, ], ), ) + break + case "updateTargetPageLayoutReducer": { + const { pageName } = action.payload as UpdateTargetPageLayoutPayload + const canvasNode = getCanvas(store.getState()) + const pageNode = searchDsl(canvasNode, pageName) + if (!pageNode) break + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_DELETE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + [pageName], + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_CREATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + [pageNode], + ), + ) + break + } + case "deleteTargetPageSectionReducer": { + const { + pageName, + deleteSectionName, + } = action.payload as DeleteTargetPageSectionPayload + const finalNode = searchDsl(getCanvas(store.getState()), pageName) + + if (!finalNode) break + const WSPayload = transformComponentReduxPayloadToWsPayload( + finalNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_DELETE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + [deleteSectionName], + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + WSPayload, + ), + ) + break + } + case "addTargetPageSectionReducer": { + const { + pageName, + addedSectionName, + } = action.payload as AddTargetPageSectionPayload + const pageNode = searchDsl(getCanvas(store.getState()), pageName) + if (!pageNode) break + const addSectionNode = pageNode.childrenNode.find( + (node) => node.showName === addedSectionName, + ) + if (!addSectionNode) break + + const WSPagePayload = transformComponentReduxPayloadToWsPayload( + pageNode, + ) + + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + WSPagePayload, + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_CREATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + [addSectionNode], + ), + ) + break + } + case "updateTargetPagePropsReducer": { + const { pageName } = action.payload as UpdateTargetPagePropsPayload + const pageNode = searchDsl(getCanvas(store.getState()), pageName) + + if (!pageNode) break + const WSPagePayload = transformComponentReduxPayloadToWsPayload( + pageNode, + ) + + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + WSPagePayload, + ), + ) + break + } + case "updateRootNodePropsReducer": { + const rootNode = getCanvas(store.getState()) + if (!rootNode) break + const WSPagePayload = transformComponentReduxPayloadToWsPayload( + rootNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + WSPagePayload, + ), + ) + break + } + case "addPageNodeWithSortOrderReducer": { + const rootNode = getCanvas(store.getState()) + const nodes = action.payload as ComponentNode[] + if (!rootNode || !Array.isArray(nodes) || nodes.length === 0) break + const rootNodeUpdateWSPayload = transformComponentReduxPayloadToWsPayload( + rootNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + rootNodeUpdateWSPayload, + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_CREATE_STATE, + Target.TARGET_COMPONENTS, + true, + { type, payload }, + nodes, + ), + ) + break + } + case "deletePageNodeReducer": { + const deletePayload = payload as DeletePageNodePayload + const rootNode = getCanvas(store.getState()) + if (!rootNode) break + const rootNodeUpdateWSPayload = transformComponentReduxPayloadToWsPayload( + rootNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + rootNodeUpdateWSPayload, + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_DELETE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + [deletePayload.displayName], + ), + ) + break + } + case "addSectionViewReducer": { + const { + parentNodeName, + containerNode, + } = payload as AddSectionViewPayload + const rootNode = getCanvas(store.getState()) + + if (!rootNode) break + const targetNode = searchDsl(rootNode, parentNodeName) + if (!targetNode) break + const updateWSPayload = transformComponentReduxPayloadToWsPayload( + targetNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + updateWSPayload, + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_CREATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + [containerNode], + ), + ) + break + } + case "deleteSectionViewReducer": { + const { + viewDisplayName, + parentNodeName, + } = payload as DeleteSectionViewPayload + const rootNode = getCanvas(store.getState()) + if (!rootNode) break + const targetNode = searchDsl(rootNode, parentNodeName) + if (!targetNode) break + const updateWSPayload = transformComponentReduxPayloadToWsPayload( + targetNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_DELETE_STATE, + Target.TARGET_COMPONENTS, + true, + null, + [viewDisplayName], + ), + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + updateWSPayload, + ), + ) + break + } + case "updateSectionViewPropsReducer": { + const { parentNodeName } = payload as UpdateSectionViewPropsPayload + const rootNode = getCanvas(store.getState()) + if (!rootNode) break + const targetNode = searchDsl(rootNode, parentNodeName) + if (!targetNode) break + const updateWSPayload = transformComponentReduxPayloadToWsPayload( + targetNode, + ) + Connection.getRoom("app", currentAppID)?.send( + getPayload( + Signal.SIGNAL_UPDATE_STATE, + Target.TARGET_COMPONENTS, + true, + { + type, + payload, + }, + updateWSPayload, + ), + ) + break + } } break case "dragShadow": diff --git a/apps/builder/src/page/App/components/Actions/ActionPanel/RestApiPanel/BodyEditor/index.tsx b/apps/builder/src/page/App/components/Actions/ActionPanel/RestApiPanel/BodyEditor/index.tsx index 262f69aab0..bbd036856f 100644 --- a/apps/builder/src/page/App/components/Actions/ActionPanel/RestApiPanel/BodyEditor/index.tsx +++ b/apps/builder/src/page/App/components/Actions/ActionPanel/RestApiPanel/BodyEditor/index.tsx @@ -101,8 +101,6 @@ export const BodyEditor: FC = (props) => { } } - console.log("longbo", newBody) - dispatch( configActions.updateCachedAction({ ...actionItem, diff --git a/apps/builder/src/page/App/components/CanvasPanel/index.tsx b/apps/builder/src/page/App/components/CanvasPanel/index.tsx index 6610fbeb60..c292684ee8 100644 --- a/apps/builder/src/page/App/components/CanvasPanel/index.tsx +++ b/apps/builder/src/page/App/components/CanvasPanel/index.tsx @@ -1,29 +1,22 @@ -import { FC, ReactNode } from "react" +import { FC } from "react" import { useTranslation } from "react-i18next" import { applyScaleContainerStyle, - previewStyle, messageStyle, messageWrapperStyle, } from "./style" import { CanvasPanelProps } from "./interface" import { DotPanel } from "@/page/App/components/DotPanel" import { useDispatch, useSelector } from "react-redux" -import { getCanvas } from "@/redux/currentApp/editor/components/componentsSelector" -import { ComponentNode } from "@/redux/currentApp/editor/components/componentsState" -import { FullScreenIcon, LockIcon } from "@illa-design/icon" -import { Button } from "@illa-design/button" +import { LockIcon } from "@illa-design/icon" import { getFreezeState, getIllaMode } from "@/redux/config/configSelector" -import { configActions } from "@/redux/config/configSlice" import { FocusManager } from "@/utils/focusManager" export const CanvasPanel: FC = (props) => { const { ...otherProps } = props const { t } = useTranslation() - const canvasTree = useSelector(getCanvas) const mode = useSelector(getIllaMode) - const dispatch = useDispatch() const isFreeze = useSelector(getFreezeState) return ( @@ -34,20 +27,9 @@ export const CanvasPanel: FC = (props) => { FocusManager.switchFocus("canvas") }} > - {applyCanvasTree(canvasTree)} + {mode === "edit" && ( <> - {/*TODO: replace this to illa-design/Message,when Message is ok*/} {isFreeze ? (
@@ -65,24 +47,4 @@ export const CanvasPanel: FC = (props) => { ) } -// current root must be dot panel -function applyCanvasTree( - componentNode: ComponentNode | null, -): ReactNode | null { - if (componentNode == null) { - return null - } - switch (componentNode.containerType) { - case "EDITOR_DOT_PANEL": - return ( - - ) - default: - return null - } -} - CanvasPanel.displayName = "CanvasPanel" diff --git a/apps/builder/src/page/App/components/CanvasPanel/style.ts b/apps/builder/src/page/App/components/CanvasPanel/style.ts index e6858574d2..2e266d75db 100644 --- a/apps/builder/src/page/App/components/CanvasPanel/style.ts +++ b/apps/builder/src/page/App/components/CanvasPanel/style.ts @@ -14,14 +14,6 @@ export function applyScaleContainerStyle(scale: number): SerializedStyles { ` } -export const previewStyle = css` - position: absolute; - z-index: 100; - box-shadow: 0 0 6px 0 ${globalColor(`--${illaPrefix}-blackAlpha-06`)}; - bottom: 8px; - right: 16px; -` - export const messageWrapperStyle = css` top: 16px; position: absolute; diff --git a/apps/builder/src/page/App/components/ComponentManager/index.tsx b/apps/builder/src/page/App/components/ComponentManager/index.tsx index 240dd87f63..991619ea1f 100644 --- a/apps/builder/src/page/App/components/ComponentManager/index.tsx +++ b/apps/builder/src/page/App/components/ComponentManager/index.tsx @@ -1,12 +1,13 @@ import { TabPane, Tabs } from "@illa-design/tabs" import { useTranslation } from "react-i18next" -import { FC, HTMLAttributes, useEffect, useState } from "react" +import { FC, HTMLAttributes, useEffect, useRef, useState } from "react" import { useSelector } from "react-redux" import { getSelectedComponents } from "@/redux/config/configSelector" import { componentPanelCss } from "./style" import { FocusManager } from "@/utils/focusManager" import { ConfigPanel } from "@/page/App/components/ConfigPanel" import { ComponentPanel } from "@/page/App/components/ComponentPanel" +import { PagePanel } from "@/page/App/components/PagePanel" export const ComponentsManager: FC> = ( props, @@ -16,14 +17,23 @@ export const ComponentsManager: FC> = ( const [activeKey, setActiveKey] = useState("Insert") const selectedDisplayNames = useSelector(getSelectedComponents) + const isClickChange = useRef(false) useEffect(() => { - if (selectedDisplayNames.length > 0) { - setActiveKey("Inspect") - } else { - setActiveKey("Insert") + if (!isClickChange.current) { + if (selectedDisplayNames.length > 0) { + setActiveKey("Inspect") + } else { + if (activeKey === "Page") { + setActiveKey("Page") + } + if (activeKey === "Inspect") { + setActiveKey("Insert") + } + } } - }, [selectedDisplayNames]) + isClickChange.current = false + }, [activeKey, selectedDisplayNames]) return (
> = ( activeKey={activeKey} colorScheme="grayBlue" onChange={(key) => { + isClickChange.current = true setActiveKey(key) }} > + + + diff --git a/apps/builder/src/page/App/components/ComponentPanel/ComponentItem.tsx b/apps/builder/src/page/App/components/ComponentPanel/ComponentItem.tsx index da290e65ca..702ad5eee9 100644 --- a/apps/builder/src/page/App/components/ComponentPanel/ComponentItem.tsx +++ b/apps/builder/src/page/App/components/ComponentPanel/ComponentItem.tsx @@ -54,6 +54,7 @@ export const ComponentItem: FC = memo( return { item, childrenNodes, + currentColumnNumber: 64, } }, }), diff --git a/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/index.tsx b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/index.tsx new file mode 100644 index 0000000000..8642ddbb67 --- /dev/null +++ b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/index.tsx @@ -0,0 +1,102 @@ +import { FC, useCallback } from "react" +import { PanelBar } from "@/components/PanelBar" +import { useTranslation } from "react-i18next" +import { + homePageIconHotSpot, + homePageIconStyle, + pageItemWrapperStyle, + pageNameStyle, +} from "./style" +import { PageItemProps } from "./interface" +import { ReactComponent as HomepageIcon } from "@/assets/dataWorkspace/homepage.svg" +import { useDispatch, useSelector } from "react-redux" +import { getRootNodeExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { generatePageConfig } from "@/utils/generators/generatePageOrSectionConfig" +import { componentsActions } from "@/redux/currentApp/editor/components/componentsSlice" +import { RootComponentNodeProps } from "@/redux/currentApp/editor/components/componentsState" +import { executionActions } from "@/redux/currentApp/executionTree/executionSlice" +import { Dropdown } from "@illa-design/dropdown" +import { ActionMenu } from "@/page/App/components/PagePanel/Components/PanelHeader/actionMenu" + +export const PageItem: FC = (props) => { + const { + isHomePage = false, + isSelected = false, + pageName, + index, + changeCurrentPageIndex, + allKeys, + } = props + return ( + } + > +
{ + changeCurrentPageIndex(index) + }} + > + {pageName} +
+ {isHomePage && } +
+
+
+ ) +} + +export const PageSpaceTree: FC = () => { + const { t } = useTranslation() + const rootNodeProps = useSelector( + getRootNodeExecutionResult, + ) as RootComponentNodeProps + const { pageSortedKey, currentPageIndex, homepageDisplayName } = rootNodeProps + const dispatch = useDispatch() + const handleClickAddButton = useCallback(() => { + const newPageConfig = generatePageConfig() + dispatch(componentsActions.addPageNodeWithSortOrderReducer([newPageConfig])) + }, [dispatch]) + const changeCurrentPage = useCallback( + (index: number) => { + if (index === currentPageIndex) return + dispatch( + executionActions.updateExecutionByDisplayNameReducer({ + displayName: "root", + value: { + currentPageIndex: index, + }, + }), + ) + }, + [currentPageIndex, dispatch], + ) + return ( + + {Array.isArray(pageSortedKey) && + pageSortedKey.map((key: string, index: number) => { + const isSelected = currentPageIndex === index + const isHomePage = homepageDisplayName + ? homepageDisplayName === key + : index === 0 + return ( + + ) + })} + + ) +} diff --git a/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/interface.ts b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/interface.ts new file mode 100644 index 0000000000..2e0e8321af --- /dev/null +++ b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/interface.ts @@ -0,0 +1,8 @@ +export interface PageItemProps { + isHomePage?: boolean + isSelected?: boolean + pageName: string + index: number + allKeys: string[] + changeCurrentPageIndex: (index: number) => void +} diff --git a/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/style.ts b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/style.ts new file mode 100644 index 0000000000..fcd15ea22b --- /dev/null +++ b/apps/builder/src/page/App/components/DataWorkspace/components/PageSpaceTree/style.ts @@ -0,0 +1,39 @@ +import { css } from "@emotion/react" +import { globalColor, illaPrefix } from "@illa-design/react" + +export const pageNameStyle = css` + max-width: 186px; + font-size: 12px; + font-weight: 500; +` + +export const homePageIconHotSpot = css` + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; +` + +export const homePageIconStyle = css` + font-size: 12px; + width: 12px; + height: 12px; + flex: none; +` + +export const pageItemWrapperStyle = (isSelected: boolean) => { + return css` + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 16px; + width: 100%; + height: 24px; + cursor: pointer; + font-family: "Fira Code", monospace; + background-color: ${isSelected + ? globalColor(`--${illaPrefix}-techPurple-07`) + : globalColor(`--${illaPrefix}-white-01`)}; + ` +} diff --git a/apps/builder/src/page/App/components/DataWorkspace/index.tsx b/apps/builder/src/page/App/components/DataWorkspace/index.tsx index 3b0af1fac0..a1f24a4f0e 100644 --- a/apps/builder/src/page/App/components/DataWorkspace/index.tsx +++ b/apps/builder/src/page/App/components/DataWorkspace/index.tsx @@ -15,6 +15,7 @@ import { getActionList } from "@/redux/currentApp/action/actionSelector" import { getGlobalInfoExecutionResult } from "@/redux/currentUser/currentUserSelector" import { FocusManager } from "@/utils/focusManager" import { cloneDeep } from "lodash" +import { PageSpaceTree } from "./components/PageSpaceTree" interface DataWorkspaceProps extends HTMLAttributes {} @@ -96,6 +97,7 @@ export const DataWorkspace: FC = (props) => { return (
+ { + return ( + (x === -1 && y === -1) || oldParentDisplayName !== currentParentDisplayName + ) +} diff --git a/apps/builder/src/page/App/components/DotPanel/changeLayoutBar.tsx b/apps/builder/src/page/App/components/DotPanel/changeLayoutBar.tsx new file mode 100644 index 0000000000..e79bddd259 --- /dev/null +++ b/apps/builder/src/page/App/components/DotPanel/changeLayoutBar.tsx @@ -0,0 +1,77 @@ +import { FC } from "react" +import { + changeLayoutBottomBarWrapperStyle, + changeLayoutBottomIconStyle, + changeLayoutHorizontalBarStyle, + changeLayoutLeftBarWrapperStyle, + changeLayoutLeftIconStyle, + changeLayoutRightBarWrapperStyle, + changeLayoutRightIconStyle, + changeLayoutTopBarWrapperStyle, + changeLayoutVerticalBarStyle, +} from "./style" +import { ReactComponent as ChangeLayout } from "@/assets/change-layout.svg" +import { ChangeLayoutBarProps } from "./interface" + +export const ChangeLayoutTopBar: FC = (props) => { + const { sectionName, changeAction, direction } = props + + return ( +
{ + changeAction(sectionName, direction) + }} + > + +
+
+ ) +} + +export const ChangeLayoutBottomBar: FC = (props) => { + const { sectionName, changeAction, direction } = props + + return ( +
{ + changeAction(sectionName, direction) + }} + > + +
+
+ ) +} + +export const ChangeLayoutLeftBar: FC = (props) => { + const { sectionName, changeAction, direction } = props + return ( +
{ + changeAction(sectionName, direction) + }} + > + +
+
+ ) +} + +export const ChangeLayoutRightBar: FC = (props) => { + const { sectionName, changeAction, direction } = props + + return ( +
{ + changeAction(sectionName, direction) + }} + > + +
+
+ ) +} diff --git a/apps/builder/src/page/App/components/DotPanel/context/resizeContext.tsx b/apps/builder/src/page/App/components/DotPanel/context/resizeContext.tsx new file mode 100644 index 0000000000..2d87627882 --- /dev/null +++ b/apps/builder/src/page/App/components/DotPanel/context/resizeContext.tsx @@ -0,0 +1,5 @@ +import { createContext } from "react" + +interface ContextShape {} + +export const ResizeContext = createContext` ` diff --git a/apps/builder/src/page/App/components/DotPanel/index.tsx b/apps/builder/src/page/App/components/DotPanel/index.tsx index bccb39d3c7..3d3ff6fd81 100644 --- a/apps/builder/src/page/App/components/DotPanel/index.tsx +++ b/apps/builder/src/page/App/components/DotPanel/index.tsx @@ -1,31 +1,55 @@ -import { FC, useRef } from "react" -import { DotPanelProps } from "@/page/App/components/DotPanel/interface" -import { applyScaleStyle } from "@/page/App/components/DotPanel/style" +import { FC, useMemo } from "react" import { useSelector } from "react-redux" -import { getPreviewEdgeWidth } from "@/redux/config/configSelector" -import { RenderComponentCanvas } from "@/page/App/components/DotPanel/renderComponentCanvas" +import { getIllaMode } from "@/redux/config/configSelector" +import { getCanvas } from "@/redux/currentApp/editor/components/componentsSelector" +import { + PageNode, + RootComponentNode, +} from "@/redux/currentApp/editor/components/componentsState" +import { RenderPage } from "./renderPage" +import { getRootNodeExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { useParams } from "react-router-dom" -export const DotPanel: FC = (props) => { - const { componentNode, ...otherProps } = props +export const DotPanel: FC = () => { + const canvasTree = useSelector(getCanvas) as RootComponentNode + const rootExecutionProps = useSelector(getRootNodeExecutionResult) + const mode = useSelector(getIllaMode) - // canvas field - const edgeWidth = useSelector(getPreviewEdgeWidth) + const { + currentPageIndex, + pageSortedKey, + homepageDisplayName, + } = rootExecutionProps + let { pageName = "page1" } = useParams() + const currentDisplayName = useMemo(() => { + if (mode === "production") { + return pageName || homepageDisplayName + } else { + return pageSortedKey[currentPageIndex] || homepageDisplayName + } + }, [currentPageIndex, homepageDisplayName, mode, pageName, pageSortedKey]) - const containerRef = useRef(null) + if ( + !canvasTree || + canvasTree.containerType !== "EDITOR_DOT_PANEL" || + canvasTree.type !== "DOT_PANEL" || + canvasTree.displayName !== "root" || + !rootExecutionProps + ) + return null + + const currentChildrenNode = canvasTree.childrenNode.find((node) => { + return node.displayName === currentDisplayName + }) + + if (currentChildrenNode == undefined) return null return ( -
- -
+ ) } diff --git a/apps/builder/src/page/App/components/DotPanel/interface.ts b/apps/builder/src/page/App/components/DotPanel/interface.ts index ae922b7777..cc3b2c74eb 100644 --- a/apps/builder/src/page/App/components/DotPanel/interface.ts +++ b/apps/builder/src/page/App/components/DotPanel/interface.ts @@ -1,9 +1,11 @@ -import { ComponentNode } from "@/redux/currentApp/editor/components/componentsState" -import { HTMLAttributes, RefObject } from "react" - -export interface DotPanelProps extends HTMLAttributes { - componentNode: ComponentNode -} +import { + ComponentNode, + PageNode, + SectionNode, + SECTION_POSITION, +} from "@/redux/currentApp/editor/components/componentsState" +import { RefObject } from "react" +import { IllaMode } from "@/redux/config/configState" export interface DragPosition { squareX: number @@ -15,6 +17,7 @@ export interface DragPosition { export interface DragInfo { item: ComponentNode childrenNodes: ComponentNode[] + currentColumnNumber: number } // return when drop trigger @@ -54,3 +57,78 @@ export interface DebounceUpdateReflow { parentDisplayName: string childNodes: ComponentNode[] } + +export interface RenderPageProps { + pageNode: PageNode + currentPageDisplayName: string +} + +export interface RenderSectionProps { + sectionNode: SectionNode + mode: IllaMode +} + +export interface RenderHeaderSectionProps { + sectionNode: SectionNode + topHeight: number + offsetTop: number + containerHeight: number + mode: IllaMode + footerHeight: number + currentPageDisplayName: string + leftPosition: SECTION_POSITION + rightPosition: SECTION_POSITION +} + +export interface RenderFooterSectionProps { + sectionNode: SectionNode + bottomHeight: number + offsetTop: number + containerHeight: number + mode: IllaMode + headerHeight: number + currentPageDisplayName: string + leftPosition: SECTION_POSITION + rightPosition: SECTION_POSITION +} + +export interface RenderLeftSectionProps { + sectionNode: SectionNode + offsetLeft: number + containerWidth: number + mode: IllaMode + leftWidth: number + rightWidth: number + currentPageDisplayName: string + leftPosition: SECTION_POSITION + showFoldIcon: boolean + isFold: boolean + setIsLeftFold: (isFold: boolean) => void +} + +export interface RenderRightSectionProps { + sectionNode: SectionNode + offsetLeft: number + containerWidth: number + mode: IllaMode + leftWidth: number + rightWidth: number + currentPageDisplayName: string + rightPosition: SECTION_POSITION + showFoldIcon: boolean + isFold: boolean + setIsRightFold: (isFold: boolean) => void +} + +export interface RenderContainerProps { + containerNode: SectionNode +} + +export interface ChangeLayoutBarProps { + sectionName: string + direction: "top" | "bottom" | "left" | "right" + changeAction: ( + sectionName: string, + direction?: "top" | "bottom" | "left" | "right", + ) => void +} diff --git a/apps/builder/src/page/App/components/DotPanel/renderComponentCanvas.tsx b/apps/builder/src/page/App/components/DotPanel/renderComponentCanvas.tsx index 20d7db5035..7e38becadc 100644 --- a/apps/builder/src/page/App/components/DotPanel/renderComponentCanvas.tsx +++ b/apps/builder/src/page/App/components/DotPanel/renderComponentCanvas.tsx @@ -33,6 +33,7 @@ import { componentsActions } from "@/redux/currentApp/editor/components/componen import { getDragResult, getReflowResult, + isAddAction, } from "@/page/App/components/DotPanel/calc" import { useDrop } from "react-dnd" import { PreviewPlaceholder } from "@/page/App/components/DotPanel/previewPlaceholder" @@ -43,7 +44,7 @@ import { widgetBuilder } from "@/widgetLibrary/widgetBuilder" import { BasicContainer } from "@/widgetLibrary/BasicContainer/BasicContainer" const UNIT_HEIGHT = 8 -const BLOCK_COLUMNS = 64 +const BASIC_BLOCK_COLUMNS = 64 export const RenderComponentCanvas: FC<{ componentNode: ComponentNode @@ -52,6 +53,7 @@ export const RenderComponentCanvas: FC<{ minHeight?: number canResizeY?: boolean safeRowNumber: number + blockColumns?: number }> = (props) => { const { componentNode, @@ -60,6 +62,7 @@ export const RenderComponentCanvas: FC<{ minHeight, canResizeY = true, safeRowNumber, + blockColumns = BASIC_BLOCK_COLUMNS, } = props const isShowCanvasDot = useSelector(isShowDot) @@ -81,8 +84,8 @@ export const RenderComponentCanvas: FC<{ ) as MutableRefObject const unitWidth = useMemo(() => { - return bounds.width / BLOCK_COLUMNS - }, [bounds.width]) + return bounds.width / blockColumns + }, [blockColumns, bounds.width]) const componentTree = useMemo(() => { const childrenNode = componentNode.childrenNode @@ -132,6 +135,7 @@ export const RenderComponentCanvas: FC<{ containerPadding={containerPadding} childrenNode={componentNode.childrenNode} collisionEffect={collisionEffect} + columnsNumber={blockColumns} /> ) default: @@ -139,6 +143,7 @@ export const RenderComponentCanvas: FC<{ } }) }, [ + blockColumns, canResizeY, collisionEffect, componentNode.childrenNode, @@ -180,16 +185,26 @@ export const RenderComponentCanvas: FC<{ setCollisionEffect(new Map()) } if (monitor.isOver({ shallow: true }) && monitor.getClientOffset()) { - const { item } = dragInfo + const { item, currentColumnNumber } = dragInfo + const scale = blockColumns / currentColumnNumber + + const scaleItem: ComponentNode = { + ...item, + w: item.w * scale, + } let dragResult if ( - (item.x === -1 && item.y === -1) || - item.parentNode !== componentNode.displayName + isAddAction( + item.x, + item.y, + item.parentNode, + componentNode.displayName, + ) ) { dragResult = getDragResult( monitor, containerRef, - item, + scaleItem, unitWidth, UNIT_HEIGHT, bounds.width, @@ -237,7 +252,7 @@ export const RenderComponentCanvas: FC<{ */ const oldParentDisplayName = item.parentNode const newItem = { - ...item, + ...scaleItem, parentNode: componentNode.displayName || "root", x: Math.round(landingX / unitWidth), y: Math.round(landingY / UNIT_HEIGHT), @@ -307,18 +322,28 @@ export const RenderComponentCanvas: FC<{ }, drop: (dragInfo, monitor) => { const isDrop = monitor.didDrop() - const { item } = dragInfo + const { item, currentColumnNumber } = dragInfo if (isDrop || item.displayName === componentNode.displayName) return if (monitor.getClientOffset()) { + const scale = blockColumns / currentColumnNumber + + const scaleItem: ComponentNode = { + ...item, + w: item.w * scale, + } let dragResult if ( - (item.x === -1 && item.y === -1) || - item.parentNode !== componentNode.displayName + isAddAction( + item.x, + item.y, + item.parentNode, + componentNode.displayName, + ) ) { dragResult = getDragResult( monitor, containerRef, - item, + scaleItem, unitWidth, UNIT_HEIGHT, bounds.width, @@ -330,7 +355,7 @@ export const RenderComponentCanvas: FC<{ dragResult = getDragResult( monitor, containerRef, - item, + scaleItem, unitWidth, UNIT_HEIGHT, bounds.width, @@ -347,7 +372,7 @@ export const RenderComponentCanvas: FC<{ */ const oldParentNodeDisplayName = item.parentNode || "root" const newItem = { - ...item, + ...scaleItem, parentNode: componentNode.displayName || "root", x: Math.round(landingX / unitWidth), y: Math.round(landingY / UNIT_HEIGHT), @@ -397,10 +422,21 @@ export const RenderComponentCanvas: FC<{ }, collect: (monitor) => { const dragInfo = monitor.getItem() + if (!dragInfo) { + return { + isActive: monitor.canDrop() && monitor.isOver({ shallow: true }), + nodeWidth: 0, + nodeHeight: 0, + } + } + const { item, currentColumnNumber } = dragInfo + let nodeWidth = item?.w ?? 0 + let nodeHeight = item?.h ?? 0 + nodeWidth = nodeWidth * (blockColumns / currentColumnNumber) return { isActive: monitor.canDrop() && monitor.isOver({ shallow: true }), - nodeWidth: dragInfo?.item?.w ?? 0, - nodeHeight: dragInfo?.item?.h ?? 0, + nodeWidth: nodeWidth, + nodeHeight: nodeHeight, } }, }), @@ -422,7 +458,7 @@ export const RenderComponentCanvas: FC<{ ), ) } else { - setRowNumber(Math.max(maxY, bounds.height / UNIT_HEIGHT)) + setRowNumber(maxY) } } }, [ @@ -446,7 +482,7 @@ export const RenderComponentCanvas: FC<{ css={applyComponentCanvasStyle( bounds.width, bounds.height, - bounds.width / BLOCK_COLUMNS, + bounds.width / blockColumns, UNIT_HEIGHT, isShowCanvasDot, rowNumber * 8, diff --git a/apps/builder/src/page/App/components/DotPanel/renderPage.tsx b/apps/builder/src/page/App/components/DotPanel/renderPage.tsx new file mode 100644 index 0000000000..b1d295b77b --- /dev/null +++ b/apps/builder/src/page/App/components/DotPanel/renderPage.tsx @@ -0,0 +1,421 @@ +import { FC, useRef, useLayoutEffect, useMemo, useState } from "react" +import { + PageNode, + SectionNode, + SECTION_POSITION, +} from "@/redux/currentApp/editor/components/componentsState" +import { RenderPageProps } from "./interface" +import { + LEFT_MIN_WIDTH, + RenderFooterSection, + RenderHeaderSection, + RenderLeftSection, + RenderRightSection, + RenderSection, + RIGHT_MIN_WIDTH, +} from "./renderSection" +import useMeasure from "react-use-measure" +import { useDispatch, useSelector } from "react-redux" +import { getCanvasShape, getIllaMode } from "@/redux/config/configSelector" +import { configActions } from "@/redux/config/configSlice" + +const getShowNameMapSectionNode = (pageNode: PageNode) => { + const { childrenNode = [] } = pageNode + const nameMapSection: Map = new Map() + childrenNode.forEach((node) => { + if (node.type === "SECTION_NODE") { + nameMapSection.set(node.showName, node as SectionNode) + } + }) + return nameMapSection +} + +const getLeftAndRightWidth = ( + canvasSize: "responsive" | "fixed", + width: number, + containerWidth: number, +) => { + if (canvasSize === "fixed") { + return width + } else { + return (width / 100) * containerWidth + } +} + +export const RenderPage: FC = (props) => { + const { pageNode, currentPageDisplayName } = props + const headerRef = useRef(null) + const footerRef = useRef(null) + const leftRef = useRef(null) + const rightRef = useRef(null) + const bodyRef = useRef(null) + const [containerRef, bounds] = useMeasure() + const canvasShape = useSelector(getCanvasShape) + const mode = useSelector(getIllaMode) + const dispatch = useDispatch() + + const showNameMapSectionNode = getShowNameMapSectionNode(pageNode) + + const { props: pageProps } = pageNode + const { + canvasSize, + hasLeft, + hasRight, + hasFooter, + hasHeader, + leftPosition, + rightPosition, + leftWidth, + rightWidth, + topHeight, + bottomHeight, + isFooterFixed, + isHeaderFixed, + isLeftFixed, + isRightFixed, + showLeftFoldIcon, + showRightFoldIcon, + } = pageProps + + const [isLeftFold, setIsLeftFold] = useState(false) + const [isRightFold, setIsRightFold] = useState(false) + + useLayoutEffect(() => { + if ( + canvasShape.canvasHeight !== bounds.height || + canvasShape.canvasWidth !== bounds.width + ) { + dispatch( + configActions.updateCanvasShapeReducer({ + canvasHeight: bounds.height, + canvasWidth: bounds.width, + }), + ) + } + }, [ + bounds.height, + bounds.width, + canvasShape.canvasHeight, + canvasShape.canvasWidth, + dispatch, + ]) + const realLeftWidth = useMemo(() => { + const leftWidthPX = getLeftAndRightWidth( + canvasSize, + leftWidth, + bounds.width, + ) + return hasLeft + ? leftWidthPX <= LEFT_MIN_WIDTH + ? LEFT_MIN_WIDTH + : leftWidthPX + : 0 + }, [bounds.width, canvasSize, hasLeft, leftWidth]) + + const realRightWidth = useMemo(() => { + const rightWidthPX = getLeftAndRightWidth( + canvasSize, + rightWidth, + bounds.width, + ) + return hasRight + ? rightWidthPX <= RIGHT_MIN_WIDTH + ? RIGHT_MIN_WIDTH + : rightWidthPX + : 0 + }, [bounds.width, canvasSize, hasRight, rightWidth]) + + const calcLeftWidth = useMemo(() => { + const leftWidthPX = getLeftAndRightWidth( + canvasSize, + leftWidth, + bounds.width, + ) + return hasLeft + ? isLeftFold + ? 32 + : leftWidthPX <= LEFT_MIN_WIDTH + ? LEFT_MIN_WIDTH + : leftWidthPX + : 0 + }, [bounds.width, canvasSize, hasLeft, isLeftFold, leftWidth]) + + const calcRightWidth = useMemo(() => { + const rightWidthPX = getLeftAndRightWidth( + canvasSize, + rightWidth, + bounds.width, + ) + return hasRight + ? isRightFold + ? 32 + : rightWidthPX <= RIGHT_MIN_WIDTH + ? RIGHT_MIN_WIDTH + : rightWidthPX + : 0 + }, [bounds.width, canvasSize, hasRight, isRightFold, rightWidth]) + + useLayoutEffect(() => { + let headerLeft = 0 + let headerWidth = bounds.width + let leftTop = 0 + let leftHeight = bounds.height + let rightTop = 0 + let rightHeight = bounds.height + let footerLeft = 0 + let footerWidth = bounds.width + let bodyWidth = bounds.width + let bodyTop = 0 + let bodyLeft = 0 + let bodyHeight = bounds.height + let leftArea = { + top: leftTop, + bottom: leftTop + leftHeight, + left: 0, + right: realLeftWidth, + } + let rightArea = { + top: rightTop, + bottom: rightTop + rightHeight, + left: realRightWidth, + right: 0, + } + let headerArea = { + top: 0, + bottom: topHeight, + left: headerLeft, + right: headerLeft + headerWidth, + } + let footerArea = { + top: bodyTop + bodyHeight, + bottom: bodyTop + bodyHeight + bodyHeight, + left: footerLeft, + right: footerLeft + footerWidth, + } + let bodyArea = { + top: bodyTop, + bottom: bodyTop + bodyHeight, + left: bodyLeft, + right: bodyLeft + bodyWidth, + } + + if (hasLeft) { + bodyWidth -= calcLeftWidth + bodyLeft = calcLeftWidth + switch (leftPosition) { + case SECTION_POSITION.TOP: { + headerLeft = calcLeftWidth + headerWidth -= calcLeftWidth + leftHeight -= bottomHeight + break + } + case SECTION_POSITION.FULL: { + headerLeft = calcLeftWidth + headerWidth -= calcLeftWidth + footerLeft = calcLeftWidth + footerWidth -= calcLeftWidth + break + } + case SECTION_POSITION.BOTTOM: { + footerLeft = calcLeftWidth + footerWidth -= calcLeftWidth + leftTop = topHeight + leftHeight -= topHeight + break + } + case SECTION_POSITION.CENTER: { + leftTop = topHeight + leftHeight = leftHeight - topHeight - bottomHeight + } + } + } + + if (hasRight) { + bodyWidth -= calcRightWidth + switch (rightPosition) { + case SECTION_POSITION.TOP: { + headerWidth -= calcRightWidth + rightHeight -= bottomHeight + break + } + case SECTION_POSITION.FULL: { + headerWidth -= calcRightWidth + footerWidth -= calcRightWidth + break + } + case SECTION_POSITION.BOTTOM: { + footerWidth -= calcRightWidth + rightTop = topHeight + rightHeight -= topHeight + break + } + case SECTION_POSITION.CENTER: { + rightTop = topHeight + rightHeight = rightHeight - bottomHeight - topHeight + } + } + } + if (hasHeader) { + bodyTop = topHeight + bodyHeight -= topHeight + } + + if (hasFooter) { + bodyHeight -= bottomHeight + } + + if (hasLeft && leftRef.current) { + leftRef.current.style.height = `${leftHeight}px` + leftRef.current.style.top = `${leftTop}px` + leftArea = { + top: leftTop, + bottom: leftTop + leftHeight, + left: 0, + right: calcLeftWidth, + } + } + if (hasRight && rightRef.current) { + rightRef.current.style.height = `${rightHeight}px` + rightRef.current.style.top = `${rightTop}px` + rightArea = { + top: rightTop, + bottom: rightTop + rightHeight, + left: calcRightWidth, + right: 0, + } + } + if (hasHeader && headerRef.current) { + headerRef.current.style.width = `${headerWidth}px` + headerRef.current.style.left = `${headerLeft}px` + headerArea = { + top: 0, + bottom: topHeight, + left: headerLeft, + right: headerLeft + headerWidth, + } + } + if (hasFooter && footerRef.current) { + footerRef.current.style.width = `${footerWidth}px` + footerRef.current.style.left = `${footerLeft}px` + footerArea = { + top: bodyTop + bodyHeight, + bottom: bodyTop + bodyHeight + bodyHeight, + left: footerLeft, + right: footerLeft + footerWidth, + } + } + + if (bodyRef.current) { + bodyRef.current.style.position = "absolute" + bodyRef.current.style.width = `${bodyWidth}px` + bodyRef.current.style.left = `${bodyLeft}px` + bodyRef.current.style.top = `${bodyTop}px` + bodyRef.current.style.height = `${bodyHeight}px` + bodyArea = { + top: bodyTop, + bottom: bodyTop + bodyHeight, + left: bodyLeft, + right: bodyLeft + bodyWidth, + } + } + }, [ + bottomHeight, + bounds, + calcLeftWidth, + calcRightWidth, + canvasSize, + hasFooter, + hasHeader, + hasLeft, + hasRight, + leftPosition, + leftWidth, + realLeftWidth, + realRightWidth, + rightPosition, + rightWidth, + topHeight, + ]) + + const headerSection = showNameMapSectionNode.get("headerSection") + const bodySection = showNameMapSectionNode.get("bodySection") + const leftSection = showNameMapSectionNode.get("leftSection") + const rightSection = showNameMapSectionNode.get("rightSection") + const footerSection = showNameMapSectionNode.get("footerSection") + + if ( + !pageNode || + pageNode.type !== "PAGE_NODE" || + !pageNode.props || + showNameMapSectionNode.size === 0 + ) + return null + + return ( +
+ {hasHeader && headerSection && currentPageDisplayName && ( + + )} + {hasLeft && leftSection && currentPageDisplayName && ( + + )} + {bodySection && currentPageDisplayName && ( + + )} + {hasRight && rightSection && currentPageDisplayName && ( + + )} + {hasFooter && footerSection && currentPageDisplayName && ( + + )} +
+ ) +} diff --git a/apps/builder/src/page/App/components/DotPanel/renderSection.tsx b/apps/builder/src/page/App/components/DotPanel/renderSection.tsx new file mode 100644 index 0000000000..ef4be23f94 --- /dev/null +++ b/apps/builder/src/page/App/components/DotPanel/renderSection.tsx @@ -0,0 +1,1249 @@ +import { + useEffect, + useRef, + forwardRef, + MutableRefObject, + useState, + useCallback, + useMemo, +} from "react" +import { useDispatch, useSelector } from "react-redux" +import { componentsActions } from "@/redux/currentApp/editor/components/componentsSlice" +import { + RenderFooterSectionProps, + RenderHeaderSectionProps, + RenderLeftSectionProps, + RenderRightSectionProps, + RenderSectionProps, +} from "./interface" +import { + applyFooterSectionWrapperStyle, + applyHeaderSectionWrapperStyle, + applyLeftSectionWrapperStyle, + applyRightSectionWrapperStyle, + applyContainerWrapperStyle, + resizeHorizontalBarStyle, + resizeHorizontalBarWrapperStyle, + resizeVerticalBarStyle, + resizeVerticalBarWrapperStyle, + applySideBarWrapperStyle, + sideBarIconStyle, + openFoldWrapperStyle, + leftOpenFoldPositionStyle, + applyNoBottomPaddingStyle, + rightOpenFoldPositionStyle, + disabledHorizontalBarWrapperStyle, + applyLeftAnimationWrapperStyle, + applyRightAnimationWrapperStyle, + leftWidthTipsStyle, + rightWidthTipsStyle, + headerHeightTipsStyle, + footerHeightTipsStyle, +} from "./style" +import { RenderComponentCanvas } from "./renderComponentCanvas" +import useMeasure from "react-use-measure" +import { + ChangeLayoutBottomBar, + ChangeLayoutLeftBar, + ChangeLayoutRightBar, + ChangeLayoutTopBar, +} from "./changeLayoutBar" +import { SECTION_POSITION } from "@/redux/currentApp/editor/components/componentsState" +import { PreIcon, NextIcon } from "@illa-design/react" +import { motion, AnimatePresence } from "framer-motion" +import { getExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { useParams } from "react-router-dom" + +export const HEADER_MIN_HEIGHT = 96 +export const FOOTER_MIN_HEIGHT = 96 +export const LEFT_MIN_WIDTH = 240 +export const RIGHT_MIN_WIDTH = 240 +export const BODY_MIN_WIDTH = 384 +export const BODY_MIN_HEIGHT = 320 + +export const RenderSection = forwardRef( + (props, ref) => { + const { sectionNode, mode } = props + const executionResult = useSelector(getExecutionResult) + const sectionNodeProps = executionResult[sectionNode.displayName] || {} + const [containerBoundRef, containerBound] = useMeasure() + const containerRef = useRef( + null, + ) as MutableRefObject + const { + viewSortedKey, + currentViewIndex, + defaultViewKey, + sectionViewConfigs, + } = sectionNodeProps + let { viewPath = "View1" } = useParams() + const currentViewDisplayName = useMemo(() => { + if (!Array.isArray(sectionViewConfigs) || !Array.isArray(viewSortedKey)) + return "View 1" + const defaultedViewKey = viewSortedKey.includes(defaultViewKey) + ? defaultViewKey + : viewSortedKey[0] + if (mode === "production") { + const targetViewName = sectionViewConfigs.find( + (config) => config.path === decodeURIComponent(viewPath), + ) + return targetViewName?.viewDisplayName || defaultedViewKey + } else { + return viewSortedKey[currentViewIndex] || defaultedViewKey + } + }, [ + currentViewIndex, + defaultViewKey, + mode, + sectionViewConfigs, + viewPath, + viewSortedKey, + ]) + if (!sectionNodeProps) return null + + const componentNode = sectionNode.childrenNode.find( + (node) => node.displayName === currentViewDisplayName, + ) + return ( +
+
{ + containerBoundRef(ele) + containerRef.current = ele + }} + > + {componentNode && ( + + )} +
+
+ ) + }, +) + +RenderSection.displayName = "RenderSection" + +export const RenderHeaderSection = forwardRef< + HTMLDivElement, + RenderHeaderSectionProps +>((props, ref) => { + const { + sectionNode, + topHeight, + offsetTop, + mode, + containerHeight, + footerHeight, + currentPageDisplayName, + leftPosition, + rightPosition, + } = props + const [isResizeActive, setIsResizeActive] = useState(false) + const [heightPX, setHeightPX] = useState(topHeight) + const [containerBoundRef, containerBound] = useMeasure() + const containerRef = useRef( + null, + ) as MutableRefObject + + const dispatch = useDispatch() + const executionResult = useSelector(getExecutionResult) + const sectionNodeProps = executionResult[sectionNode.displayName] || {} + const { + viewSortedKey, + currentViewIndex, + defaultViewKey, + sectionViewConfigs, + } = sectionNodeProps + let { viewPath = "View1" } = useParams() + const currentViewDisplayName = useMemo(() => { + if (!Array.isArray(sectionViewConfigs) || !Array.isArray(viewSortedKey)) + return "View 1" + const defaultedViewKey = viewSortedKey.includes(defaultViewKey) + ? defaultViewKey + : viewSortedKey[0] + if (mode === "production") { + const targetViewName = sectionViewConfigs.find( + (config) => config.path === decodeURIComponent(viewPath), + ) + return targetViewName?.viewDisplayName || defaultedViewKey + } else { + return viewSortedKey[currentViewIndex] || defaultedViewKey + } + }, [ + currentViewIndex, + defaultViewKey, + mode, + sectionViewConfigs, + viewPath, + viewSortedKey, + ]) + + const handleClickMoveBar = () => { + setHeightPX(topHeight) + setIsResizeActive(true) + } + + useEffect(() => { + const mouseMoveListener = (e: globalThis.MouseEvent) => { + if (isResizeActive) { + const { clientY } = e + let currentPointPositionY = clientY - offsetTop + let otherPanelHeightPX = footerHeight + if (currentPointPositionY % 8 !== 0) { + currentPointPositionY = Math.round(currentPointPositionY / 8) * 8 + } + if (currentPointPositionY < HEADER_MIN_HEIGHT) { + currentPointPositionY = HEADER_MIN_HEIGHT + } + if ( + containerHeight - currentPointPositionY - otherPanelHeightPX < + BODY_MIN_HEIGHT + ) { + if (footerHeight !== 0) { + otherPanelHeightPX = + containerHeight - BODY_MIN_HEIGHT - currentPointPositionY + if (otherPanelHeightPX <= FOOTER_MIN_HEIGHT) { + otherPanelHeightPX = FOOTER_MIN_HEIGHT + } + } + currentPointPositionY = + containerHeight - BODY_MIN_HEIGHT - otherPanelHeightPX + } + setHeightPX(currentPointPositionY) + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + topHeight: currentPointPositionY, + bottomHeight: otherPanelHeightPX, + }, + }), + ) + } + } + + const mouseUpListener = () => { + setIsResizeActive(false) + } + document.addEventListener("mousemove", mouseMoveListener) + document.addEventListener("mouseup", mouseUpListener) + return () => { + document.removeEventListener("mousemove", mouseMoveListener) + document.removeEventListener("mouseup", mouseUpListener) + } + }, [ + containerHeight, + currentPageDisplayName, + dispatch, + footerHeight, + isResizeActive, + offsetTop, + ]) + const [isInSection, setIsInSection] = useState(false) + + const onMouseEnter = useCallback(() => { + setIsInSection(true) + }, []) + + const onMouseLeave = useCallback(() => { + setIsInSection(false) + }, []) + + const handleUpdateLayout = useCallback( + (sectionName: string, direction?: "top" | "bottom" | "left" | "right") => { + if (sectionName === "leftSection") { + switch (leftPosition) { + case SECTION_POSITION.TOP: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: SECTION_POSITION.CENTER, + }, + }), + ) + break + } + case SECTION_POSITION.FULL: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: SECTION_POSITION.BOTTOM, + }, + }), + ) + break + } + } + } + + if (sectionName === "rightSection") { + switch (rightPosition) { + case SECTION_POSITION.TOP: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: SECTION_POSITION.CENTER, + }, + }), + ) + break + } + case SECTION_POSITION.FULL: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: SECTION_POSITION.BOTTOM, + }, + }), + ) + break + } + } + } + }, + [currentPageDisplayName, dispatch, leftPosition, rightPosition], + ) + if (!sectionNodeProps) return null + + const componentNode = sectionNode.childrenNode.find( + (node) => node.displayName === currentViewDisplayName, + ) + + return ( +
+ {isInSection && + mode === "edit" && + !isResizeActive && + (leftPosition === SECTION_POSITION.TOP || + leftPosition === SECTION_POSITION.FULL) && ( + + )} + {isInSection && + mode === "edit" && + !isResizeActive && + (rightPosition === SECTION_POSITION.TOP || + rightPosition === SECTION_POSITION.FULL) && ( + + )} +
{ + containerBoundRef(ele) + containerRef.current = ele + }} + > + {componentNode && ( + + )} +
+ {mode === "edit" && ( +
+
+
+ )} + {isResizeActive && ( +
{heightPX.toFixed(0)}px
+ )} +
+ ) +}) +RenderHeaderSection.displayName = "RenderHeaderSection" + +export const RenderFooterSection = forwardRef< + HTMLDivElement, + RenderFooterSectionProps +>((props, ref) => { + const { + sectionNode, + bottomHeight, + containerHeight, + offsetTop, + mode, + headerHeight, + currentPageDisplayName, + leftPosition, + rightPosition, + } = props + const executionResult = useSelector(getExecutionResult) + const [isResizeActive, setIsResizeActive] = useState(false) + const [heightPX, setHeightPX] = useState(bottomHeight) + const sectionNodeProps = executionResult[sectionNode.displayName] || {} + const { + viewSortedKey, + currentViewIndex, + defaultViewKey, + sectionViewConfigs, + } = sectionNodeProps + let { viewPath = "View1" } = useParams() + const currentViewDisplayName = useMemo(() => { + if (!Array.isArray(sectionViewConfigs) || !Array.isArray(viewSortedKey)) + return "View 1" + const defaultedViewKey = viewSortedKey.includes(defaultViewKey) + ? defaultViewKey + : viewSortedKey[0] + if (mode === "production") { + const targetViewName = sectionViewConfigs.find( + (config) => config.path === decodeURIComponent(viewPath), + ) + return targetViewName?.viewDisplayName || defaultedViewKey + } else { + return viewSortedKey[currentViewIndex] || defaultedViewKey + } + }, [ + currentViewIndex, + defaultViewKey, + mode, + sectionViewConfigs, + viewPath, + viewSortedKey, + ]) + + const componentNode = sectionNode.childrenNode.find( + (node) => node.displayName === currentViewDisplayName, + ) + + const [containerBoundRef, containerBound] = useMeasure() + const containerRef = useRef( + null, + ) as MutableRefObject + + const dispatch = useDispatch() + + const handleClickMoveBar = () => { + setHeightPX(bottomHeight) + setIsResizeActive(true) + } + + useEffect(() => { + const mouseMoveListener = (e: globalThis.MouseEvent) => { + if (isResizeActive) { + const { clientY } = e + let currentPointPositionY = containerHeight - (clientY - offsetTop) + let otherPanelHeightPX = headerHeight + if (currentPointPositionY % 8 !== 0) { + currentPointPositionY = Math.round(currentPointPositionY / 8) * 8 + } + if (currentPointPositionY < FOOTER_MIN_HEIGHT) { + currentPointPositionY = FOOTER_MIN_HEIGHT + } + if ( + containerHeight - currentPointPositionY - otherPanelHeightPX < + BODY_MIN_HEIGHT + ) { + if (headerHeight !== 0) { + otherPanelHeightPX = + containerHeight - BODY_MIN_HEIGHT - currentPointPositionY + if (otherPanelHeightPX <= HEADER_MIN_HEIGHT) { + otherPanelHeightPX = HEADER_MIN_HEIGHT + } + } + + currentPointPositionY = + containerHeight - BODY_MIN_HEIGHT - otherPanelHeightPX + } + setHeightPX(currentPointPositionY) + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + bottomHeight: currentPointPositionY, + topHeight: otherPanelHeightPX, + }, + }), + ) + } + } + + const mouseUpListener = () => { + setIsResizeActive(false) + } + document.addEventListener("mousemove", mouseMoveListener) + document.addEventListener("mouseup", mouseUpListener) + return () => { + document.removeEventListener("mousemove", mouseMoveListener) + document.removeEventListener("mouseup", mouseUpListener) + } + }, [ + containerHeight, + currentPageDisplayName, + dispatch, + headerHeight, + isResizeActive, + offsetTop, + ]) + + const [isInSection, setIsInSection] = useState(false) + + const onMouseEnter = useCallback(() => { + setIsInSection(true) + }, []) + + const onMouseLeave = useCallback(() => { + setIsInSection(false) + }, []) + + const handleUpdateLayout = useCallback( + (sectionName: string, direction?: "top" | "bottom" | "left" | "right") => { + if (sectionName === "leftSection") { + switch (leftPosition) { + case SECTION_POSITION.BOTTOM: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: SECTION_POSITION.CENTER, + }, + }), + ) + break + } + case SECTION_POSITION.FULL: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: SECTION_POSITION.TOP, + }, + }), + ) + break + } + } + } + + if (sectionName === "rightSection") { + switch (rightPosition) { + case SECTION_POSITION.BOTTOM: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: SECTION_POSITION.CENTER, + }, + }), + ) + break + } + case SECTION_POSITION.FULL: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: SECTION_POSITION.TOP, + }, + }), + ) + break + } + } + } + }, + [currentPageDisplayName, dispatch, leftPosition, rightPosition], + ) + + return ( +
+ {isInSection && + mode === "edit" && + !isResizeActive && + (leftPosition === SECTION_POSITION.BOTTOM || + leftPosition === SECTION_POSITION.FULL) && ( + + )} + {isInSection && + mode === "edit" && + !isResizeActive && + (rightPosition === SECTION_POSITION.BOTTOM || + rightPosition === SECTION_POSITION.FULL) && ( + + )} +
{ + containerBoundRef(ele) + containerRef.current = ele + }} + > + {componentNode && ( + + )} +
+ {mode === "edit" && ( +
+
+
+ )} + {isResizeActive && ( +
{heightPX.toFixed(0)}px
+ )} +
+ ) +}) +RenderFooterSection.displayName = "RenderHeaderSection" + +export const RenderLeftSection = forwardRef< + HTMLDivElement, + RenderLeftSectionProps +>((props, ref) => { + const { + sectionNode, + offsetLeft, + containerWidth, + mode, + rightWidth, + currentPageDisplayName, + leftPosition, + showFoldIcon, + isFold, + leftWidth, + setIsLeftFold, + } = props + + const executionResult = useSelector(getExecutionResult) + const sectionNodeProps = executionResult[sectionNode.displayName] || {} + const { + viewSortedKey, + currentViewIndex, + defaultViewKey, + sectionViewConfigs, + } = sectionNodeProps + let { viewPath = "View1" } = useParams() + const currentViewDisplayName = useMemo(() => { + if (!Array.isArray(sectionViewConfigs) || !Array.isArray(viewSortedKey)) + return "View 1" + const defaultedViewKey = viewSortedKey.includes(defaultViewKey) + ? defaultViewKey + : viewSortedKey[0] + if (mode === "production") { + const targetViewName = sectionViewConfigs.find( + (config) => config.path === decodeURIComponent(viewPath), + ) + return targetViewName?.viewDisplayName || defaultedViewKey + } else { + return viewSortedKey[currentViewIndex] || defaultedViewKey + } + }, [ + currentViewIndex, + defaultViewKey, + mode, + sectionViewConfigs, + viewPath, + viewSortedKey, + ]) + const [isInSection, setIsInSection] = useState(false) + const [animationComplete, setAnimationComplete] = useState(true) + const [isResizeActive, setIsResizeActive] = useState(false) + const [presetWidth, setPresetWidth] = useState(0) + + const componentNode = sectionNode.childrenNode.find( + (node) => node.displayName === currentViewDisplayName, + ) + + const [containerBoundRef, containerBound] = useMeasure() + const containerRef = useRef( + null, + ) as MutableRefObject + + const dispatch = useDispatch() + + const handleClickMoveBar = () => { + const presetWidth = (leftWidth / containerWidth) * 100 + setPresetWidth(presetWidth) + setIsResizeActive(true) + } + + useEffect(() => { + const mouseMoveListener = (e: globalThis.MouseEvent) => { + if (isResizeActive) { + const { clientX } = e + let currentPointPositionX = clientX - offsetLeft + let otherPanelWidthPX = rightWidth + if (currentPointPositionX < LEFT_MIN_WIDTH) { + currentPointPositionX = LEFT_MIN_WIDTH + } + if ( + containerWidth - currentPointPositionX - otherPanelWidthPX < + BODY_MIN_WIDTH + ) { + if (rightWidth !== 0) { + otherPanelWidthPX = + containerWidth - BODY_MIN_WIDTH - currentPointPositionX + if (otherPanelWidthPX <= RIGHT_MIN_WIDTH) { + otherPanelWidthPX = RIGHT_MIN_WIDTH + } + } + + currentPointPositionX = + containerWidth - BODY_MIN_WIDTH - otherPanelWidthPX + } + const presetWidth = (currentPointPositionX / containerWidth) * 100 + const otherPanelWidth = (otherPanelWidthPX / containerWidth) * 100 + setPresetWidth(presetWidth) + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + leftWidth: presetWidth, + rightWidth: otherPanelWidth, + }, + }), + ) + } + } + + const mouseUpListener = () => { + setIsResizeActive(false) + } + document.addEventListener("mousemove", mouseMoveListener) + document.addEventListener("mouseup", mouseUpListener) + return () => { + document.removeEventListener("mousemove", mouseMoveListener) + document.removeEventListener("mouseup", mouseUpListener) + } + }, [ + containerWidth, + currentPageDisplayName, + dispatch, + isResizeActive, + offsetLeft, + rightWidth, + ]) + + const onMouseEnter = useCallback(() => { + setIsInSection(true) + }, []) + + const onMouseLeave = useCallback(() => { + setIsInSection(false) + }, []) + + const handleUpdateLayout = useCallback( + (sectionName: string, direction?: "top" | "bottom" | "left" | "right") => { + if (sectionName === "leftSection") { + switch (leftPosition) { + case SECTION_POSITION.TOP: + case SECTION_POSITION.BOTTOM: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: SECTION_POSITION.FULL, + }, + }), + ) + break + } + case SECTION_POSITION.CENTER: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + leftPosition: + direction === "top" + ? SECTION_POSITION.TOP + : SECTION_POSITION.BOTTOM, + }, + }), + ) + break + } + } + } + }, + [currentPageDisplayName, dispatch, leftPosition], + ) + + const handleOnClickFoldIcon = useCallback(() => { + setIsLeftFold(!isFold) + }, [isFold, setIsLeftFold]) + + return ( +
+
+ {isInSection && + mode === "edit" && + !isResizeActive && + (leftPosition === SECTION_POSITION.BOTTOM || + leftPosition === SECTION_POSITION.CENTER) && ( + + )} + {isInSection && + mode === "edit" && + !isResizeActive && + (leftPosition === SECTION_POSITION.TOP || + leftPosition === SECTION_POSITION.CENTER) && ( + + )} + +
{ + containerBoundRef(ele) + containerRef.current = ele + }} + > + {componentNode && animationComplete && ( + + )} + {showFoldIcon && ( +
+ +
+ )} +
+ + {mode === "edit" && animationComplete && ( +
+
+
+ )} + {isResizeActive && ( +
{presetWidth.toFixed(0)}%
+ )} +
+ + + {isFold && ( + + + + )} + + + { + setAnimationComplete(true) + }} + > + {mode === "edit" && isFold && ( + { + setAnimationComplete(false) + }} + /> + )} + +
+ ) +}) +RenderLeftSection.displayName = "RenderLeftSection" + +export const RenderRightSection = forwardRef< + HTMLDivElement, + RenderRightSectionProps +>((props, ref) => { + const { + sectionNode, + offsetLeft, + containerWidth, + mode, + leftWidth, + currentPageDisplayName, + rightPosition, + showFoldIcon, + isFold, + rightWidth, + setIsRightFold, + } = props + + const executionResult = useSelector(getExecutionResult) + const sectionNodeProps = executionResult[sectionNode.displayName] || {} + const { + viewSortedKey, + currentViewIndex, + defaultViewKey, + sectionViewConfigs, + } = sectionNodeProps + let { viewPath = "View1" } = useParams() + const currentViewDisplayName = useMemo(() => { + if (!Array.isArray(sectionViewConfigs) || !Array.isArray(viewSortedKey)) + return "View 1" + const defaultedViewKey = viewSortedKey.includes(defaultViewKey) + ? defaultViewKey + : viewSortedKey[0] + if (mode === "production") { + const targetViewName = sectionViewConfigs.find( + (config) => config.path === decodeURIComponent(viewPath), + ) + return targetViewName?.viewDisplayName || defaultedViewKey + } else { + return viewSortedKey[currentViewIndex] || defaultedViewKey + } + }, [ + currentViewIndex, + defaultViewKey, + mode, + sectionViewConfigs, + viewPath, + viewSortedKey, + ]) + + const componentNode = sectionNode.childrenNode.find( + (node) => node.displayName === currentViewDisplayName, + ) + + const [containerBoundRef, containerBound] = useMeasure() + const containerRef = useRef( + null, + ) as MutableRefObject + + const [isResizeActive, setIsResizeActive] = useState(false) + const [presetWidth, setPresetWidth] = useState(0) + const [isInSection, setIsInSection] = useState(false) + const [animationComplete, setAnimationComplete] = useState(true) + + const dispatch = useDispatch() + + const handleClickMoveBar = () => { + const presetWidth = (rightWidth / containerWidth) * 100 + setPresetWidth(presetWidth) + setIsResizeActive(true) + } + + useEffect(() => { + const mouseMoveListener = (e: globalThis.MouseEvent) => { + if (isResizeActive) { + const { clientX } = e + let otherPanelWidthPX = leftWidth + let currentPointPositionX = containerWidth - (clientX - offsetLeft) + if (currentPointPositionX < RIGHT_MIN_WIDTH) { + currentPointPositionX = RIGHT_MIN_WIDTH + } + if ( + containerWidth - currentPointPositionX - otherPanelWidthPX < + BODY_MIN_WIDTH + ) { + if (leftWidth !== 0) { + otherPanelWidthPX = + containerWidth - BODY_MIN_WIDTH - currentPointPositionX + if (otherPanelWidthPX <= LEFT_MIN_WIDTH) { + otherPanelWidthPX = LEFT_MIN_WIDTH + } + } + + currentPointPositionX = + containerWidth - BODY_MIN_WIDTH - otherPanelWidthPX + } + const presetWidth = (currentPointPositionX / containerWidth) * 100 + const otherPanelWidth = (otherPanelWidthPX / containerWidth) * 100 + setPresetWidth(presetWidth) + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + rightWidth: presetWidth, + leftWidth: otherPanelWidth, + }, + }), + ) + } + } + + const mouseUpListener = () => { + setIsResizeActive(false) + } + document.addEventListener("mousemove", mouseMoveListener) + document.addEventListener("mouseup", mouseUpListener) + return () => { + document.removeEventListener("mousemove", mouseMoveListener) + document.removeEventListener("mouseup", mouseUpListener) + } + }, [ + containerWidth, + currentPageDisplayName, + dispatch, + isResizeActive, + leftWidth, + offsetLeft, + ]) + + const onMouseEnter = useCallback(() => { + setIsInSection(true) + }, []) + + const onMouseLeave = useCallback(() => { + setIsInSection(false) + }, []) + + const handleUpdateLayout = useCallback( + (sectionName: string, direction?: "top" | "bottom" | "left" | "right") => { + if (sectionName === "rightSection") { + switch (rightPosition) { + case SECTION_POSITION.TOP: + case SECTION_POSITION.BOTTOM: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: SECTION_POSITION.FULL, + }, + }), + ) + break + } + case SECTION_POSITION.CENTER: { + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + layout: "Custom", + rightPosition: + direction === "top" + ? SECTION_POSITION.TOP + : SECTION_POSITION.BOTTOM, + }, + }), + ) + break + } + } + } + }, + [currentPageDisplayName, dispatch, rightPosition], + ) + + const handleOnClickFoldIcon = useCallback(() => { + setIsRightFold(!isFold) + }, [isFold, setIsRightFold]) + + return ( +
+
+ {isInSection && + mode === "edit" && + !isResizeActive && + (rightPosition === SECTION_POSITION.BOTTOM || + rightPosition === SECTION_POSITION.CENTER) && ( + + )} + {isInSection && + mode === "edit" && + !isResizeActive && + (rightPosition === SECTION_POSITION.TOP || + rightPosition === SECTION_POSITION.CENTER) && ( + + )} +
{ + containerBoundRef(ele) + containerRef.current = ele + }} + > + {componentNode && animationComplete && ( + + )} + {showFoldIcon && ( +
+ +
+ )} +
+ {mode === "edit" && animationComplete && ( +
+
+
+ )} + {isResizeActive && ( +
{presetWidth.toFixed(0)}%
+ )} +
+ + + {isFold && ( + + + + )} + + + { + setAnimationComplete(true) + }} + > + {mode === "edit" && isFold && ( + { + setAnimationComplete(false) + }} + /> + )} + +
+ ) +}) +RenderRightSection.displayName = "RenderRightSection" diff --git a/apps/builder/src/page/App/components/DotPanel/style.ts b/apps/builder/src/page/App/components/DotPanel/style.ts index 683fa41b53..98306e9434 100644 --- a/apps/builder/src/page/App/components/DotPanel/style.ts +++ b/apps/builder/src/page/App/components/DotPanel/style.ts @@ -1,14 +1,15 @@ import { css, SerializedStyles } from "@emotion/react" import { globalColor, illaPrefix } from "@illa-design/theme" +import { IllaMode } from "@/redux/config/configState" export function applyScaleStyle( verticalResize: boolean, edgeWidth: number, ): SerializedStyles { return css` - padding-left: ${edgeWidth}px; + /* padding-left: ${edgeWidth}px; padding-right: ${edgeWidth}px; - padding-top: ${edgeWidth}px; + padding-top: ${edgeWidth}px; */ overflow-x: hidden; overflow-y: ${verticalResize ? "auto" : "hidden"}; width: 100%; @@ -130,3 +131,312 @@ export const applyFreezePlaceholderShapeStyle = ( z-index: 6; ` } + +export const resizeVerticalBarWrapperStyle = css` + width: 100%; + height: 8px; + background-color: #f7f8fa; + transition: background-color 200ms ease-in-out; + display: flex; + align-items: center; + justify-content: center; + flex: none; + cursor: row-resize; + :hover { + background-color: ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + } + :active { + background-color: ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + } +` + +export const resizeVerticalBarStyle = css` + height: 2px; + width: 32px; + border-radius: 1px; + background-color: ${globalColor(`--${illaPrefix}-grayBlue-06`)}; +` + +export const disabledHorizontalBarWrapperStyle = css` + height: 100%; + width: 8px; + background-color: #f7f8fa; +` + +export const resizeHorizontalBarWrapperStyle = css` + height: 100%; + width: 8px; + background-color: #f7f8fa; + transition: background-color 200ms ease-in-out; + display: flex; + flex: none; + align-items: center; + justify-content: center; + cursor: col-resize; + :hover { + background-color: ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + } + :active { + background-color: ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + } +` + +export const resizeHorizontalBarStyle = css` + width: 2px; + height: 32px; + border-radius: 1px; + background-color: ${globalColor(`--${illaPrefix}-grayBlue-06`)}; +` + +export const applyHeaderSectionWrapperStyle = ( + height: string, + left: string = "0px", + width: string = "0px", +) => { + return css` + position: absolute; + top: 0; + left: ${left}; + width: ${width}; + height: ${height}; + display: flex; + flex-direction: column; + min-height: 96px; + ` +} + +export const applyFooterSectionWrapperStyle = ( + height: string, + left: string = "0px", + width: string = "0px", +) => { + return css` + position: absolute; + bottom: 0; + left: ${left}; + width: ${width}; + height: ${height}; + display: flex; + flex-direction: column-reverse; + min-height: 96px; + ` +} + +export const applyLeftSectionWrapperStyle = ( + width: string, + top: string = "0px", + isFold: boolean, +) => { + return css` + position: absolute; + top: ${top}; + left: 0; + height: 100%; + width: ${width}; + display: flex; + flex-direction: row; + min-width: ${isFold ? 0 : "240px"}; + ` +} + +export const applyRightSectionWrapperStyle = ( + width: string, + top: string = "0px", + isFold: boolean, +) => { + return css` + position: absolute; + top: ${top}; + right: 0; + height: 100%; + width: ${width}; + display: flex; + flex-direction: row-reverse; + min-width: ${isFold ? 0 : "240px"}; + ` +} + +export const applyContainerWrapperStyle = (model: IllaMode) => { + return css` + width: 100%; + height: 100%; + padding: ${model !== "edit" ? "0" : "8px"}; + overflow-y: auto; + overflow-x: hidden; + ` +} + +export const applyNoBottomPaddingStyle = (isShowFold: boolean) => { + return isShowFold + ? css` + padding-bottom: 0; + ` + : null +} + +export const changeLayoutTopBarWrapperStyle = css` + position: absolute; + top: -9px; + display: flex; + flex-direction: column; + align-items: center; + width: calc(100% - 8px); + cursor: pointer; + z-index: 10; +` + +export const changeLayoutBottomBarWrapperStyle = css` + position: absolute; + bottom: -9px; + display: flex; + flex-direction: column-reverse; + align-items: center; + width: calc(100% - 8px); + cursor: pointer; + z-index: 10; +` + +export const changeLayoutLeftBarWrapperStyle = css` + position: absolute; + left: -9px; + display: flex; + align-items: center; + height: calc(100% - 8px); + cursor: pointer; + z-index: 10; +` + +export const changeLayoutRightBarWrapperStyle = css` + position: absolute; + right: -9px; + display: flex; + flex-direction: row-reverse; + align-items: center; + height: calc(100% - 8px); + cursor: pointer; + z-index: 10; +` + +export const changeLayoutHorizontalBarStyle = css` + width: 100%; + height: 2px; + background-color: ${globalColor(`--${illaPrefix}-techPurple-01`)}; +` + +export const changeLayoutBottomIconStyle = css` + transform: rotate(180deg); +` + +export const changeLayoutLeftIconStyle = css` + transform: rotate(-90deg); +` + +export const changeLayoutRightIconStyle = css` + transform: rotate(90deg); +` + +export const changeLayoutVerticalBarStyle = css` + width: 2px; + height: 100%; + background-color: ${globalColor(`--${illaPrefix}-techPurple-01`)}; +` + +export const applySideBarWrapperStyle = (direction: "left" | "right") => { + return css` + width: 100%; + display: flex; + align-items: center; + justify-content: ${direction === "left" ? "flex-end" : "flex-start"}; + padding: 8px 0px; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; + font-size: 12px; + ` +} + +export const sideBarIconStyle = css` + cursor: pointer; + flex: none; +` + +export const openFoldWrapperStyle = css` + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background-color: ${globalColor(`--${illaPrefix}-white-01`)}; + box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.08); + border-radius: 0px 16px 16px 0px; + cursor: pointer; + font-size: 12px; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; +` + +export const leftOpenFoldPositionStyle = css` + position: absolute; + bottom: 8px; +` + +export const rightOpenFoldPositionStyle = css` + transform: rotate(180deg); + position: absolute; + bottom: 8px; +` + +export const applyLeftAnimationWrapperStyle = (isFold: boolean) => { + return css` + width: 100%; + height: 100%; + visibility: ${!isFold ? "visible" : "hidden"}; + display: flex; + flex-direction: row; + position: relative; + ` +} + +export const applyRightAnimationWrapperStyle = (isFold: boolean) => { + return css` + width: 100%; + height: 100%; + visibility: ${!isFold ? "visible" : "hidden"}; + display: flex; + flex-direction: row-reverse; + ` +} + +export const basicTipsStyle = css` + position: absolute; + padding: 2px 4px; + background-color: ${globalColor(`--${illaPrefix}-techPurple-01`)}; + border: 1px solid ${globalColor(`--${illaPrefix}-white-01`)}; + border-radius: 4px; + color: white; + user-select: none; + pointer-events: none; + font-size: 12px; + overflow: hidden; + z-index: 10; +` + +export const leftWidthTipsStyle = css` + ${basicTipsStyle}; + right: -35px; + top: calc(50% - 10px); +` +export const rightWidthTipsStyle = css` + ${basicTipsStyle}; + left: -35px; + top: calc(50% - 10px); +` + +export const headerHeightTipsStyle = css` + ${basicTipsStyle}; + bottom: -20px; + left: calc(50% - 18px); +` + +export const footerHeightTipsStyle = css` + ${basicTipsStyle}; + top: -20px; + left: calc(50% - 18px); +` diff --git a/apps/builder/src/page/App/components/PageNavBar/index.tsx b/apps/builder/src/page/App/components/PageNavBar/index.tsx index e5cef710da..dd1cd0551d 100644 --- a/apps/builder/src/page/App/components/PageNavBar/index.tsx +++ b/apps/builder/src/page/App/components/PageNavBar/index.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback, useState, MouseEvent } from "react" +import { FC, useCallback, useState } from "react" import { useDispatch, useSelector } from "react-redux" import { useTranslation } from "react-i18next" import { ReactComponent as Logo } from "@/assets/illa-logo.svg" @@ -6,12 +6,15 @@ import { BugIcon, CaretRightIcon, ExitIcon, + FullScreenIcon, LockIcon, UnlockIcon, WindowBottomIcon, WindowLeftIcon, WindowRightIcon, } from "@illa-design/icon" +import { Trigger } from "@illa-design/trigger" +import { Message } from "@illa-design/message" import { Button, ButtonGroup } from "@illa-design/button" import { PageNavBarProps } from "@/page/App/components/PageNavBar/interface" import { configActions } from "@/redux/config/configSlice" @@ -36,13 +39,11 @@ import { windowIconStyle, } from "./style" import { Api } from "@/api/base" -import { Message } from "@illa-design/message" import { Badge } from "@illa-design/badge" import { DeployResp } from "@/page/App/components/PageNavBar/resp" import { fromNow } from "@/utils/dayjs" import { globalColor, illaPrefix } from "@illa-design/theme" import { getExecutionDebuggerData } from "@/redux/currentApp/executionTree/executionSelector" -import { Trigger } from "@illa-design/trigger" export const PageNavBar: FC = (props) => { const { className } = props @@ -189,6 +190,15 @@ export const PageNavBar: FC = (props) => { onClick={handleClickFreezeIcon} /> + +
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/index.tsx b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/index.tsx new file mode 100644 index 0000000000..61160f2c37 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/index.tsx @@ -0,0 +1,45 @@ +import { FC } from "react" +import { ListBody } from "./body" +import { ViewListHeader } from "./header" +import { ViewListProps } from "./interface" +import { viewsListWrapperStyle } from "./style" +import { useDispatch, useSelector } from "react-redux" +import { + getCanvas, + searchDsl, +} from "@/redux/currentApp/editor/components/componentsSelector" +import { + getRootNodeExecutionResult, + getSectionExecutionResultArray, +} from "@/redux/currentApp/executionTree/executionSelector" +import { RootState } from "@/store" + +export const ViewList: FC = (props) => { + const { sectionName } = props + const dispatch = useDispatch() + const rootNodeProps = useSelector(getRootNodeExecutionResult) + const { currentPageIndex, pageSortedKey } = rootNodeProps + const currentPageDisplayName = pageSortedKey[currentPageIndex] + const sectionNodeExecutionResult = useSelector((state) => { + const canvas = getCanvas(state) + const currentPageNode = searchDsl(canvas, currentPageDisplayName) + if (!currentPageNode) return null + const currentSectionNode = currentPageNode.childrenNode.find( + (node) => node.showName === sectionName, + ) + if (!currentSectionNode) return null + const currentSectionDisplayName = currentSectionNode.displayName + const execution = getSectionExecutionResultArray(state) + return execution[currentSectionDisplayName] || null + }) as Record + if (!sectionNodeExecutionResult) return null + return ( +
+ + +
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/interface.ts b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/interface.ts new file mode 100644 index 0000000000..d820acf396 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/interface.ts @@ -0,0 +1,41 @@ +import { SectionViewShape } from "@/redux/currentApp/editor/components/componentsState" + +export interface HeaderProps { + sectionName: string + sectionNodeExecutionResult: Record +} + +export interface ViewListProps { + sectionName: string +} + +export interface BodyProps { + sectionNodeExecutionResult: Record +} + +export interface ItemProps extends Omit { + name: string + otherKeys: string[] + isSelected: boolean + index: number + handleChangSectionView: (index: number) => void + handleDeleteSectionView: (index: number) => void + handleUpdateItem: (path: string, value: string) => void + attrPath: string +} + +export interface LabelNameAndDragIconProps { + name: string + isDuplicationKey: boolean + isSelected: boolean + index: number + handleChangSectionView: (index: number) => void +} + +export interface ModalProps { + onCloseModal: () => void + name: string + path: string + handleUpdateItem: (path: string, value: string) => void + attrPath: string +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/item.tsx b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/item.tsx new file mode 100644 index 0000000000..a3193fbdee --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/item.tsx @@ -0,0 +1,65 @@ +import { ReduceIcon, Trigger } from "@illa-design/react" +import { FC, useMemo, useState } from "react" +import { LabelNameAndDragIcon } from "./labelName" +import { deleteIconStyle, itemWrapperStyle } from "./style" +import { ItemProps } from "./interface" +import { Modal } from "./modal" +export const Item: FC = (props) => { + const { + name, + otherKeys, + isSelected, + index, + handleChangSectionView, + handleDeleteSectionView, + path, + handleUpdateItem, + attrPath, + } = props + const [modalVisible, setModalVisible] = useState(false) + const isDuplicationKey = useMemo(() => { + return otherKeys.some((viewKey) => viewKey == name) + }, [otherKeys, name]) + return ( + { + setModalVisible(false) + }} + name={name} + path={path} + handleUpdateItem={handleUpdateItem} + attrPath={attrPath} + /> + } + trigger="click" + showArrow={false} + position="left" + clickOutsideToClose + onVisibleChange={(visible) => { + setModalVisible(visible) + }} + > +
+ + { + e.stopPropagation() + handleDeleteSectionView(index) + }} + /> +
+
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/labelName.tsx b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/labelName.tsx new file mode 100644 index 0000000000..2bfccd058c --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/labelName.tsx @@ -0,0 +1,55 @@ +import { + DragIcon, + globalColor, + illaPrefix, + Trigger, + WarningCircleIcon, +} from "@illa-design/react" +import { FC } from "react" +import { useTranslation } from "react-i18next" +import { LabelNameAndDragIconProps } from "./interface" +import { + labelNameAndDragIconWrapperStyle, + labelNameStyle, + moveIconStyle, + selectedIconAndLabelNameWrapperStyle, + selectedIconStyle, +} from "./style" + +export const LabelNameAndDragIcon: FC = (props) => { + const { + name, + isDuplicationKey, + isSelected, + index, + handleChangSectionView, + } = props + const { t } = useTranslation() + return ( +
+ +
+
{ + e.stopPropagation() + handleChangSectionView(index) + }} + /> + {name} + {isDuplicationKey && ( + + + + )} +
+
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/modal.tsx b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/modal.tsx new file mode 100644 index 0000000000..18bc6e2a93 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/modal.tsx @@ -0,0 +1,59 @@ +import { CloseIcon, Input } from "@illa-design/react" +import { FC } from "react" +import { useTranslation } from "react-i18next" +import { LeftAndRightLayout } from "../../Layout/leftAndRight" +import { SetterPadding } from "../../Layout/setterPadding" +import { PageLabel } from "../Label" +import { ModalProps } from "./interface" +import { + modalHeaderCloseIconHotSpot, + modalHeaderWrapper, + modalWrapperStyle, + titleStyle, +} from "./style" + +export const Modal: FC = (props) => { + const { onCloseModal, name, path, handleUpdateItem, attrPath } = props + const { t } = useTranslation() + + return ( +
+
+ {t("editor.page.label_name.edit_view")} +
+ +
+
+ + + + { + handleUpdateItem(`${attrPath}.key`, value) + }} + /> + + + + + + { + handleUpdateItem(`${attrPath}.path`, value) + }} + /> + + +
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/style.tsx b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/style.tsx new file mode 100644 index 0000000000..0794b0101f --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/style.tsx @@ -0,0 +1,139 @@ +import { css } from "@emotion/react" +import { globalColor, illaPrefix } from "@illa-design/react" + +export const viewsListWrapperStyle = css` + display: flex; + flex-direction: column; + border: 1px solid ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + border-radius: 8px; + overflow: hidden; +` + +export const viewsListHeaderWrapperStyle = css` + display: flex; + justify-content: space-between; + align-items: center; + width: 287px; + height: 40px; + padding: 8px 8px 8px 16px; + background-color: ${globalColor(`--${illaPrefix}-grayBlue-09`)}; +` +export const viewsListBodyWrapperStyle = css` + width: 100%; + display: flex; + flex-direction: column; +` + +export const headerLabelStyle = css` + font-size: 14px; + font-weight: 500; + color: ${globalColor(`--${illaPrefix}-grayBlue-02`)}; +` + +export const headerAddIconStyle = css` + width: 8px; + height: 8px; +` + +export const labelNameAndDragIconWrapperStyle = css` + display: flex; + align-items: center; +` + +export const selectedIconAndLabelNameWrapperStyle = css` + display: flex; + max-width: 191px; + align-items: center; + gap: 8px; +` + +export const selectedIconStyle = (isSelected: boolean) => { + return css` + width: 16px; + height: 16px; + border: ${isSelected + ? `4px solid ${globalColor(`--${illaPrefix}-techPurple-01`)}` + : `2px solid ${globalColor(`--${illaPrefix}-grayBlue-08`)}`}; + border-radius: 50%; + ` +} + +export const labelNameStyle = css` + max-width: 147px; + font-size: 14px; + font-weight: 400; + color: ${globalColor(`--${illaPrefix}-grayBlue-02`)}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +` + +export const moveIconStyle = css` + cursor: move; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; + width: 16px; + height: 16px; + visibility: hidden; +` + +export const deleteIconStyle = css` + cursor: pointer; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; + width: 16px; + height: 16px; +` + +export const itemWrapperStyle = css` + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + padding: 9px 16px 9px 0; + gap: 4px; + height: 40px; + cursor: pointer; + :hover { + .dragIcon { + visibility: visible; + } + } +` + +export const modalWrapperStyle = css` + display: flex; + flex-direction: column; + align-items: center; + background-color: ${globalColor(`--${illaPrefix}-white-01`)}; + box-shadow: 0px 2px 16px rgba(0, 0, 0, 0.16); + border-radius: 8px; + overflow: hidden; + position: relative; +` + +export const modalHeaderWrapper = css` + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + width: 360px; +` + +export const modalHeaderCloseIconHotSpot = css` + width: 24px; + height: 24px; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + color: ${globalColor(`--${illaPrefix}-grayBlue-02`)}; + cursor: pointer; +` + +export const titleStyle = css` + font-weight: 500; + font-size: 16px; + color: ${globalColor(`--${illaPrefix}-grayBlue-04`)}; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; +` diff --git a/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/utils.ts b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/utils.ts new file mode 100644 index 0000000000..b6ca98701e --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Components/ViewsList/utils.ts @@ -0,0 +1,30 @@ +import { v4 } from "uuid" +import { SectionViewShape } from "@/redux/currentApp/editor/components/componentsState" + +export let viewNameSet = new Set() + +const generateDatasetName = (prefix: string) => { + let i = 1 + let ViewName = `View ${i}` + while (viewNameSet.has(`${prefix}-${ViewName}`)) { + i++ + ViewName = `View ${i}` + } + return ViewName +} + +export const generateNewViewItem = ( + hasViewNameSet: string[], + viewDisplayName: string, + prefix: string, +): SectionViewShape => { + viewNameSet = new Set(hasViewNameSet) + const viewName = generateDatasetName(prefix) + + return { + id: v4(), + key: viewName, + path: viewName, + viewDisplayName, + } +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/divider/index.tsx b/apps/builder/src/page/App/components/PagePanel/Layout/divider/index.tsx new file mode 100644 index 0000000000..51aa1ddba2 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/divider/index.tsx @@ -0,0 +1,13 @@ +import { Divider } from "@illa-design/react" +import { FC } from "react" +import { PanelDividerProps } from "./interface" +import { applyPanelDividerCss } from "./style" + +export const PanelDivider: FC = (props) => { + const { hasMargin = true } = props + return ( +
+ +
+ ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/divider/interface.ts b/apps/builder/src/page/App/components/PagePanel/Layout/divider/interface.ts new file mode 100644 index 0000000000..3e08641eca --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/divider/interface.ts @@ -0,0 +1,3 @@ +export interface PanelDividerProps { + hasMargin?: boolean +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/divider/style.ts b/apps/builder/src/page/App/components/PagePanel/Layout/divider/style.ts new file mode 100644 index 0000000000..eb8cbb5cd2 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/divider/style.ts @@ -0,0 +1,8 @@ +import { css } from "@emotion/react" + +export const applyPanelDividerCss = (hasMargin: boolean) => { + return css` + padding: 0 16px; + margin-top: ${hasMargin ? "8px" : 0}; + ` +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/index.tsx b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/index.tsx new file mode 100644 index 0000000000..ccb84ecfcb --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/index.tsx @@ -0,0 +1,8 @@ +import { FC } from "react" +import { LeftAndRightLayoutProps } from "./interface" +import { leftAndRightCss } from "./style" + +export const LeftAndRightLayout: FC = (props) => { + const { children } = props + return
{children}
+} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/interface.ts b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/interface.ts new file mode 100644 index 0000000000..08a8935822 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/interface.ts @@ -0,0 +1,4 @@ +import { ReactNode } from "react" +export interface LeftAndRightLayoutProps { + children?: ReactNode +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/style.ts b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/style.ts new file mode 100644 index 0000000000..015c7013b5 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/leftAndRight/style.ts @@ -0,0 +1,10 @@ +import { css } from "@emotion/react" + +export const leftAndRightCss = css` + min-height: 40px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 16px; + width: 100%; +` diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/index.tsx b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/index.tsx new file mode 100644 index 0000000000..b107819dac --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/index.tsx @@ -0,0 +1,8 @@ +import { FC } from "react" +import { SetterPaddingLayout } from "./interface" +import { setterPaddingStyle } from "./style" + +export const SetterPadding: FC = (props) => { + const { children } = props + return
{children}
+} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/interface.ts b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/interface.ts new file mode 100644 index 0000000000..d7546a7f16 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/interface.ts @@ -0,0 +1,5 @@ +import { ReactNode } from "react" + +export interface SetterPaddingLayout { + children: ReactNode +} diff --git a/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/style.ts b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/style.ts new file mode 100644 index 0000000000..208d935c06 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Layout/setterPadding/style.ts @@ -0,0 +1,5 @@ +import { css } from "@emotion/react" + +export const setterPaddingStyle = css` + padding: 8px 0px; +` diff --git a/apps/builder/src/page/App/components/PagePanel/Modules/Basic/index.tsx b/apps/builder/src/page/App/components/PagePanel/Modules/Basic/index.tsx new file mode 100644 index 0000000000..c8c050c539 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Modules/Basic/index.tsx @@ -0,0 +1,245 @@ +import { FC, useCallback, useMemo } from "react" +import { PanelBar } from "@/components/PanelBar" +import { useTranslation } from "react-i18next" +import { LeftAndRightLayout } from "@/page/App/components/PagePanel/Layout/leftAndRight" +import { PageLabel } from "@/page/App/components/PagePanel/Components/Label" +import { SetterPadding } from "@/page/App/components/PagePanel/Layout/setterPadding" +import { Input, Switch } from "@illa-design/react" +import { useDispatch, useSelector } from "react-redux" +import { componentsActions } from "@/redux/currentApp/editor/components/componentsSlice" +import { getRootNodeExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { ViewList } from "@/page/App/components/PagePanel/Components/ViewsList" +import { RootState } from "@/store" +import { + getCanvas, + searchDsl, +} from "@/redux/currentApp/editor/components/componentsSelector" +import { + PageNodeProps, + SectionNode, +} from "@/redux/currentApp/editor/components/componentsState" + +export const PageBasic: FC = () => { + const { t } = useTranslation() + const dispatch = useDispatch() + const rootExecutionProps = useSelector(getRootNodeExecutionResult) + + const { + currentPageIndex, + pageSortedKey, + homepageDisplayName, + } = rootExecutionProps + const currentPageDisplayName = pageSortedKey[currentPageIndex] + const pageProps = useSelector((state) => { + const canvas = getCanvas(state) + return searchDsl(canvas, currentPageDisplayName)?.props || {} + }) as PageNodeProps + const sectionNodes = useSelector((state) => { + const canvas = getCanvas(state) + const currentPageNode = searchDsl(canvas, currentPageDisplayName) + if (!currentPageNode) return null + return currentPageNode.childrenNode + }) as SectionNode[] | null + + const { hasLeft, hasRight, hasFooter, hasHeader } = pageProps + const isHomepage = useMemo(() => { + return homepageDisplayName + ? homepageDisplayName === currentPageDisplayName + : currentPageDisplayName === "page1" + }, [currentPageDisplayName, homepageDisplayName]) + + const handleChangeIsHomePage = useCallback( + (value?: boolean) => { + if (currentPageDisplayName !== homepageDisplayName) { + dispatch( + componentsActions.updateRootNodePropsReducer({ + homepageDisplayName: currentPageDisplayName, + }), + ) + } + }, + [currentPageDisplayName, dispatch, homepageDisplayName], + ) + + const targetDefaultViewValue = useCallback( + (showName: string) => { + if (!Array.isArray(sectionNodes)) return "" + const targetSectionNode = sectionNodes.find((node) => { + return node.showName === showName + }) + if (!targetSectionNode) return "" + return targetSectionNode.props.defaultViewKey + }, + [sectionNodes], + ) + + const handleChangeDefaultView = useCallback( + (value: string, showName: string) => { + if (!Array.isArray(sectionNodes)) return + const targetSectionNode = sectionNodes.find((node) => { + return node.showName === showName + }) + if (!targetSectionNode) return + dispatch( + componentsActions.updateSectionViewPropsReducer({ + parentNodeName: targetSectionNode.displayName, + newProps: { + defaultViewKey: value, + }, + }), + ) + }, + [dispatch, sectionNodes], + ) + return ( + + + + + + + + + + + + + + + + + + + { + handleChangeDefaultView(value, "bodySection") + }} + /> + + + {hasLeft && ( + <> + + + + + + + + + + + + { + handleChangeDefaultView(value, "leftSection") + }} + /> + + + + )} + {hasRight && ( + <> + + + + + + + + + + + + { + handleChangeDefaultView(value, "rightSection") + }} + /> + + + + )} + {hasHeader && ( + <> + + + + + + + + + + + + { + handleChangeDefaultView(value, "headerSection") + }} + /> + + + + )} + {hasFooter && ( + <> + + + + + + + + + + + + { + handleChangeDefaultView(value, "footerSection") + }} + /> + + + + )} + + ) +} diff --git a/apps/builder/src/page/App/components/PagePanel/Modules/Basic/style.ts b/apps/builder/src/page/App/components/PagePanel/Modules/Basic/style.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/builder/src/page/App/components/PagePanel/Modules/Frame/index.tsx b/apps/builder/src/page/App/components/PagePanel/Modules/Frame/index.tsx new file mode 100644 index 0000000000..69bdbe0f6e --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Modules/Frame/index.tsx @@ -0,0 +1,645 @@ +import { FC, useCallback, useMemo } from "react" +import { PanelBar } from "@/components/PanelBar" +import { useTranslation } from "react-i18next" +import { InputNumber, Modal, Switch } from "@illa-design/react" +import { ReactComponent as FrameFixedIcon } from "@/assets/rightPagePanel/frame-fixed.svg" +import { ReactComponent as FrameResponsiveIcon } from "@/assets/rightPagePanel/frame-responsive.svg" +import { PageLabel } from "@/page/App/components/PagePanel/Components/Label" +import { LayoutSelect } from "@/page/App/components/PagePanel/Components/LayoutSelect" +import { LeftAndRightLayout } from "@/page/App/components/PagePanel/Layout/leftAndRight" +import { SetterPadding } from "@/page/App/components/PagePanel/Layout/setterPadding" +import { PanelDivider } from "@/page/App/components/PagePanel/Layout/divider" +import { useDispatch, useSelector } from "react-redux" +import { + getCanvas, + searchDsl, +} from "@/redux/currentApp/editor/components/componentsSelector" +import { PageNodeProps } from "@/redux/currentApp/editor/components/componentsState" +import { PanelActionBar } from "@/page/App/components/PagePanel/Components/PanelActionBar" +import { componentsActions } from "@/redux/currentApp/editor/components/componentsSlice" +import { getCanvasShape } from "@/redux/config/configSelector" +import { + BODY_MIN_HEIGHT, + BODY_MIN_WIDTH, + FOOTER_MIN_HEIGHT, + HEADER_MIN_HEIGHT, + LEFT_MIN_WIDTH, + RIGHT_MIN_WIDTH, +} from "@/page/App/components/DotPanel/renderSection" +import { groupWrapperStyle } from "./style" +import { getRootNodeExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { RootState } from "@/store" + +const getRealCanvasWidth = ( + canvasSize: "fixed" | "responsive", + canvasWidth: string, +) => { + if (canvasSize === "fixed") return canvasWidth + return "auto" +} + +const canvasSizeOptions = [ + { label: , value: "fixed" }, + { label: , value: "responsive" }, +] + +export const PageFrame: FC = () => { + const { t } = useTranslation() + const rootNodeProps = useSelector(getRootNodeExecutionResult) + const { currentPageIndex, pageSortedKey } = rootNodeProps + const currentPageDisplayName = pageSortedKey[currentPageIndex] + const pageProps = useSelector((state) => { + const canvas = getCanvas(state) + return searchDsl(canvas, currentPageDisplayName)?.props || {} + }) as PageNodeProps + + const canvasShape = useSelector(getCanvasShape) + const dispatch = useDispatch() + const { + canvasSize, + canvasWidth, + layout, + leftWidth, + rightWidth, + topHeight, + bottomHeight, + isLeftFixed, + isRightFixed, + isFooterFixed, + isHeaderFixed, + hasLeft, + hasRight, + hasFooter, + hasHeader, + showLeftFoldIcon, + showRightFoldIcon, + } = pageProps + + const bodyWidth = useMemo(() => { + if (canvasSize === "responsive") { + return 100 - leftWidth - rightWidth + } else { + return 0 + } + }, [canvasSize, leftWidth, rightWidth]) + + const finalCanvasWidth = getRealCanvasWidth(canvasSize, canvasWidth) + const widthI18n = useMemo(() => { + return canvasSize === "fixed" + ? `${t("editor.page.label_name.width")}(px)` + : `${t("editor.page.label_name.width")}(%)` + }, [canvasSize, t]) + + const handleDeleteSection = useCallback( + ( + deleteSectionName: + | "leftSection" + | "rightSection" + | "headerSection" + | "footerSection", + options: Record, + ) => { + if (!currentPageDisplayName) return + Modal.confirm({ + w: "496px", + content: t("editor.page.model_tips.delete_section_message"), + cancelText: t("editor.page.model_tips.cancel_button"), + okText: t("editor.page.model_tips.ok_button"), + okButtonProps: { + colorScheme: "red", + }, + closable: false, + onOk: () => { + dispatch( + componentsActions.deleteTargetPageSectionReducer({ + pageName: currentPageDisplayName, + deleteSectionName, + options, + }), + ) + }, + }) + }, + [currentPageDisplayName, dispatch, t], + ) + + const handleAddSection = useCallback( + ( + addedSectionName: + | "leftSection" + | "rightSection" + | "headerSection" + | "footerSection", + options: Record, + ) => { + if (!currentPageDisplayName) return + dispatch( + componentsActions.addTargetPageSectionReducer({ + pageName: currentPageDisplayName, + addedSectionName, + options, + }), + ) + }, + [currentPageDisplayName, dispatch], + ) + + const handleUpdateBodyPanelWidth = useCallback( + (value?: number) => { + if (!currentPageDisplayName || !value) return + let finalValue = value + let finalLeftValue = leftWidth + let finalRightValue = rightWidth + if (canvasSize === "responsive") { + let finalLeftValuePX = (finalLeftValue / 100) * canvasShape.canvasWidth + let finalRightValuePX = + (finalRightValue / 100) * canvasShape.canvasWidth + let finalValuePX = (finalValue / 100) * canvasShape.canvasWidth + if (finalValuePX <= BODY_MIN_HEIGHT) { + const restWidth = canvasShape.canvasWidth - BODY_MIN_HEIGHT + finalLeftValuePX = + (finalLeftValuePX / (finalLeftValuePX + finalRightValuePX)) * + restWidth + finalRightValuePX = restWidth - finalLeftValuePX + finalLeftValue = (finalLeftValuePX / canvasShape.canvasWidth) * 100 + finalRightValue = (finalRightValuePX / canvasShape.canvasWidth) * 100 + } else { + let restWidth = canvasShape.canvasWidth - finalValuePX + finalLeftValuePX = + (finalLeftValuePX / (finalLeftValuePX + finalRightValuePX)) * + restWidth + if (finalLeftValuePX < LEFT_MIN_WIDTH) { + finalLeftValuePX = LEFT_MIN_WIDTH + } + finalRightValuePX = restWidth - finalLeftValuePX + if (finalRightValuePX < RIGHT_MIN_WIDTH) { + finalRightValuePX = LEFT_MIN_WIDTH + } + finalLeftValue = (finalLeftValuePX / canvasShape.canvasWidth) * 100 + finalRightValue = (RIGHT_MIN_WIDTH / canvasShape.canvasWidth) * 100 + } + } + if (canvasSize === "fixed") { + } + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + leftWidth: finalLeftValue, + rightWidth: finalRightValue, + }, + }), + ) + }, + [ + canvasShape.canvasWidth, + canvasSize, + currentPageDisplayName, + dispatch, + leftWidth, + rightWidth, + ], + ) + + const handleUpdateLeftPanelWidth = useCallback( + (value?: number) => { + if (!currentPageDisplayName || !value) return + let finalValue = value + if (canvasSize === "responsive") { + const leftWidthPX = (value / 100) * canvasShape.canvasWidth + const currentRightWidthPX = (rightWidth / 100) * canvasShape.canvasWidth + if ( + canvasShape.canvasWidth - leftWidthPX - currentRightWidthPX < + BODY_MIN_WIDTH + ) { + finalValue = + ((canvasShape.canvasWidth - currentRightWidthPX - BODY_MIN_WIDTH) / + canvasShape.canvasWidth) * + 100 + } + if ((value / 100) * canvasShape.canvasWidth < LEFT_MIN_WIDTH) { + finalValue = (LEFT_MIN_WIDTH / canvasShape.canvasWidth) * 100 + } + } + if (canvasSize === "fixed") { + } + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + leftWidth: finalValue, + }, + }), + ) + }, + [ + canvasShape.canvasWidth, + canvasSize, + currentPageDisplayName, + dispatch, + rightWidth, + ], + ) + + const handleUpdateRightPanelWidth = useCallback( + (value?: number) => { + if (!currentPageDisplayName || !value) return + let finalValue = value + if (canvasSize === "responsive") { + const rightWidthPX = (value / 100) * canvasShape.canvasWidth + const currentLeftWidthPX = (leftWidth / 100) * canvasShape.canvasWidth + if ( + canvasShape.canvasWidth - rightWidthPX - currentLeftWidthPX < + BODY_MIN_WIDTH + ) { + finalValue = + ((canvasShape.canvasWidth - currentLeftWidthPX - BODY_MIN_WIDTH) / + canvasShape.canvasWidth) * + 100 + } + if ((value / 100) * canvasShape.canvasWidth < RIGHT_MIN_WIDTH) { + finalValue = (RIGHT_MIN_WIDTH / canvasShape.canvasWidth) * 100 + } + } + if (canvasSize === "fixed") { + } + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + rightWidth: finalValue, + }, + }), + ) + }, + [ + canvasShape.canvasWidth, + canvasSize, + currentPageDisplayName, + dispatch, + leftWidth, + ], + ) + + const handleUpdateHeaderPanelWidth = useCallback( + (value?: number) => { + if (!currentPageDisplayName || !value) return + let finalValue = value + if (canvasSize === "responsive") { + if (canvasShape.canvasHeight - value - bottomHeight < BODY_MIN_HEIGHT) { + finalValue = canvasShape.canvasHeight - bottomHeight - BODY_MIN_HEIGHT + } + if (value < HEADER_MIN_HEIGHT) { + finalValue = HEADER_MIN_HEIGHT + } + } + if (canvasSize === "fixed") { + } + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + topHeight: finalValue, + }, + }), + ) + }, + [ + bottomHeight, + canvasShape.canvasHeight, + canvasSize, + currentPageDisplayName, + dispatch, + ], + ) + + const handleUpdateFooterPanelWidth = useCallback( + (value?: number) => { + if (!currentPageDisplayName || !value) return + let finalValue = value + if (canvasSize === "responsive") { + if (canvasShape.canvasHeight - value - topHeight < BODY_MIN_HEIGHT) { + finalValue = canvasShape.canvasHeight - topHeight - BODY_MIN_HEIGHT + } + if (value < FOOTER_MIN_HEIGHT) { + finalValue = FOOTER_MIN_HEIGHT + } + } + if (canvasSize === "fixed") { + } + + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps: { + bottomHeight: finalValue, + }, + }), + ) + }, + [ + canvasShape.canvasHeight, + canvasSize, + currentPageDisplayName, + dispatch, + topHeight, + ], + ) + + const handleUpdateShowFoldIcon = useCallback( + (value: boolean, sectionName: string) => { + if (!currentPageDisplayName) return + const newProps = + sectionName === "leftSection" + ? { + showLeftFoldIcon: value, + } + : { showRightFoldIcon: value } + dispatch( + componentsActions.updateTargetPagePropsReducer({ + pageName: currentPageDisplayName, + newProps, + }), + ) + }, + [currentPageDisplayName, dispatch], + ) + + return ( + + {/* + + + + + + + + + */} + + + + + + + +
+ + + + { + handleDeleteSection("leftSection", { + hasLeft: false, + leftWidth: 0, + leftPosition: "NONE", + layout: "Custom", + }) + }} + addPanelAction={() => { + handleAddSection("leftSection", { + hasLeft: true, + leftWidth: 20, + leftPosition: "FULL", + layout: "Custom", + }) + }} + /> + + + {hasLeft && ( + <> + + + + + + + + + + { + handleUpdateShowFoldIcon(value, "leftSection") + }} + /> + + + + )} +
+ +
+ + + + { + handleDeleteSection("rightSection", { + hasRight: false, + rightWidth: 0, + rightPosition: "NONE", + layout: "Custom", + }) + }} + addPanelAction={() => { + handleAddSection("rightSection", { + hasRight: true, + rightWidth: 20, + rightPosition: "FULL", + layout: "Custom", + }) + }} + /> + + + {hasRight && ( + <> + + + + + + + + + + { + handleUpdateShowFoldIcon(value, "rightSection") + }} + /> + + + + )} +
+ + +
+ + + + + + + + + +
+ +
+ + + + { + handleDeleteSection("headerSection", { + hasHeader: false, + topHeight: 0, + layout: "Custom", + }) + }} + addPanelAction={() => { + handleAddSection("headerSection", { + hasHeader: true, + topHeight: 96, + layout: "Custom", + }) + }} + /> + + + {hasHeader && ( + + + + + + + )} +
+ +
+ + + + { + handleDeleteSection("footerSection", { + hasFooter: false, + bottomHeight: 0, + layout: "Custom", + }) + }} + addPanelAction={() => { + handleAddSection("footerSection", { + hasFooter: true, + bottomHeight: 96, + layout: "Custom", + }) + }} + /> + + + {hasFooter && ( + + + + + + + )} +
+
+ ) +} + +PageFrame.displayName = "PageFrame" diff --git a/apps/builder/src/page/App/components/PagePanel/Modules/Frame/style.ts b/apps/builder/src/page/App/components/PagePanel/Modules/Frame/style.ts new file mode 100644 index 0000000000..82b0343582 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/Modules/Frame/style.ts @@ -0,0 +1,5 @@ +import { css } from "@emotion/react" + +export const groupWrapperStyle = css` + padding: 8px 0; +` diff --git a/apps/builder/src/page/App/components/PagePanel/index.tsx b/apps/builder/src/page/App/components/PagePanel/index.tsx new file mode 100644 index 0000000000..2ccb3a413c --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/index.tsx @@ -0,0 +1,19 @@ +import { FC } from "react" +import { PagePanelWrapperStyle, PageScrollContainerWrapperStyle } from "./style" +import { PageFrame } from "./Modules/Frame" +import { PageBasic } from "./Modules/Basic" +import { PanelHeader } from "./Components/PanelHeader/header" + +export const PagePanel: FC = () => { + return ( +
+
+ + + +
+
+ ) +} + +PagePanel.displayName = "PagePanel" diff --git a/apps/builder/src/page/App/components/PagePanel/style.ts b/apps/builder/src/page/App/components/PagePanel/style.ts new file mode 100644 index 0000000000..2ac5e69750 --- /dev/null +++ b/apps/builder/src/page/App/components/PagePanel/style.ts @@ -0,0 +1,25 @@ +import { globalColor, illaPrefix } from "@illa-design/theme" +import { css } from "@emotion/react" + +export const PagePanelWrapperStyle = css` + border-top: 1px solid ${globalColor(`--${illaPrefix}-grayBlue-08`)}; + height: 100%; + width: 100%; + padding-bottom: 16px; + overflow: hidden; + display: flex; + flex-direction: column; +` + +export const PageScrollContainerWrapperStyle = css` + width: 100%; + height: 100%; + overflow-y: auto; +` + +export const headerWrapperStyle = css` + display: flex; + height: 48px; + align-items: center; + padding: 0 16px; +` diff --git a/apps/builder/src/page/App/components/PanelSetters/ChartSetter/chartKeysSelectSetter.tsx b/apps/builder/src/page/App/components/PanelSetters/ChartSetter/chartKeysSelectSetter.tsx index 13b537ff00..4123da6493 100644 --- a/apps/builder/src/page/App/components/PanelSetters/ChartSetter/chartKeysSelectSetter.tsx +++ b/apps/builder/src/page/App/components/PanelSetters/ChartSetter/chartKeysSelectSetter.tsx @@ -97,7 +97,6 @@ export const ChartKeysSelectSetter: FC = ( if (attrName === "groupBy") { if ((!!value && !newValue) || (!value && !!newValue)) { const newDatasets = generateNewDatasets(!!newValue) - console.log("newDatasets", newDatasets) handleUpdateMultiAttrDSL?.({ datasets: newDatasets, [attrName]: newValue, diff --git a/apps/builder/src/page/App/components/PanelSetters/SelectSetter/eventBodyViewSelect.tsx b/apps/builder/src/page/App/components/PanelSetters/SelectSetter/eventBodyViewSelect.tsx new file mode 100644 index 0000000000..ffe8fec4f2 --- /dev/null +++ b/apps/builder/src/page/App/components/PanelSetters/SelectSetter/eventBodyViewSelect.tsx @@ -0,0 +1,76 @@ +import { FC, useMemo } from "react" +import { useSelector } from "react-redux" +import { Select } from "@illa-design/select" +import { BaseSelectSetterProps } from "./interface" +import { applyBaseSelectWrapperStyle } from "@/page/App/components/PanelSetters/SelectSetter/style" +import { get } from "lodash" +import { + getCanvas, + searchDsl, +} from "@/redux/currentApp/editor/components/componentsSelector" +import { RootState } from "@/store" +import { PageNode } from "@/redux/currentApp/editor/components/componentsState" + +export const EventTargetViewSelect: FC = (props) => { + const { + isSetterSingleRow, + attrName, + handleUpdateDsl, + value, + componentNode, + placeholder, + } = props + let parentAttrNameArray = attrName.split(".") + parentAttrNameArray.splice(-1, 1) + let finalParentPath = `props.${parentAttrNameArray.join(".")}` + const parentAttr = get(componentNode, finalParentPath) + const pagePath = get(parentAttr, "pagePath") + const pageComponent = useSelector((state) => { + const canvas = getCanvas(state) + if (!canvas) return null + return searchDsl(canvas, pagePath) || null + }) as PageNode | null + const finalOptions = useMemo(() => { + if (!pageComponent) return [] + const options: { label: string; value: string }[] = [] + pageComponent.childrenNode.forEach((node) => { + const { props } = node + if ( + props && + Array.isArray(props.viewSortedKey) && + Array.isArray(props.sectionViewConfigs) + ) { + props.sectionViewConfigs.forEach((config) => { + options.push({ + label: config.path, + value: config.path, + }) + }) + } + }) + return options + }, [pageComponent]) + + const finalValue = useMemo(() => { + const index = finalOptions.findIndex((option) => { + return option.value === value + }) + if (index !== -1) return value + return undefined + }, [finalOptions, value]) + + return ( +
+ { + handleUpdateDsl(attrName, value) + }} + placeholder={placeholder} + /> +
+ ) +} diff --git a/apps/builder/src/page/App/components/PanelSetters/index.tsx b/apps/builder/src/page/App/components/PanelSetters/index.tsx index 898b14d685..5604eaed1d 100644 --- a/apps/builder/src/page/App/components/PanelSetters/index.tsx +++ b/apps/builder/src/page/App/components/PanelSetters/index.tsx @@ -30,6 +30,8 @@ import { ContainerDefaultViewKeySetter } from "@/page/App/components/PanelSetter import { TabListSetter } from "@/page/App/components/PanelSetters/TabsSetter/TabListSetter" import { TabsContainerSelectSetter } from "@/page/App/components/PanelSetters/TabsSetter/TabsContainerSelectSetter" import { TabsDefaultKeySetter } from "@/page/App/components/PanelSetters/TabsSetter/defaultTabKeySetter" +import { EventTargetPageSelect } from "./SelectSetter/pageSelect" +import { EventTargetViewSelect } from "./SelectSetter/eventBodyViewSelect" const SetterTypeMapSetter = { INPUT_SETTER: BaseInput, @@ -52,6 +54,8 @@ const SetterTypeMapSetter = { EVENT_HANDLER_SETTER: EventHandlerSetter, EVENT_TARGET_SELECT_SETTER: EventTargetWidgetSelect, EVENT_TARGET_ACTION_SELECT_SETTER: EventTargetActionSelect, + EVENT_TARGET_PAGE_SELECT_SETTER: EventTargetPageSelect, + EVENT_TARGET_VIEW_PATH_SELECT_SETTER: EventTargetViewSelect, OPTION_MAPPED_INPUT_SETTER: OptionMappedInputSetter, EVENT_WIDGET_METHOD_SELECT_SETTER: EventWidgetMethodSelect, EVENT_ACTION_SELECT_SETTER: EventActionTypeSelect, diff --git a/apps/builder/src/page/App/components/ScaleSquare/index.tsx b/apps/builder/src/page/App/components/ScaleSquare/index.tsx index ad6d230665..3d549e05c4 100644 --- a/apps/builder/src/page/App/components/ScaleSquare/index.tsx +++ b/apps/builder/src/page/App/components/ScaleSquare/index.tsx @@ -69,6 +69,7 @@ export const ScaleSquare = memo((props: ScaleSquareProps) => { containerHeight, childrenNode, collisionEffect, + columnsNumber, } = props const canRenderDashedLine = !collisionEffect.has(componentNode.displayName) @@ -261,6 +262,7 @@ export const ScaleSquare = memo((props: ScaleSquareProps) => { return { item: componentNode, childrenNodes, + currentColumnNumber: columnsNumber, } }, collect: (monitor) => { diff --git a/apps/builder/src/page/App/components/ScaleSquare/interface.ts b/apps/builder/src/page/App/components/ScaleSquare/interface.ts index cea4a64d5b..cc33d9162f 100644 --- a/apps/builder/src/page/App/components/ScaleSquare/interface.ts +++ b/apps/builder/src/page/App/components/ScaleSquare/interface.ts @@ -15,6 +15,7 @@ export interface ScaleSquareProps extends HTMLAttributes { containerPadding: number childrenNode: ComponentNode[] collisionEffect: Map + columnsNumber: number } export interface MoveBarProps { diff --git a/apps/builder/src/page/App/index.tsx b/apps/builder/src/page/App/index.tsx index 07ba858b75..77e7249540 100644 --- a/apps/builder/src/page/App/index.tsx +++ b/apps/builder/src/page/App/index.tsx @@ -46,7 +46,7 @@ export const Editor: FC = () => { const currentUser = useSelector(getCurrentUser) useEffect(() => { - if (currentUser != null && currentUser.userId != 0) { + if (currentUser != null && currentUser.userId != "") { Connection.enterRoom( "app", appId ?? "", diff --git a/apps/builder/src/page/App/style.tsx b/apps/builder/src/page/App/style.tsx index 0d94e95027..ed2303240c 100644 --- a/apps/builder/src/page/App/style.tsx +++ b/apps/builder/src/page/App/style.tsx @@ -32,6 +32,7 @@ export const navbarStyle = css` box-sizing: border-box; width: 100%; height: ${NAVBAR_HEIGHT}px; + flex: none; ` export const leftPanelStyle = css` diff --git a/apps/builder/src/page/Dashboard/components/CreateNewModal/index.tsx b/apps/builder/src/page/Dashboard/components/CreateNewModal/index.tsx index 8e4a66874e..65fc44ef74 100644 --- a/apps/builder/src/page/Dashboard/components/CreateNewModal/index.tsx +++ b/apps/builder/src/page/Dashboard/components/CreateNewModal/index.tsx @@ -9,6 +9,7 @@ import { DashboardApp } from "@/redux/dashboard/apps/dashboardAppState" import { Api } from "@/api/base" import { dashboardAppActions } from "@/redux/dashboard/apps/dashboardAppSlice" import { useNavigate } from "react-router-dom" +import { BASIC_APP_CONFIG } from "@/config/newAppConfig" export const CreateNewModal: FC = (props) => { const { visible, onVisibleChange } = props @@ -47,6 +48,7 @@ export const CreateNewModal: FC = (props) => { method: "POST", data: { appName: name, + initScheme: BASIC_APP_CONFIG, }, }, (response) => { diff --git a/apps/builder/src/redux/config/configPayload.ts b/apps/builder/src/redux/config/configPayload.ts index 18e95bb14a..b85f7f8a4c 100644 --- a/apps/builder/src/redux/config/configPayload.ts +++ b/apps/builder/src/redux/config/configPayload.ts @@ -10,3 +10,8 @@ export interface UpdateParamsPayload { index: number params: Params } + +export interface UpdateCanvasShapePayload { + canvasWidth: number + canvasHeight: number +} diff --git a/apps/builder/src/redux/config/configReducer.ts b/apps/builder/src/redux/config/configReducer.ts index c3cc28aad7..fde88bf0c4 100644 --- a/apps/builder/src/redux/config/configReducer.ts +++ b/apps/builder/src/redux/config/configReducer.ts @@ -8,6 +8,7 @@ import { ActionContent, ActionItem, } from "@/redux/currentApp/action/actionState" +import { UpdateCanvasShapePayload } from "./configPayload" export const updateLeftPanel: CaseReducer< ConfigState, @@ -121,3 +122,12 @@ export const updateFreezeStateReducer: CaseReducer< > = (state, action) => { state.freezeCanvas = action.payload } + +export const updateCanvasShapeReducer: CaseReducer< + ConfigState, + PayloadAction +> = (state, action) => { + const { canvasHeight, canvasWidth } = action.payload + state.canvasHeight = canvasHeight + state.canvasWidth = canvasWidth +} diff --git a/apps/builder/src/redux/config/configSelector.ts b/apps/builder/src/redux/config/configSelector.ts index b7ade8ce4a..c7c51d819c 100644 --- a/apps/builder/src/redux/config/configSelector.ts +++ b/apps/builder/src/redux/config/configSelector.ts @@ -59,3 +59,10 @@ export const getExpandedKeys = (state: RootState) => { export const getFreezeState = (state: RootState) => { return state.config.freezeCanvas } + +export const getCanvasShape = (state: RootState) => { + return { + canvasWidth: state.config.canvasWidth, + canvasHeight: state.config.canvasHeight, + } +} diff --git a/apps/builder/src/redux/config/configSlice.ts b/apps/builder/src/redux/config/configSlice.ts index c531bfc965..a7d075b221 100644 --- a/apps/builder/src/redux/config/configSlice.ts +++ b/apps/builder/src/redux/config/configSlice.ts @@ -17,6 +17,7 @@ import { updateRightPanel, updateSelectedComponent, updateShowDot, + updateCanvasShapeReducer, } from "@/redux/config/configReducer" const configSlice = createSlice({ @@ -39,6 +40,7 @@ const configSlice = createSlice({ setExpandedKey, removeExpandedKey, updateFreezeStateReducer, + updateCanvasShapeReducer, }, }) diff --git a/apps/builder/src/redux/config/configState.ts b/apps/builder/src/redux/config/configState.ts index d908f081ee..0bb21f0aa7 100644 --- a/apps/builder/src/redux/config/configState.ts +++ b/apps/builder/src/redux/config/configState.ts @@ -18,6 +18,8 @@ export interface ConfigState { expandedKeys: string[] mode: IllaMode freezeCanvas: boolean + canvasHeight: number + canvasWidth: number } export const ConfigInitialState: ConfigState = { @@ -33,4 +35,6 @@ export const ConfigInitialState: ConfigState = { showDot: false, expandedKeys: [], freezeCanvas: false, + canvasHeight: 1080, + canvasWidth: 1920, } diff --git a/apps/builder/src/redux/currentApp/editor/components/componentsListener.ts b/apps/builder/src/redux/currentApp/editor/components/componentsListener.ts index 6a5a8e93bd..6c28611c83 100644 --- a/apps/builder/src/redux/currentApp/editor/components/componentsListener.ts +++ b/apps/builder/src/redux/currentApp/editor/components/componentsListener.ts @@ -6,8 +6,10 @@ import { AppListenerEffectAPI, AppStartListening } from "@/store" import { Unsubscribe, isAnyOf, AnyAction } from "@reduxjs/toolkit" import { componentsActions } from "@/redux/currentApp/editor/components/componentsSlice" import { getReflowResult } from "@/page/App/components/DotPanel/calc" -import { ComponentNode } from "./componentsState" +import { ComponentNode, CONTAINER_TYPE } from "./componentsState" import { configActions } from "@/redux/config/configSlice" +import { executionActions } from "@/redux/currentApp/executionTree/executionSlice" +import { getExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" function handleCopyComponentReflowEffect( action: ReturnType, @@ -60,13 +62,65 @@ function handleUpdateComponentDisplayNameEffect( const rootState = listenApi.getState() const rootNode = getCanvas(rootState) const newComponent = searchDsl(rootNode, newDisplayName) - if (newComponent) { + if ( + newComponent && + newComponent.containerType === CONTAINER_TYPE.EDITOR_SCALE_SQUARE + ) { listenApi.dispatch( configActions.updateSelectedComponent([newComponent.displayName]), ) } } +async function handleChangeCurrentPageWhenDelete( + action: ReturnType, + listenerApi: AppListenerEffectAPI, +) { + const rootState = listenerApi.getState() + const executionTree = getExecutionResult(rootState) + const rootNode = executionTree.root + const { displayName, originPageSortedKey } = action.payload + const oldIndex = originPageSortedKey.findIndex((key) => key === displayName) + if (oldIndex === rootNode.currentPageIndex) { + listenerApi.dispatch( + executionActions.updateExecutionByDisplayNameReducer({ + displayName: "root", + value: { + currentPageIndex: 0, + }, + }), + ) + } +} + +async function handleChangeCurrentSectionWhenDelete( + action: ReturnType, + listenerApi: AppListenerEffectAPI, +) { + const { + viewDisplayName, + originPageSortedKey, + parentNodeName, + } = action.payload + const rootState = listenerApi.getState() + const executionTree = getExecutionResult(rootState) + const parentNode = executionTree[parentNodeName] + if (!parentNode) return + const oldIndex = originPageSortedKey.findIndex( + (key) => key === viewDisplayName, + ) + if (oldIndex === parentNode.currentViewIndex) { + listenerApi.dispatch( + executionActions.updateExecutionByDisplayNameReducer({ + displayName: parentNodeName, + value: { + currentViewIndex: 0, + }, + }), + ) + } +} + function handleUpdateComponentReflowEffect( action: AnyAction, listenApi: AppListenerEffectAPI, @@ -134,6 +188,14 @@ export function setupComponentsListeners( ), effect: handleUpdateComponentReflowEffect, }), + startListening({ + actionCreator: componentsActions.deletePageNodeReducer, + effect: handleChangeCurrentPageWhenDelete, + }), + startListening({ + actionCreator: componentsActions.deleteSectionViewReducer, + effect: handleChangeCurrentSectionWhenDelete, + }), ] return () => { diff --git a/apps/builder/src/redux/currentApp/editor/components/componentsReducer.ts b/apps/builder/src/redux/currentApp/editor/components/componentsReducer.ts index 4bfedf5de7..113f0761a1 100644 --- a/apps/builder/src/redux/currentApp/editor/components/componentsReducer.ts +++ b/apps/builder/src/redux/currentApp/editor/components/componentsReducer.ts @@ -1,13 +1,25 @@ import { CaseReducer, PayloadAction } from "@reduxjs/toolkit" import { + AddTargetPageSectionPayload, ComponentNode, ComponentsState, CopyComponentPayload, DeleteComponentNodePayload, + DeleteTargetPageSectionPayload, + PageNodeProps, + RootComponentNodeProps, sortComponentNodeChildrenPayload, UpdateComponentDisplayNamePayload, UpdateComponentPropsPayload, UpdateComponentReflowPayload, + UpdateTargetPageLayoutPayload, + UpdateTargetPagePropsPayload, + RootComponentNode, + DeletePageNodePayload, + AddSectionViewPayload, + DeleteSectionViewPayload, + SectionViewShape, + UpdateSectionViewPropsPayload, } from "@/redux/currentApp/editor/components/componentsState" import { cloneDeep } from "lodash" import { searchDsl } from "@/redux/currentApp/editor/components/componentsSelector" @@ -18,6 +30,11 @@ import { UpdateComponentsShapePayload, } from "@/redux/currentApp/editor/components/componentsPayload" import { DisplayNameGenerator } from "@/utils/generators/generateDisplayName" +import { + generateSectionConfig, + generateSectionContainerConfig, + layoutValueMapGenerateConfig, +} from "@/utils/generators/generatePageOrSectionConfig" export const updateComponentReducer: CaseReducer< ComponentsState, @@ -112,6 +129,51 @@ export const deleteComponentNodeReducer: CaseReducer< DisplayNameGenerator.removeDisplayNameMulti(allDisplayNames) } +export const deletePageNodeReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + const { displayName } = action.payload + if (state == null) { + return + } + const rootNode = state + const allDisplayNames = [displayName] + + const searchNode = searchDsl(rootNode, displayName) + if (!searchNode) return + const searchNodeChildNodes = searchNode.childrenNode + searchNodeChildNodes?.forEach((node) => { + allDisplayNames.push(node.displayName) + }) + const parentNode = rootNode as RootComponentNode + const childrenNodes = parentNode.childrenNode + const currentIndex = childrenNodes.findIndex((value) => { + return value.displayName === searchNode.displayName + }) + childrenNodes.splice(currentIndex, 1) + const indexOfSortedKey = parentNode.props.pageSortedKey.findIndex( + (key) => key === displayName, + ) + if (indexOfSortedKey === -1) return + parentNode.props.pageSortedKey.splice(indexOfSortedKey, 1) + if ( + indexOfSortedKey !== 0 && + parentNode.props.pageSortedKey[parentNode.props.currentPageIndex] === + displayName + ) { + parentNode.props.currentPageIndex = 0 + } + + if ( + parentNode.props.homepageDisplayName === displayName || + !parentNode.props.homepageDisplayName + ) { + parentNode.props.homepageDisplayName = parentNode.props.pageSortedKey[0] + } + DisplayNameGenerator.removeDisplayName(displayName) +} + export const sortComponentNodeChildrenReducer: CaseReducer< ComponentsState, PayloadAction @@ -184,6 +246,46 @@ export const updateComponentDisplayNameReducer: CaseReducer< const node = searchDsl(state, displayName) if (!node) return node.displayName = newDisplayName + if (Array.isArray(node.childrenNode)) { + node.childrenNode.forEach((child) => { + child.parentNode = newDisplayName + }) + } + const parentNode = searchDsl(state, node.parentNode) + if (parentNode && parentNode.props) { + if (Array.isArray(parentNode.props.pageSortedKey)) { + const indexOfOldDisplayName = parentNode.props.pageSortedKey.findIndex( + (originDisplayName) => originDisplayName === displayName, + ) + if (indexOfOldDisplayName !== -1) { + parentNode.props.pageSortedKey.splice( + indexOfOldDisplayName, + 1, + newDisplayName, + ) + } + } + if (Array.isArray(parentNode.props.viewSortedKey)) { + const indexOfOldDisplayName = parentNode.props.viewSortedKey.findIndex( + (originDisplayName) => originDisplayName === displayName, + ) + if (indexOfOldDisplayName !== -1) { + parentNode.props.pageSortedKey.splice( + indexOfOldDisplayName, + 1, + newDisplayName, + ) + } + } + if (parentNode.displayName === "root" && parentNode.props) { + if (parentNode.props.homepageDisplayName === displayName) { + parentNode.props.homepageDisplayName = newDisplayName + } + if (!parentNode.props.homepageDisplayName) { + parentNode.props.homepageDisplayName = parentNode.props.pageSortedKey[0] + } + } + } } export const updateComponentsShape: CaseReducer< @@ -263,3 +365,211 @@ export const updateComponentReflowReducer: CaseReducer< } }) } + +export const updateHeaderSectionReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + const { payload } = action + const targetSection = searchDsl(state, "headerSection") + if (targetSection) { + targetSection.props = { + ...targetSection.props, + height: payload, + } + } +} + +export const updateCurrentPagePropsReducer: CaseReducer< + ComponentsState, + PayloadAction> +> = (state, action) => { + if (!state?.props) return state + const { currentPageIndex, pageSortedKey } = state.props + const currentPageDisplayName = pageSortedKey[currentPageIndex] + const currentPage = state.childrenNode.find( + (node) => node.displayName === currentPageDisplayName, + ) + if (!currentPage) return state + currentPage.props = { + ...currentPage.props, + ...action.payload, + } +} + +const transComponentNode = (node: ComponentNode, displayName: string[]) => { + if (Array.isArray(node.childrenNode)) { + node.childrenNode.forEach((child) => { + displayName.push(child.displayName) + transComponentNode(child, displayName) + }) + } +} + +export const updateTargetPageLayoutReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + if (!state) return state + const { pageName, layout } = action.payload + const config = layoutValueMapGenerateConfig[layout] + let targetPageNodeIndex = state.childrenNode.findIndex( + (node) => node.displayName === pageName, + ) + if (targetPageNodeIndex === -1) return state + const targetPageNode = state.childrenNode[targetPageNodeIndex] + const allComponentDisplayName: string[] = [] + transComponentNode(targetPageNode, allComponentDisplayName) + const pageConfig = config(targetPageNode.displayName) + DisplayNameGenerator.removeDisplayNameMulti(allComponentDisplayName) + state.childrenNode.splice(targetPageNodeIndex, 1, pageConfig) +} + +export const updateTargetPagePropsReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + if (!state?.props) return state + const { pageName, newProps } = action.payload + const currentPage = state.childrenNode.find( + (node) => node.displayName === pageName, + ) + if (!currentPage) return state + currentPage.props = { + ...currentPage.props, + ...newProps, + } +} + +export const deleteTargetPageSectionReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + if (!state?.childrenNode) return state + const { pageName, deleteSectionName, options } = action.payload + const targetPageIndex = state.childrenNode.findIndex( + (node) => node.displayName === pageName, + ) + if (targetPageIndex === -1) return state + const targetPage = cloneDeep(state.childrenNode[targetPageIndex]) + + targetPage.props = { + ...targetPage.props, + ...options, + } + + const targetPageChildrenNodeIndex = targetPage.childrenNode.findIndex( + (node) => node.showName === deleteSectionName, + ) + if (targetPageChildrenNodeIndex === -1) return state + targetPage.childrenNode.splice(targetPageChildrenNodeIndex, 1) + state.childrenNode.splice(targetPageIndex, 1, targetPage) +} + +export const addTargetPageSectionReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + if (!state?.childrenNode) return state + const { pageName, addedSectionName, options } = action.payload + const targetPageIndex = state.childrenNode.findIndex( + (node) => node.displayName === pageName, + ) + if (targetPageIndex === -1) return state + const targetPage = cloneDeep(state.childrenNode[targetPageIndex]) + + targetPage.props = { + ...targetPage.props, + ...options, + } + + const config = generateSectionConfig(pageName, addedSectionName) + if (!config) return state + targetPage.childrenNode.push(config) + state.childrenNode.splice(targetPageIndex, 1, targetPage) +} + +export const updateRootNodePropsReducer: CaseReducer< + ComponentsState, + PayloadAction> +> = (state, action) => { + if (!state) return state + if (!state.props) { + state.props = action.payload + } else { + state.props = { + ...state.props, + ...action.payload, + } + } +} + +export const addPageNodeWithSortOrderReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + action.payload.forEach((node) => { + const parentNode = searchDsl( + state, + node.parentNode, + ) as RootComponentNode | null + if (!parentNode) return + parentNode.props.pageSortedKey.push(node.displayName) + if (!Array.isArray(parentNode.childrenNode)) { + parentNode.childrenNode = [node] + } else { + parentNode.childrenNode.push(node) + } + }) +} + +export const addSectionViewReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + const { parentNodeName, containerNode, newSectionViewConfig } = action.payload + const parentNode = searchDsl(state, parentNodeName) + if (!parentNode || !parentNode.props) return + parentNode.childrenNode.push(containerNode) + parentNode.props.viewSortedKey.push(containerNode.displayName) + parentNode.props.sectionViewConfigs.push(newSectionViewConfig) +} + +export const updateSectionViewPropsReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + const { parentNodeName, newProps } = action.payload + const parentNode = searchDsl(state, parentNodeName) + if (!parentNode || !parentNode.props) return + parentNode.props = { + ...parentNode.props, + ...newProps, + } +} + +export const deleteSectionViewReducer: CaseReducer< + ComponentsState, + PayloadAction +> = (state, action) => { + const { viewDisplayName } = action.payload + const currentNode = searchDsl(state, viewDisplayName) + if (!currentNode) return + const parentNode = searchDsl(state, currentNode.parentNode) + if (!parentNode || !parentNode.props) return + const currentIndex = parentNode.childrenNode.findIndex( + (node) => node.displayName === viewDisplayName, + ) + if (currentIndex === -1) return + parentNode.childrenNode.splice(currentIndex, 1) + const viewSortedKeyIndex = parentNode.props.viewSortedKey.findIndex( + (key: string) => key === viewDisplayName, + ) + if (viewSortedKeyIndex === -1) return + parentNode.props.viewSortedKey.splice(viewSortedKeyIndex, 1) + const sectionViewConfigsIndex = parentNode.props.sectionViewConfigs.findIndex( + (config: SectionViewShape) => config.viewDisplayName === viewDisplayName, + ) + if (sectionViewConfigsIndex === -1) return + parentNode.props.sectionViewConfigs.splice(sectionViewConfigsIndex, 1) +} diff --git a/apps/builder/src/redux/currentApp/editor/components/componentsSelector.ts b/apps/builder/src/redux/currentApp/editor/components/componentsSelector.ts index 4888387372..dfc76c5eae 100644 --- a/apps/builder/src/redux/currentApp/editor/components/componentsSelector.ts +++ b/apps/builder/src/redux/currentApp/editor/components/componentsSelector.ts @@ -37,15 +37,16 @@ export function searchDsl( return null } -export function flattenDslToMap(rootNode: ComponentNode): { +export function flattenDslToMap( + rootNode: ComponentNode, +): { [key: string]: ComponentNode } { const queue = [rootNode] let res = {} while (queue.length > 0) { const head = queue[queue.length - 1] - - if (head.containerType !== "EDITOR_DOT_PANEL") { + if (head.type !== "CONTAINER_NODE") { res = { ...res, [head.displayName]: head || {} } } queue.pop() @@ -60,6 +61,28 @@ export function flattenDslToMap(rootNode: ComponentNode): { return res } +export function flattenAllComponentNodeToMap( + rootNode: ComponentNode, +): { + [key: string]: ComponentNode +} { + const queue = [rootNode] + let res = {} + while (queue.length > 0) { + const head = queue[queue.length - 1] + res = { ...res, [head.displayName]: head || {} } + queue.pop() + if (head.childrenNode) { + head.childrenNode.forEach((child) => { + if (child) { + queue.push(child) + } + }) + } + } + return res +} + export function flattenDslToArray(rootNode: ComponentNode): ComponentNode[] { const queue = [rootNode] let res: ComponentNode[] = [] @@ -144,3 +167,46 @@ export const getFlattenArrayComponentNodes = createSelector( return components || [] }, ) + +export const getCurrentPageNode = createSelector([getCanvas], (rootDSL) => { + if (rootDSL == null || !rootDSL.props) { + return null + } + const { currentPageIndex, pageSortedKey } = rootDSL.props + const currentPageDisplayName = pageSortedKey[currentPageIndex] + const currentPage = rootDSL.childrenNode.find( + (node) => node.displayName === currentPageDisplayName, + ) + if (!currentPage) return null + return currentPage +}) + +export const getCurrentPageProps = createSelector( + [getCurrentPageNode], + (currentPageNode) => { + if (currentPageNode == null || !currentPageNode.props) { + return {} + } + return currentPageNode.props + }, +) + +export const getCurrentPageDisplayName = createSelector( + [getCurrentPageNode], + (currentPageNode) => { + if (currentPageNode == null || !currentPageNode.props) { + return null + } + return currentPageNode.displayName + }, +) + +export const getRootNodeProps = createSelector([getCanvas], (rootNode) => { + if (!rootNode) + return { + currentPageIndex: 0, + pageSortedKey: ["page1"], + homepageDisplayName: "page1", + } + return rootNode.props +}) diff --git a/apps/builder/src/redux/currentApp/editor/components/componentsSlice.ts b/apps/builder/src/redux/currentApp/editor/components/componentsSlice.ts index 998efe95f6..4ef85be27b 100644 --- a/apps/builder/src/redux/currentApp/editor/components/componentsSlice.ts +++ b/apps/builder/src/redux/currentApp/editor/components/componentsSlice.ts @@ -12,7 +12,18 @@ import { updateComponentReducer, updateComponentReflowReducer, updateComponentsShape, + updateCurrentPagePropsReducer, updateMultiComponentPropsReducer, + updateTargetPageLayoutReducer, + updateTargetPagePropsReducer, + deleteTargetPageSectionReducer, + addTargetPageSectionReducer, + updateRootNodePropsReducer, + addPageNodeWithSortOrderReducer, + deletePageNodeReducer, + addSectionViewReducer, + deleteSectionViewReducer, + updateSectionViewPropsReducer, } from "@/redux/currentApp/editor/components/componentsReducer" const componentsSlice = createSlice({ @@ -31,6 +42,17 @@ const componentsSlice = createSlice({ updateComponentReflowReducer, updateComponentContainerReducer, updateMultiComponentPropsReducer, + updateCurrentPagePropsReducer, + updateTargetPageLayoutReducer, + updateTargetPagePropsReducer, + deleteTargetPageSectionReducer, + addTargetPageSectionReducer, + updateRootNodePropsReducer, + addPageNodeWithSortOrderReducer, + deletePageNodeReducer, + addSectionViewReducer, + deleteSectionViewReducer, + updateSectionViewPropsReducer, }, }) diff --git a/apps/builder/src/redux/currentApp/editor/components/componentsState.ts b/apps/builder/src/redux/currentApp/editor/components/componentsState.ts index 173754ac48..3332c71c6b 100644 --- a/apps/builder/src/redux/currentApp/editor/components/componentsState.ts +++ b/apps/builder/src/redux/currentApp/editor/components/componentsState.ts @@ -1,6 +1,16 @@ export enum CONTAINER_TYPE { "EDITOR_DOT_PANEL" = "EDITOR_DOT_PANEL", "EDITOR_SCALE_SQUARE" = "EDITOR_SCALE_SQUARE", + "EDITOR_PAGE_SQUARE" = "EDITOR_PAGE_SQUARE", + "EDITOR_LAYOUT_SQUARE" = "EDITOR_LAYOUT_SQUARE", +} + +export enum SECTION_POSITION { + "TOP" = "TOP", + "BOTTOM" = "BOTTOM", + "CENTER" = "CENTER", + "FULL" = "FULL", + "NONE" = "NONE", } export interface ComponentNode { displayName: string @@ -29,9 +39,74 @@ export interface ComponentNode { props: { [key: string]: any } | null - panelConfig?: { - dynamicStrings: string[] - } +} + +export interface RootComponentNodeProps { + currentPageIndex: number + pageSortedKey: string[] + homepageDisplayName?: string +} + +export interface RootComponentNode extends ComponentNode { + type: "DOT_PANEL" + props: RootComponentNodeProps +} + +export interface PageNodeProps { + canvasSize: "responsive" | "fixed" + canvasWidth: string + layout: string + leftPosition: SECTION_POSITION + rightPosition: SECTION_POSITION + hasLeft: boolean + hasRight: boolean + hasHeader: boolean + hasFooter: boolean + isLeftFixed: boolean + isRightFixed: boolean + isHeaderFixed: boolean + isFooterFixed: boolean + leftWidth: number + rightWidth: number + topHeight: number + bottomHeight: number + showLeftFoldIcon: boolean + showRightFoldIcon: boolean +} +export interface PageNode extends ComponentNode { + type: "PAGE_NODE" + props: PageNodeProps +} + +export interface SectionViewShape { + viewDisplayName: string + key: string + id: string + path: string +} + +export interface LeftOrRightSectionNodeProps { + showFoldIcon: boolean + currentViewIndex: number + viewSortedKey: string[] + sectionViewConfigs: SectionViewShape[] + defaultViewKey: string +} + +export interface HeaderOrBottomSectionNodeProps { + currentViewIndex: number + viewSortedKey: string[] + sectionViewConfigs: SectionViewShape[] + defaultViewKey: string +} + +export type SectionNodeProps = + | LeftOrRightSectionNodeProps + | HeaderOrBottomSectionNodeProps + +export interface SectionNode extends ComponentNode { + type: "SECTION_NODE" + props: SectionNodeProps } export type ComponentsState = ComponentNode | null @@ -41,6 +116,11 @@ export interface DeleteComponentNodePayload { displayNames: string[] } +export interface DeletePageNodePayload { + displayName: string + originPageSortedKey: string[] +} + export interface sortComponentNodeChildrenPayload { parentDisplayName: string newChildrenNode: ComponentNode[] @@ -64,3 +144,52 @@ export interface CopyComponentPayload { oldComponentNode: ComponentNode newComponentNode: ComponentNode } + +export interface UpdateTargetPageLayoutPayload { + pageName: string + layout: "default" | "presetA" | "presetB" | "presetC" | "presetD" | "presetE" +} + +export interface UpdateTargetPagePropsPayload { + pageName: string + newProps: Partial +} + +export interface UpdateRootNodePropsPayload {} + +export interface DeleteTargetPageSectionPayload { + pageName: string + deleteSectionName: + | "leftSection" + | "rightSection" + | "headerSection" + | "footerSection" + options: Record +} + +export interface AddTargetPageSectionPayload { + pageName: string + addedSectionName: + | "leftSection" + | "rightSection" + | "headerSection" + | "footerSection" + options: Record +} + +export interface AddSectionViewPayload { + parentNodeName: string + containerNode: ComponentNode + newSectionViewConfig: SectionViewShape +} + +export interface DeleteSectionViewPayload { + viewDisplayName: string + parentNodeName: string + originPageSortedKey: string[] +} + +export interface UpdateSectionViewPropsPayload { + parentNodeName: string + newProps: Record +} diff --git a/apps/builder/src/redux/currentApp/executionTree/executionListener.ts b/apps/builder/src/redux/currentApp/executionTree/executionListener.ts index ed00a083b3..b392f11c92 100644 --- a/apps/builder/src/redux/currentApp/executionTree/executionListener.ts +++ b/apps/builder/src/redux/currentApp/executionTree/executionListener.ts @@ -95,8 +95,9 @@ async function handleStartExecutionOnCanvas( const rootState = listenerApi.getState() const oldExecutionTree = getExecutionResult(rootState) if (executionTree) { - const executionResult = - executionTree.updateTreeFromExecution(oldExecutionTree) + const executionResult = executionTree.updateTreeFromExecution( + oldExecutionTree, + ) const evaluatedTree = executionResult.evaluatedTree const updates = diff(oldExecutionTree, evaluatedTree) || [] listenerApi.dispatch( @@ -120,6 +121,16 @@ export function setupExecutionListeners( componentsActions.updateComponentDisplayNameReducer, componentsActions.resetComponentPropsReducer, componentsActions.updateMultiComponentPropsReducer, + componentsActions.addTargetPageSectionReducer, + componentsActions.updateTargetPagePropsReducer, + componentsActions.deleteTargetPageSectionReducer, + componentsActions.addPageNodeWithSortOrderReducer, + componentsActions.updateRootNodePropsReducer, + componentsActions.updateTargetPageLayoutReducer, + componentsActions.deletePageNodeReducer, + componentsActions.addSectionViewReducer, + componentsActions.deleteSectionViewReducer, + componentsActions.updateSectionViewPropsReducer, actionActions.addActionItemReducer, actionActions.removeActionItemReducer, actionActions.updateActionItemReducer, diff --git a/apps/builder/src/redux/currentApp/executionTree/executionSelector.ts b/apps/builder/src/redux/currentApp/executionTree/executionSelector.ts index 81120d5958..570966ecfd 100644 --- a/apps/builder/src/redux/currentApp/executionTree/executionSelector.ts +++ b/apps/builder/src/redux/currentApp/executionTree/executionSelector.ts @@ -39,6 +39,12 @@ export const getExecutionDebuggerData = createSelector( [getExecution], (execution) => execution.debuggerData ?? {}, ) +const IGNORE_WIDGET_TYPES = new Set([ + "PAGE_NODE", + "SECTION_NODE", + "CANVAS", + "DOT_PANEL", +]) export const getWidgetExecutionResult = createSelector( [getExecutionResult], @@ -59,15 +65,63 @@ export const getWidgetExecutionResultArray = createSelector( (widgetExecutionResult) => { const widgetExecutionResultArray: Record[] = [] Object.keys(widgetExecutionResult).forEach((key) => { - widgetExecutionResultArray.push({ - ...widgetExecutionResult[key], - displayName: key, - }) + if (!IGNORE_WIDGET_TYPES.has(widgetExecutionResult[key].$widgetType)) { + widgetExecutionResultArray.push({ + ...widgetExecutionResult[key], + displayName: key, + }) + } + }) + return widgetExecutionResultArray + }, +) + +export const getPageExecutionResultArray = createSelector( + [getWidgetExecutionResult], + (widgetExecutionResult) => { + const widgetExecutionResultArray: Record[] = [] + Object.keys(widgetExecutionResult).forEach((key) => { + if (widgetExecutionResult[key].$widgetType === "PAGE_NODE") { + widgetExecutionResultArray.push({ + ...widgetExecutionResult[key], + displayName: key, + }) + } }) return widgetExecutionResultArray }, ) +export const getSectionExecutionResultArray = createSelector( + [getWidgetExecutionResult], + (widgetExecutionResult) => { + const sectionExecutionResult: Record = {} + Object.keys(widgetExecutionResult).forEach((key) => { + if (widgetExecutionResult[key].$widgetType === "SECTION_NODE") { + sectionExecutionResult[key] = { + ...widgetExecutionResult[key], + displayName: key, + } + } + }) + return sectionExecutionResult + }, +) + +export const getRootNodeExecutionResult = createSelector( + [getWidgetExecutionResult], + (widgetExecutionResult) => { + const rootNode = widgetExecutionResult["root"] + return rootNode + ? rootNode + : { + currentPageIndex: 0, + pageSortedKey: ["page1"], + homepageDisplayName: "page1", + } + }, +) + export const getActionExecutionResult = createSelector( [getExecutionResult], (executionResult) => { diff --git a/apps/builder/src/router/index.tsx b/apps/builder/src/router/index.tsx index dbeae020b7..fe5d3ef60a 100644 --- a/apps/builder/src/router/index.tsx +++ b/apps/builder/src/router/index.tsx @@ -1,4 +1,4 @@ -import { useRoutes } from "react-router-dom" +import { createBrowserRouter } from "react-router-dom" import { routerConfig } from "@/router/routerConfig" import { RoutesObjectPro } from "@/router/interface" import { CheckIsLogin } from "@/auth" @@ -22,6 +22,4 @@ const wrappedRouter = (routesConfig: RoutesObjectPro[]) => { }) } -export const ILLARoute = () => { - return useRoutes(wrappedRouter(routerConfig)) -} +export const ILLARoute = createBrowserRouter(wrappedRouter(routerConfig)) diff --git a/apps/builder/src/router/interface.ts b/apps/builder/src/router/interface.ts index a92052f456..0e096f66c5 100644 --- a/apps/builder/src/router/interface.ts +++ b/apps/builder/src/router/interface.ts @@ -1,6 +1,6 @@ import { RouteObject } from "react-router-dom" -export interface RoutesObjectPro extends RouteObject { +export type RoutesObjectPro = RouteObject & { /** * @description need login, if use check role,can replace this */ diff --git a/apps/builder/src/router/routerConfig.tsx b/apps/builder/src/router/routerConfig.tsx index 494a2e2d7d..fcdbf28ff0 100644 --- a/apps/builder/src/router/routerConfig.tsx +++ b/apps/builder/src/router/routerConfig.tsx @@ -104,6 +104,16 @@ export const routerConfig: RoutesObjectPro[] = [ element: , needLogin: true, }, + { + path: "/deploy/app/:appId/version/:versionId/:pageName", + element: , + needLogin: true, + }, + { + path: "/deploy/app/:appId/version/:versionId/:pageName/:viewPath", + element: , + needLogin: true, + }, { path: "/403", element: , diff --git a/apps/builder/src/utils/eventHandlerHelper/index.ts b/apps/builder/src/utils/eventHandlerHelper/index.ts index a0f1b9f051..9538df0988 100644 --- a/apps/builder/src/utils/eventHandlerHelper/index.ts +++ b/apps/builder/src/utils/eventHandlerHelper/index.ts @@ -1,3 +1,10 @@ +import { SectionViewShape } from "@/redux/currentApp/editor/components/componentsState" +import { + searchDsl, + getCanvas, +} from "@/redux/currentApp/editor/components/componentsSelector" +import { getIllaMode } from "@/redux/config/configSelector" +import { executionActions } from "@/redux/currentApp/executionTree/executionSlice" import store from "@/store" import { getActionItemByDisplayName } from "@/redux/currentApp/action/actionSelector" import { runAction } from "@/page/App/components/Actions/ActionPanel/utils/runAction" @@ -9,6 +16,9 @@ import { showNotification, } from "@/page/App/context/globalDataProvider" import { get } from "lodash" +import { getRootNodeExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" +import { ILLARoute } from "@/router" +import { UpdateExecutionByDisplayNamePayload } from "@/redux/currentApp/executionTree/executionState" export const transformEvents = ( event: any, @@ -41,6 +51,74 @@ export const transformEvents = ( enabled, } } + if (actionType === "setRouter") { + console.log("event", event) + const { pagePath, viewPath } = event + let finalPath = `/${pagePath}` + finalPath = viewPath ? finalPath + `/${viewPath}` : finalPath + return { + script: () => { + const originPath = window.location.pathname + const originPathArray = originPath.split("/") + const mode = getIllaMode(store.getState()) + const rootNodeProps = getRootNodeExecutionResult(store.getState()) + const { pageSortedKey } = rootNodeProps + const index = pageSortedKey.findIndex( + (path: string) => path === pagePath, + ) + if (index === -1) return + if (mode === "production" && originPathArray.length >= 6) { + if (mode === "production") { + ILLARoute.navigate( + originPathArray.slice(0, 6).join("/") + finalPath, + ) + } + } + const updateSlice: UpdateExecutionByDisplayNamePayload[] = [ + { + displayName: "root", + value: { + currentPageIndex: index, + }, + }, + ] + if (viewPath) { + const canvas = getCanvas(store.getState()) + if (!canvas) return + const pageNode = searchDsl(canvas, pagePath) + if (!pageNode) return + pageNode.childrenNode.forEach((node) => { + const sectionViewConfigs = node.props?.sectionViewConfigs || [] + const viewSortedKey = node.props?.viewSortedKey || [] + const findConfig = sectionViewConfigs.find( + (config: SectionViewShape) => { + return config.path === viewPath + }, + ) + if (findConfig) { + const viewDisplayName = findConfig.viewDisplayName + const indexOfViewKey = viewSortedKey.findIndex( + (key: string) => key === viewDisplayName, + ) + if (indexOfViewKey !== -1) { + updateSlice.push({ + displayName: node.displayName, + value: { + currentViewIndex: indexOfViewKey, + }, + }) + } + } + }) + } + store.dispatch( + executionActions.updateExecutionByMultiDisplayNameReducer( + updateSlice, + ), + ) + }, + } + } if (actionType === "widget") { const { widgetID, widgetMethod, enabled } = event if ( diff --git a/apps/builder/src/utils/executionTreeHelper/executionTreeFactory.ts b/apps/builder/src/utils/executionTreeHelper/executionTreeFactory.ts index ad05b3e710..b1b1c6ef42 100644 --- a/apps/builder/src/utils/executionTreeHelper/executionTreeFactory.ts +++ b/apps/builder/src/utils/executionTreeHelper/executionTreeFactory.ts @@ -1,3 +1,4 @@ +import { isObject } from "@/utils/typeHelper" import { RawTreeShape } from "@/utils/executionTreeHelper/interface" import { cloneDeep, flatten, get, set, unset } from "lodash" import { @@ -68,27 +69,29 @@ export class ExecutionTreeFactory { return current } const validationPaths = widgetOrAction.$validationPaths - Object.keys(validationPaths).forEach((validationPath) => { - const validationType = validationPaths[validationPath] - const fullPath = `${displayName}.${validationPath}` - const validationFunc = validationFactory[validationType] - const value = get(widgetOrAction, validationPath) - const { isValid, safeValue, errorMessage } = validationFunc(value) - set(current, fullPath, safeValue) - if (!isValid) { - let error = get(this.errorTree, fullPath) - if (!Array.isArray(error)) { - error = [] + if (isObject(validationPaths)) { + Object.keys(validationPaths).forEach((validationPath) => { + const validationType = validationPaths[validationPath] + const fullPath = `${displayName}.${validationPath}` + const validationFunc = validationFactory[validationType] + const value = get(widgetOrAction, validationPath) + const { isValid, safeValue, errorMessage } = validationFunc(value) + set(current, fullPath, safeValue) + if (!isValid) { + let error = get(this.errorTree, fullPath) + if (!Array.isArray(error)) { + error = [] + } + error.push({ + errorType: ExecutionErrorType.VALIDATION, + errorMessage: errorMessage as string, + errorName: "Validation", + }) + set(this.errorTree, fullPath, error) + this.debuggerData[fullPath] = error } - error.push({ - errorType: ExecutionErrorType.VALIDATION, - errorMessage: errorMessage as string, - errorName: "Validation", - }) - set(this.errorTree, fullPath, error) - this.debuggerData[fullPath] = error - } - }) + }) + } return current }, tree) diff --git a/apps/builder/src/utils/generators/generateDisplayName.ts b/apps/builder/src/utils/generators/generateDisplayName.ts index 457802cd86..12e975cb3f 100644 --- a/apps/builder/src/utils/generators/generateDisplayName.ts +++ b/apps/builder/src/utils/generators/generateDisplayName.ts @@ -11,8 +11,10 @@ export const ADD_DISPLAY_NAME = "addDisplayName" export const REMOVE_DISPLAY_NAME = "removeDisplayName" export const UPDATE_DISPLAY_NAME = "updateDisplayName" +export const PLACEHOLDER_DISPLAYNAME = [] + export class DisplayNameGenerator { - static displayNameList = new Set() + static displayNameList = new Set(PLACEHOLDER_DISPLAYNAME) static appId: string = "" diff --git a/apps/builder/src/utils/generators/generatePageOrSectionConfig.ts b/apps/builder/src/utils/generators/generatePageOrSectionConfig.ts new file mode 100644 index 0000000000..bcca697c0b --- /dev/null +++ b/apps/builder/src/utils/generators/generatePageOrSectionConfig.ts @@ -0,0 +1,504 @@ +import { + ComponentNode, + CONTAINER_TYPE, + SECTION_POSITION, +} from "@/redux/currentApp/editor/components/componentsState" +import { v4 } from "uuid" +import { DisplayNameGenerator } from "./generateDisplayName" + +export type SectionNodeType = + | "bodySection" + | "leftSection" + | "rightSection" + | "headerSection" + | "footerSection" + +export const generateSectionContainerConfig = ( + parentNode: string, + showName: string, + childrenNode: ComponentNode[] = [], +) => { + const displayName = DisplayNameGenerator.generateDisplayName( + "CONTAINER_NODE", + `${parentNode}-${showName}`, + ) + return { + displayName: displayName, + parentNode: parentNode, + showName: showName, + isDragging: false, + isResizing: false, + type: "CONTAINER_NODE", + containerType: CONTAINER_TYPE.EDITOR_DOT_PANEL, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + childrenNode: childrenNode, + props: {}, + } +} + +export const generateSectionConfig = ( + parentNode: string, + showName: SectionNodeType, +) => { + const displayName = DisplayNameGenerator.generateDisplayName( + "SECTION_NODE", + showName, + ) + const childrenNode = generateSectionContainerConfig( + displayName, + `${showName}Container`, + ) + return { + displayName: `${displayName}`, + parentNode: parentNode, + showName: showName, + isDragging: false, + isResizing: false, + type: "SECTION_NODE", + containerType: CONTAINER_TYPE.EDITOR_LAYOUT_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + currentViewIndex: 0, + viewSortedKey: [childrenNode.displayName], + defaultViewKey: "View 1", + sectionViewConfigs: [ + { + id: v4(), + viewDisplayName: childrenNode.displayName, + key: "View 1", + path: "View1", + }, + ], + }, + childrenNode: [childrenNode], + } +} + +export const defaultPageProps = { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "default", + leftPosition: SECTION_POSITION.NONE, + rightPosition: SECTION_POSITION.NONE, + hasFooter: false, + hasHeader: false, + hasLeft: false, + hasRight: false, + leftWidth: 0, + rightWidth: 0, + topHeight: 0, + bottomHeight: 0, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, +} + +export const generatePageConfig = () => { + const displayName = DisplayNameGenerator.generateDisplayName( + "PAGE_NODE", + "page", + ) + const childrenNode = generateSectionConfig(displayName, "bodySection") + return { + displayName: displayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: defaultPageProps, + childrenNode: [childrenNode], + } +} + +export const generateDefaultLayoutConfig = (currentDisplayName: string) => { + const childrenNode = generateSectionConfig(currentDisplayName, "bodySection") + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "default", + leftPosition: SECTION_POSITION.NONE, + rightPosition: SECTION_POSITION.NONE, + hasFooter: false, + hasHeader: false, + hasLeft: false, + hasRight: false, + leftWidth: 0, + rightWidth: 0, + topHeight: 0, + bottomHeight: 0, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [childrenNode], + } +} + +export const generatePresetALayoutConfig = (currentDisplayName: string) => { + const leftSectionNode = generateSectionConfig( + currentDisplayName, + "leftSection", + ) + const bodySectionNode = generateSectionConfig( + currentDisplayName, + "bodySection", + ) + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "presetA", + leftPosition: SECTION_POSITION.FULL, + rightPosition: SECTION_POSITION.NONE, + hasFooter: false, + hasHeader: false, + hasLeft: true, + hasRight: false, + leftWidth: 20, + rightWidth: 0, + topHeight: 0, + bottomHeight: 0, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [leftSectionNode, bodySectionNode], + } +} + +export const generatePresetBLayoutConfig = (currentDisplayName: string) => { + const headerSectionNode = generateSectionConfig( + currentDisplayName, + "headerSection", + ) + const bodySectionNode = generateSectionConfig( + currentDisplayName, + "bodySection", + ) + const footerSectionNode = generateSectionConfig( + currentDisplayName, + "footerSection", + ) + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "presetB", + leftPosition: "NONE", + rightPosition: "NONE", + hasFooter: true, + hasHeader: true, + hasLeft: false, + hasRight: false, + leftWidth: 0, + rightWidth: 0, + topHeight: 96, + bottomHeight: 96, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + }, + childrenNode: [headerSectionNode, bodySectionNode, footerSectionNode], + } +} + +export const generatePresetCLayoutConfig = (currentDisplayName: string) => { + const headerSectionNode = generateSectionConfig( + currentDisplayName, + "headerSection", + ) + const leftSectionNode = generateSectionConfig( + currentDisplayName, + "leftSection", + ) + const bodySectionNode = generateSectionConfig( + currentDisplayName, + "bodySection", + ) + const footerSectionNode = generateSectionConfig( + currentDisplayName, + "footerSection", + ) + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "presetC", + leftPosition: SECTION_POSITION.BOTTOM, + rightPosition: SECTION_POSITION.NONE, + hasFooter: true, + hasHeader: true, + hasLeft: true, + hasRight: false, + leftWidth: 20, + rightWidth: 0, + topHeight: 96, + bottomHeight: 96, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [ + headerSectionNode, + leftSectionNode, + bodySectionNode, + footerSectionNode, + ], + } +} + +export const generatePresetDLayoutConfig = (currentDisplayName: string) => { + const headerSectionNode = generateSectionConfig( + currentDisplayName, + "headerSection", + ) + const leftSectionNode = generateSectionConfig( + currentDisplayName, + "leftSection", + ) + const rightSectionNode = generateSectionConfig( + currentDisplayName, + "rightSection", + ) + const bodySectionNode = generateSectionConfig( + currentDisplayName, + "bodySection", + ) + const footerSectionNode = generateSectionConfig( + currentDisplayName, + "footerSection", + ) + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "presetD", + leftPosition: SECTION_POSITION.BOTTOM, + rightPosition: SECTION_POSITION.BOTTOM, + hasFooter: true, + hasHeader: true, + hasLeft: true, + hasRight: true, + leftWidth: 20, + rightWidth: 20, + topHeight: 96, + bottomHeight: 96, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [ + headerSectionNode, + rightSectionNode, + leftSectionNode, + bodySectionNode, + footerSectionNode, + ], + } +} + +export const generatePresetELayoutConfig = (currentDisplayName: string) => { + const headerSectionNode = generateSectionConfig( + currentDisplayName, + "headerSection", + ) + const leftSectionNode = generateSectionConfig( + currentDisplayName, + "leftSection", + ) + const bodySectionNode = generateSectionConfig( + currentDisplayName, + "bodySection", + ) + const footerSectionNode = generateSectionConfig( + currentDisplayName, + "footerSection", + ) + return { + displayName: currentDisplayName, + parentNode: "root", + showName: "page", + isDragging: false, + isResizing: false, + type: "PAGE_NODE", + containerType: CONTAINER_TYPE.EDITOR_PAGE_SQUARE, + verticalResize: true, + h: 0, + w: 0, + minH: 0, + minW: 0, + unitW: 0, + unitH: 0, + x: -1, + y: -1, + z: 0, + props: { + canvasSize: "responsive", + canvasWidth: "auto", + layout: "presetE", + leftPosition: SECTION_POSITION.FULL, + rightPosition: SECTION_POSITION.NONE, + hasFooter: true, + hasHeader: true, + hasLeft: true, + hasRight: false, + leftWidth: 20, + rightWidth: 20, + topHeight: 96, + bottomHeight: 96, + isLeftFixed: true, + isRightFixed: true, + isHeaderFixed: true, + isFooterFixed: true, + showLeftFoldIcon: false, + showRightFoldIcon: false, + }, + childrenNode: [ + headerSectionNode, + leftSectionNode, + bodySectionNode, + footerSectionNode, + ], + } +} + +export const layoutValueMapGenerateConfig = { + default: generateDefaultLayoutConfig, + presetA: generatePresetALayoutConfig, + presetB: generatePresetBLayoutConfig, + presetC: generatePresetCLayoutConfig, + presetD: generatePresetDLayoutConfig, + presetE: generatePresetELayoutConfig, +} diff --git a/apps/builder/src/utils/shortcut/index.tsx b/apps/builder/src/utils/shortcut/index.tsx index cfb7d6fe9f..81aa0b91be 100644 --- a/apps/builder/src/utils/shortcut/index.tsx +++ b/apps/builder/src/utils/shortcut/index.tsx @@ -19,10 +19,13 @@ import { CopyManager } from "@/utils/copyManager" import { FocusManager } from "@/utils/focusManager" import { RootState } from "@/store" import { + flattenAllComponentNodeToMap, getCanvas, + searchDsl, searchDSLByDisplayName, } from "@/redux/currentApp/editor/components/componentsSelector" import { ComponentNode } from "@/redux/currentApp/editor/components/componentsState" +import { getExecutionResult } from "@/redux/currentApp/executionTree/executionSelector" export const Shortcut: FC = ({ children }) => { const dispatch = useDispatch() @@ -43,7 +46,7 @@ export const Shortcut: FC = ({ children }) => { const currentSelectedAction = useSelector(getSelectedAction) const canvasRootNode = useSelector(getCanvas) - + const executionResult = useSelector(getExecutionResult) const freezeState = useSelector(getFreezeState) const showShadows = useSelector(isShowDot) @@ -65,7 +68,11 @@ export const Shortcut: FC = ({ children }) => { boolean >(false) - const showDeleteDialog = (displayName: string[]) => { + const showDeleteDialog = ( + displayName: string[], + type?: "widget" | "page", + options?: Record, + ) => { if (!alreadyShowDeleteDialog && displayName.length > 0) { const textList = displayName.join(", ").toString() setAlreadyShowDeleteDialog(true) @@ -88,6 +95,15 @@ export const Shortcut: FC = ({ children }) => { }, onOk: () => { setAlreadyShowDeleteDialog(false) + if (type === "page") { + dispatch( + componentsActions.deletePageNodeReducer({ + displayName: displayName[0], + originPageSortedKey: options?.originPageSortedKey || [], + }), + ) + return + } dispatch( componentsActions.deleteComponentNodeReducer({ displayNames: displayName, @@ -137,10 +153,44 @@ export const Shortcut: FC = ({ children }) => { break case "canvas": { if (canvasRootNode) { - const childNode = canvasRootNode.childrenNode - const childNodeDisplayNames = childNode.map((node) => { - return node.displayName + const childNodeDisplayNames: string[] = [] + const rootNode = executionResult.root + if (!rootNode) return + const currentPageDisplayName = + rootNode.pageSortedKey[rootNode.currentPageIndex] + const pageNode = searchDsl(canvasRootNode, currentPageDisplayName) + if (!pageNode) return + const sectionContainerNodeDisplayName: string[] = [] + pageNode.childrenNode.forEach((sectionNode) => { + const displayName = sectionNode.displayName + const currentSectionProps = executionResult[displayName] + if ( + currentSectionProps && + currentSectionProps.viewSortedKey && + currentSectionProps.currentViewIndex >= 0 + ) { + const { currentViewIndex, viewSortedKey } = currentSectionProps + const currentDisplayName = viewSortedKey[currentViewIndex] + sectionContainerNodeDisplayName.push(currentDisplayName) + } + }) + const componentNodesMap = flattenAllComponentNodeToMap(pageNode) + const allChildrenNodes: ComponentNode[] = [] + sectionContainerNodeDisplayName.forEach((displayName) => { + if (componentNodesMap[displayName]) { + const childrenNode = Array.isArray( + componentNodesMap[displayName].childrenNode, + ) + ? componentNodesMap[displayName].childrenNode + : [] + allChildrenNodes.push(...childrenNode) + } }) + + allChildrenNodes.forEach((node) => { + childNodeDisplayNames.push(node.displayName) + }) + dispatch( configActions.updateSelectedComponent(childNodeDisplayNames), ) @@ -148,7 +198,7 @@ export const Shortcut: FC = ({ children }) => { } } }, - [canvasRootNode], + [canvasRootNode, executionResult], ) useHotkeys( diff --git a/apps/builder/src/utils/shortcut/interface.ts b/apps/builder/src/utils/shortcut/interface.ts index 52c38200de..6d87d15e9d 100644 --- a/apps/builder/src/utils/shortcut/interface.ts +++ b/apps/builder/src/utils/shortcut/interface.ts @@ -1,3 +1,7 @@ export interface ShortcutContextProp { - showDeleteDialog: (displayName: string[]) => void + showDeleteDialog: ( + displayName: string[], + type?: "page" | "widget", + options?: Record, + ) => void } diff --git a/apps/builder/src/widgetLibrary/NumberInputWidget/numberInput.tsx b/apps/builder/src/widgetLibrary/NumberInputWidget/numberInput.tsx index 77cac9b08a..5516ddd1cb 100644 --- a/apps/builder/src/widgetLibrary/NumberInputWidget/numberInput.tsx +++ b/apps/builder/src/widgetLibrary/NumberInputWidget/numberInput.tsx @@ -57,7 +57,7 @@ export const WrappedInputNumber = forwardRef< { displayName, value: { - value: value || "", + value: value === undefined ? "" : value, validateMessage: message, }, }, diff --git a/apps/builder/src/widgetLibrary/PublicSector/TransformWidgetWrapper/style.ts b/apps/builder/src/widgetLibrary/PublicSector/TransformWidgetWrapper/style.ts index e3634bd5da..f3b63fb7d7 100644 --- a/apps/builder/src/widgetLibrary/PublicSector/TransformWidgetWrapper/style.ts +++ b/apps/builder/src/widgetLibrary/PublicSector/TransformWidgetWrapper/style.ts @@ -85,5 +85,6 @@ export const applyWrapperStylesStyle = ( ? backgroundColor || "white" : "transparent"}; box-shadow: ${shadowStyle}; + overflow-x: hidden; ` } diff --git a/apps/builder/src/widgetLibrary/PublicSector/utils/generatorEventHandlerConfig.ts b/apps/builder/src/widgetLibrary/PublicSector/utils/generatorEventHandlerConfig.ts index ea816a938c..f956f13773 100644 --- a/apps/builder/src/widgetLibrary/PublicSector/utils/generatorEventHandlerConfig.ts +++ b/apps/builder/src/widgetLibrary/PublicSector/utils/generatorEventHandlerConfig.ts @@ -47,6 +47,10 @@ export const generatorEventHandlerConfig = ( label: i18n.t("editor.inspect.setter_label.show_notification"), value: "showNotification", }, + { + label: i18n.t("editor.inspect.setter_label.set_router"), + value: "setRouter", + }, ], }, { @@ -59,7 +63,7 @@ export const generatorEventHandlerConfig = ( }, { id: `${baseWidgetName}-interaction-event-handler-actionMethod`, - labelName: i18n.t("Action Method"), + labelName: i18n.t("editor.inspect.setter_label.method"), setterType: "BASE_SELECT_SETTER", attrName: "widgetMethod", bindAttrName: ["queryID"], @@ -259,6 +263,30 @@ export const generatorEventHandlerConfig = ( }, ], }, + { + id: `${baseWidgetName}-interaction-event-handler-page`, + labelName: i18n.t("editor.inspect.setter_label.page"), + setterType: "EVENT_TARGET_PAGE_SELECT_SETTER", + placeholder: i18n.t( + "editor.inspect.setter_content.select_page_setter.placeholder", + ), + expectedType: VALIDATION_TYPES.STRING, + attrName: "pagePath", + bindAttrName: ["actionType"], + shown: (type) => type === "setRouter", + }, + { + id: `${baseWidgetName}-interaction-event-handler-view-path`, + labelName: i18n.t("editor.page.label_name.view_path"), + placeholder: i18n.t( + "editor.inspect.setter_content.select_view_setter.placeholder", + ), + setterType: "EVENT_TARGET_VIEW_PATH_SELECT_SETTER", + expectedType: VALIDATION_TYPES.STRING, + attrName: "viewPath", + bindAttrName: ["actionType"], + shown: (type) => type === "setRouter", + }, { id: `${baseWidgetName}-interaction-event-handler-duration`, labelName: `${i18n.t("editor.inspect.setter_label.duration")}(ms)`, diff --git a/illa-design b/illa-design index cb836e014e..e3391d1fc1 160000 --- a/illa-design +++ b/illa-design @@ -1 +1 @@ -Subproject commit cb836e014e412dc14e6500b7bb73242a45de7b2a +Subproject commit e3391d1fc17ba782bb8a9660c6038039a0b931b9 diff --git a/yarn.lock b/yarn.lock index f4949c621b..1d32a6c166 100644 --- a/yarn.lock +++ b/yarn.lock @@ -963,7 +963,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.16.0", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.16.0", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.18.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== @@ -2041,6 +2041,11 @@ redux-thunk "^2.4.1" reselect "^4.1.5" +"@remix-run/router@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.3.tgz#953b88c20ea00d0eddaffdc1b115c08474aa295d" + integrity sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q== + "@rollup/plugin-typescript@^8.3.0": version "8.3.2" resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz#e1b719e2ed3e752bbc092001656c48378f2d15f0" @@ -6454,13 +6459,6 @@ hey-listen@^1.0.8: resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== -history@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" - integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== - dependencies: - "@babel/runtime" "^7.7.6" - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -9767,20 +9765,20 @@ react-rnd@^10.3.7: react-draggable "4.4.4" tslib "2.3.1" -react-router-dom@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" - integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== +react-router-dom@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.3.tgz#70093b5f65f85f1df9e5d4182eb7ff3a08299275" + integrity sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ== dependencies: - history "^5.2.0" - react-router "6.3.0" + "@remix-run/router" "1.0.3" + react-router "6.4.3" -react-router@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" - integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== +react-router@6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.3.tgz#9ed3ee4d6e95889e9b075a5d63e29acc7def0d49" + integrity sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA== dependencies: - history "^5.2.0" + "@remix-run/router" "1.0.3" react-style-singleton@^2.2.1: version "2.2.1"