diff --git a/package-lock.json b/package-lock.json index 7251782..505b395 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@marsidev/react-turnstile": "^1.0.2", "@monaco-editor/react": "^4.6.0", "@shikijs/monaco": "^1.22.2", + "@xyflow/react": "^12.3.2", "antd": "^5.21.6", "antd-style": "^3.7.1", "axios": "^1.7.7", @@ -2216,6 +2217,49 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2286,14 +2330,14 @@ "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2589,6 +2633,34 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@xyflow/react": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.3.2.tgz", + "integrity": "sha512-+bK3L61BDIvUX++jMiEqIjy5hIIyVmfeiUavpeOZIYKwg6NW0pR5EnHJM2JFfkVqZisFauzS9EgmI+tvTqx9Qw==", + "dependencies": { + "@xyflow/system": "0.0.43", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.43.tgz", + "integrity": "sha512-1zHgad1cWr1mKm2xbFaarK0Jg8WRgaQ8ubSBIo/pRdq3fEgCuqgNkL9NSAP6Rvm8zi3+Lu4JPUMN+EEx5QgX9A==", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, "node_modules/acorn": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", @@ -2958,6 +3030,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -3113,6 +3190,102 @@ "integrity": "sha512-eMTyp8AKnE5eo+mKNqG3743eb5ZND5LhBgf9F8BN2tVdhSBnOCHH7me7iTcv0BUDhUW2dBQiHWLWMy776yZW1A==", "license": "MIT" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -6499,6 +6672,14 @@ "react": ">= 16.x" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/vanilla-tilt": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/vanilla-tilt/-/vanilla-tilt-1.8.1.tgz", @@ -6768,6 +6949,33 @@ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", "license": "0BSD" }, + "node_modules/zustand": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", + "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==", + "dependencies": { + "use-sync-external-store": "1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 91a798d..b7df19e 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@marsidev/react-turnstile": "^1.0.2", "@monaco-editor/react": "^4.6.0", "@shikijs/monaco": "^1.22.2", + "@xyflow/react": "^12.3.2", "antd": "^5.21.6", "antd-style": "^3.7.1", "axios": "^1.7.7", diff --git a/src/components/Playground/CodeEditor/index.style.ts b/src/components/Playground/CodeEditor/index.style.ts index 8b61578..6c48409 100644 --- a/src/components/Playground/CodeEditor/index.style.ts +++ b/src/components/Playground/CodeEditor/index.style.ts @@ -2,9 +2,7 @@ import { createStyles } from 'antd-style' export default createStyles(({ token }) => ({ root: { - position: 'relative', - width: '100%', - height: '100%' + position: 'relative' }, errorMessage: { diff --git a/src/components/Playground/Output/Preview/Render.tsx b/src/components/Playground/Output/Preview/Render.tsx index 40d015d..ac37647 100644 --- a/src/components/Playground/Output/Preview/Render.tsx +++ b/src/components/Playground/Output/Preview/Render.tsx @@ -1,7 +1,11 @@ -import { ChangeEvent } from 'react' +import Icon from '@ant-design/icons' +import { Background, Controls, MiniMap, Node, Panel, ReactFlow } from '@xyflow/react' +import '@xyflow/react/dist/style.css' +import { AppContext } from '@/App' import useStyles from '@/components/Playground/Output/Preview/render.style' import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw' -import HideScrollbar from '@/components/common/HideScrollbar' +import devices, { DeviceName } from '@/components/Playground/Output/Preview/devices' +import Simulation, { SimulationData } from '@/components/Playground/Output/Preview/Simulation' interface RenderProps { iframeKey: string @@ -10,7 +14,7 @@ interface RenderProps { } interface IMessage { - type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE' | 'SCALE' + type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE' msg: string data: { compiledCode?: string @@ -18,131 +22,37 @@ interface IMessage { } } -interface IDevice { - name: string - width: number - height: number -} - const getIframeUrl = (iframeRaw: string) => { - const shimsUrl = '//unpkg.com/es-module-shims/dist/es-module-shims.js' - // 判断浏览器是否支持esm ,不支持esm就引入es-module-shims - const newIframeRaw = - typeof import.meta === 'undefined' - ? iframeRaw.replace( - '', - `` - ) - : iframeRaw - return URL.createObjectURL(new Blob([newIframeRaw], { type: 'text/html' })) + return URL.createObjectURL(new Blob([iframeRaw], { type: 'text/html' })) } const iframeUrl = getIframeUrl(iframeRaw) const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) => { - const { styles, theme, cx } = useStyles() + const { styles, theme } = useStyles() + const { isDarkMode } = useContext(AppContext) const iframeRef = useRef(null) const [isLoaded, setIsLoaded] = useState(false) - const [selectedDevice, setSelectedDevice] = useState('Pixel 7') - const [zoom, setZoom] = useState(1) + const [selectedDevice, setSelectedDevice] = useState('Pixel 7') const [isRotate, setIsRotate] = useState(false) - const devices: IDevice[] = [ + const nodes: Node[] = [ { - name: 'iPhone SE', - width: 375, - height: 667 - }, - { - name: 'iPhone XR', - width: 414, - height: 896 - }, - { - name: 'iPhone 12 Pro', - width: 390, - height: 844 - }, - { - name: 'iPhone 14 Pro Max', - width: 430, - height: 932 - }, - { - name: 'Pixel 7', - width: 412, - height: 915 - }, - { - name: 'Samsung Galaxy S8+', - width: 360, - height: 740 - }, - { - name: 'Samsung Galaxy S20 Ultra', - width: 412, - height: 915 - }, - { - name: 'iPad Mini', - width: 768, - height: 1024 - }, - { - name: 'iPad Air', - width: 820, - height: 1180 - }, - { - name: 'iPad Pro', - width: 1024, - height: 1366 - }, - { - name: 'Surface Pro 7', - width: 912, - height: 1368 - }, - { - name: 'Surface Duo', - width: 540, - height: 720 - }, - { - name: 'Galaxy Fold', - width: 280, - height: 653 - }, - { - name: 'Asus Zenbook Fold', - width: 853, - height: 1280 - }, - { - name: 'Samsung Galaxy A51/71', - width: 412, - height: 914 - }, - { - name: 'Nest Hub', - width: 1024, - height: 600 - }, - { - name: 'Nest Hub Max', - width: 1280, - height: 800 + id: 'device', + type: 'simulation', + position: { x: 0, y: 0 }, + data: { + deviceWidth: devices[selectedDevice].width, + deviceHeight: devices[selectedDevice].height, + isRotate, + iframeKey, + iframeRef, + iframeUrl, + setIsLoaded + } } ] - const handleOnChangeDevice = (e: ChangeEvent) => { - setSelectedDevice(e.target.value) - } - - const handleOnChangeZoom = (e: ChangeEvent) => { - setZoom(Number(e.target.value)) - } - const handleOnRotateDevice = () => { setIsRotate(!isRotate) } @@ -159,98 +69,44 @@ const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) => } }, [isLoaded, compiledCode]) - useEffect(() => { - if (isLoaded) { - iframeRef.current?.contentWindow?.postMessage( - { - type: 'SCALE', - data: { zoom } - } as IMessage, - '*' - ) - } - }, [isLoaded, zoom]) - return mobileMode ? ( <> - -
-
-
+ + + + + ({ + label: item.name, + value: item.name + }))} + value={selectedDevice} + onChange={setSelectedDevice} /> -
value.name === selectedDevice) - ?.height ?? 915) - : (devices.find((value) => value.name === selectedDevice) - ?.width ?? 412), - height: isRotate - ? (devices.find((value) => value.name === selectedDevice) - ?.width ?? 412) - : (devices.find((value) => value.name === selectedDevice) - ?.height ?? 915) - }} - > -