Files
oxygen-ui/src/components/Playground/Output/Preview/Render.tsx

256 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ChangeEvent } from 'react'
import '@/components/Playground/Output/Preview/render.scss'
import { COLOR_FONT_MAIN } from '@/constants/common.constants'
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
import HideScrollbar from '@/components/common/HideScrollbar'
interface RenderProps {
iframeKey: string
compiledCode: string
mobileMode?: boolean
}
interface IMessage {
type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE' | 'SCALE'
msg: string
data: {
compiledCode?: string
zoom?: number
}
}
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(
'<!-- es-module-shims -->',
`<script async src="${shimsUrl}"></script>`
)
: iframeRaw
return URL.createObjectURL(new Blob([newIframeRaw], { type: 'text/html' }))
}
const iframeUrl = getIframeUrl(iframeRaw)
const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) => {
const iframeRef = useRef<HTMLIFrameElement>(null)
const [isLoaded, setIsLoaded] = useState(false)
const [selectedDevice, setSelectedDevice] = useState('Pixel 7')
const [zoom, setZoom] = useState(1)
const [isRotate, setIsRotate] = useState(false)
const devices: IDevice[] = [
{
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
}
]
const handleOnChangeDevice = (e: ChangeEvent<HTMLSelectElement>) => {
setSelectedDevice(e.target.value)
}
const handleOnChangeZoom = (e: ChangeEvent<HTMLInputElement>) => {
setZoom(Number(e.target.value))
}
const handleOnRotateDevice = () => {
setIsRotate(!isRotate)
}
useEffect(() => {
if (isLoaded) {
iframeRef.current?.contentWindow?.postMessage(
{
type: 'UPDATE',
data: { compiledCode }
} as IMessage,
'*'
)
}
}, [isLoaded, compiledCode])
useEffect(() => {
if (isLoaded) {
iframeRef.current?.contentWindow?.postMessage(
{
type: 'SCALE',
data: { zoom: zoom }
} as IMessage,
'*'
)
}
}, [isLoaded, zoom])
return mobileMode ? (
<>
<HideScrollbar
className={'mobile-mode-background'}
isShowVerticalScrollbar
isShowHorizontalScrollbar
autoHideWaitingTime={1000}
>
<div className={'mobile-mode-content'} style={{ zoom }}>
<div className={`device${isRotate ? ' rotate' : ''}`}>
<div className={`device-header${isRotate ? ' rotate' : ''}`} />
<div
className={`device-content${isRotate ? ' rotate' : ''}`}
style={{
width: isRotate
? (devices.find((value) => 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)
}}
>
<iframe
data-component={'playground-output-preview-render'}
key={iframeKey}
ref={iframeRef}
src={iframeUrl}
onLoad={() => setIsLoaded(true)}
sandbox="allow-downloads allow-forms allow-modals allow-scripts"
allow={'clipboard-read; clipboard-write'}
/>
</div>
<div className={`device-footer${isRotate ? ' rotate' : ''}`} />
</div>
</div>
</HideScrollbar>
<div className={'switch-device'}>
<IconOxygenMobile fill={COLOR_FONT_MAIN} />
<select value={selectedDevice} onChange={handleOnChangeDevice}>
{devices.map((value) => (
<option value={value.name}>{value.name}</option>
))}
</select>
<div className={'rotate-device'} title={'旋转屏幕'} onClick={handleOnRotateDevice}>
{isRotate ? (
<IconOxygenRotateRight fill={COLOR_FONT_MAIN} />
) : (
<IconOxygenRotateLeft fill={COLOR_FONT_MAIN} />
)}
</div>
</div>
<div className={'switch-zoom'}>
<IconOxygenZoom fill={COLOR_FONT_MAIN} />
<input
type={'range'}
min={0.5}
max={2}
step={0.1}
value={zoom}
onChange={handleOnChangeZoom}
/>
</div>
</>
) : (
<iframe
data-component={'playground-output-preview-render'}
key={iframeKey}
ref={iframeRef}
src={iframeUrl}
onLoad={() => setIsLoaded(true)}
sandbox={'allow-downloads allow-forms allow-modals allow-scripts'}
allow={'clipboard-read; clipboard-write'}
/>
)
}
export default Render