chore: dynamic device sizing (#3426)
This commit is contained in:
@@ -11,6 +11,10 @@ export interface LivePreviewContextType {
|
||||
deviceFrameRef: React.RefObject<HTMLDivElement>
|
||||
iframeHasLoaded: boolean
|
||||
iframeRef: React.RefObject<HTMLIFrameElement>
|
||||
measuredDeviceSize: {
|
||||
height: number
|
||||
width: number
|
||||
}
|
||||
setBreakpoint: (breakpoint: LivePreview['breakpoints'][number]['name']) => void
|
||||
setHeight: (height: number) => void
|
||||
setIframeHasLoaded: (loaded: boolean) => void
|
||||
@@ -35,6 +39,10 @@ export const LivePreviewContext = createContext<LivePreviewContextType>({
|
||||
deviceFrameRef: undefined,
|
||||
iframeHasLoaded: false,
|
||||
iframeRef: undefined,
|
||||
measuredDeviceSize: {
|
||||
height: 0,
|
||||
width: 0,
|
||||
},
|
||||
setBreakpoint: () => {},
|
||||
setHeight: () => {},
|
||||
setIframeHasLoaded: () => {},
|
||||
@@ -39,23 +39,6 @@ export const LivePreviewProvider: React.FC<ToolbarProviderProps> = (props) => {
|
||||
const [breakpoint, setBreakpoint] =
|
||||
React.useState<LivePreview['breakpoints'][0]['name']>('responsive')
|
||||
|
||||
const foundBreakpoint = breakpoint && breakpoints.find((bp) => bp.name === breakpoint)
|
||||
|
||||
let margin = '0'
|
||||
|
||||
if (foundBreakpoint && breakpoint !== 'responsive') {
|
||||
margin = '0 auto'
|
||||
|
||||
if (
|
||||
typeof zoom === 'number' &&
|
||||
typeof foundBreakpoint.width === 'number' &&
|
||||
typeof foundBreakpoint.height === 'number'
|
||||
) {
|
||||
// keep it centered horizontally
|
||||
margin = `0 ${foundBreakpoint.width / 2 / zoom}px`
|
||||
}
|
||||
}
|
||||
|
||||
// The toolbar needs to freely drag and drop around the page
|
||||
const handleDragEnd = (ev) => {
|
||||
// only update position if the toolbar is completely within the preview area
|
||||
@@ -87,19 +70,31 @@ export const LivePreviewProvider: React.FC<ToolbarProviderProps> = (props) => {
|
||||
[setSize],
|
||||
)
|
||||
|
||||
const { size: actualDeviceSize } = useResize(deviceFrameRef)
|
||||
|
||||
// explicitly set new width and height when as new breakpoints are selected
|
||||
// exclude `custom` breakpoint as it is handled by the `setWidth` and `setHeight` directly
|
||||
useEffect(() => {
|
||||
if (actualDeviceSize) {
|
||||
const foundBreakpoint = breakpoints?.find((bp) => bp.name === breakpoint)
|
||||
|
||||
if (
|
||||
foundBreakpoint &&
|
||||
breakpoint !== 'responsive' &&
|
||||
breakpoint !== 'custom' &&
|
||||
typeof foundBreakpoint?.width === 'number' &&
|
||||
typeof foundBreakpoint?.height === 'number'
|
||||
) {
|
||||
setSize({
|
||||
type: 'reset',
|
||||
value: {
|
||||
height: Number(actualDeviceSize.height.toFixed(0)),
|
||||
width: Number(actualDeviceSize.width.toFixed(0)),
|
||||
height: foundBreakpoint.height,
|
||||
width: foundBreakpoint.width,
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [actualDeviceSize])
|
||||
}, [breakpoint, breakpoints])
|
||||
|
||||
// keep an accurate measurement of the actual device size as it is truly rendered
|
||||
// this is helpful when `sizes` are non-number units like percentages, etc.
|
||||
const { size: measuredDeviceSize } = useResize(deviceFrameRef)
|
||||
|
||||
return (
|
||||
<LivePreviewContext.Provider
|
||||
@@ -109,6 +104,7 @@ export const LivePreviewProvider: React.FC<ToolbarProviderProps> = (props) => {
|
||||
deviceFrameRef,
|
||||
iframeHasLoaded,
|
||||
iframeRef,
|
||||
measuredDeviceSize,
|
||||
setBreakpoint,
|
||||
setHeight,
|
||||
setIframeHasLoaded,
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useLivePreviewContext } from '../Context/context'
|
||||
|
||||
export const DeviceContainer: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = (props) => {
|
||||
const { children } = props
|
||||
|
||||
const { breakpoint, deviceFrameRef, size, zoom } = useLivePreviewContext()
|
||||
|
||||
let x = '0'
|
||||
let margin = '0'
|
||||
|
||||
if (breakpoint && breakpoint !== 'responsive') {
|
||||
x = '-50%'
|
||||
|
||||
if (
|
||||
typeof zoom === 'number' &&
|
||||
typeof size.width === 'number' &&
|
||||
typeof size.height === 'number'
|
||||
) {
|
||||
const scaledWidth = size.width / zoom
|
||||
const difference = scaledWidth - size.width
|
||||
x = `${difference / 2}px`
|
||||
margin = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
let width = zoom ? `${100 / zoom}%` : '100%'
|
||||
let height = zoom ? `${100 / zoom}%` : '100%'
|
||||
|
||||
if (breakpoint !== 'responsive') {
|
||||
width = `${size?.width / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
height = `${size?.height / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={deviceFrameRef}
|
||||
style={{
|
||||
height,
|
||||
margin,
|
||||
transform: `translate3d(${x}, 0, 0)`,
|
||||
width,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useLivePreviewContext } from '../Context/context'
|
||||
|
||||
export const DeviceContainer: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = (props) => {
|
||||
const { children } = props
|
||||
|
||||
const { breakpoint, breakpoints, deviceFrameRef, size, zoom } = useLivePreviewContext()
|
||||
|
||||
const foundBreakpoint = breakpoint && breakpoints.find((bp) => bp.name === breakpoint)
|
||||
|
||||
let x = '0'
|
||||
let margin = '0'
|
||||
|
||||
if (foundBreakpoint && breakpoint !== 'responsive') {
|
||||
x = '-50%'
|
||||
|
||||
if (
|
||||
typeof zoom === 'number' &&
|
||||
typeof size.width === 'number' &&
|
||||
typeof size.height === 'number'
|
||||
) {
|
||||
const scaledWidth = size.width / zoom
|
||||
const difference = scaledWidth - size.width
|
||||
x = `${difference / 2}px`
|
||||
margin = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={deviceFrameRef}
|
||||
style={{
|
||||
height:
|
||||
foundBreakpoint && foundBreakpoint?.name !== 'responsive'
|
||||
? `${size?.height / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
: typeof zoom === 'number'
|
||||
? `${100 / zoom}%`
|
||||
: '100%',
|
||||
margin,
|
||||
transform: `translate3d(${x}, 0, 0)`,
|
||||
width:
|
||||
foundBreakpoint && foundBreakpoint?.name !== 'responsive'
|
||||
? `${size?.width / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
: typeof zoom === 'number'
|
||||
? `${100 / zoom}%`
|
||||
: '100%',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { forwardRef } from 'react'
|
||||
|
||||
import { useLivePreviewContext } from '../PreviewContext/context'
|
||||
import { useLivePreviewContext } from '../Context/context'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'live-preview-iframe'
|
||||
@@ -8,7 +8,7 @@
|
||||
overflow: hidden;
|
||||
|
||||
&--has-breakpoint {
|
||||
padding-top: var(--base);
|
||||
padding: var(--base);
|
||||
|
||||
.live-preview-iframe {
|
||||
border: 1px solid var(--theme-elevation-100);
|
||||
@@ -1,69 +1,21 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import type { LivePreview } from '../../../../../exports/config'
|
||||
import type { LivePreview as LivePreviewType } from '../../../../../exports/config'
|
||||
import type { EditViewProps } from '../../types'
|
||||
import type { usePopupWindow } from '../usePopupWindow'
|
||||
|
||||
import { useAllFormFields } from '../../../forms/Form/context'
|
||||
import reduceFieldsToValues from '../../../forms/Form/reduceFieldsToValues'
|
||||
import { LivePreviewProvider } from '../PreviewContext'
|
||||
import { useLivePreviewContext } from '../PreviewContext/context'
|
||||
import { IFrame } from '../PreviewIFrame'
|
||||
import { LivePreviewProvider } from '../Context'
|
||||
import { useLivePreviewContext } from '../Context/context'
|
||||
import { DeviceContainer } from '../Device'
|
||||
import { IFrame } from '../IFrame'
|
||||
import { LivePreviewToolbar } from '../Toolbar'
|
||||
import { ToolbarArea } from '../ToolbarArea'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'live-preview-window'
|
||||
|
||||
const ResponsiveWindow: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = (props) => {
|
||||
const { children } = props
|
||||
|
||||
const { breakpoint, breakpoints, deviceFrameRef, zoom } = useLivePreviewContext()
|
||||
|
||||
const foundBreakpoint = breakpoint && breakpoints.find((bp) => bp.name === breakpoint)
|
||||
|
||||
let x = '0'
|
||||
|
||||
if (foundBreakpoint && breakpoint !== 'responsive') {
|
||||
x = '-50%'
|
||||
|
||||
if (
|
||||
typeof zoom === 'number' &&
|
||||
typeof foundBreakpoint.width === 'number' &&
|
||||
typeof foundBreakpoint.height === 'number'
|
||||
) {
|
||||
// keep it centered horizontally
|
||||
x = `${foundBreakpoint.width / 2}px`
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${baseClass}__responsive-window`}
|
||||
ref={deviceFrameRef}
|
||||
style={{
|
||||
height:
|
||||
foundBreakpoint && typeof foundBreakpoint?.height === 'number'
|
||||
? `${foundBreakpoint?.height / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
: typeof zoom === 'number'
|
||||
? `${100 / zoom}%`
|
||||
: '100%',
|
||||
transform: `translate3d(${x}, 0, 0)`,
|
||||
width:
|
||||
foundBreakpoint && typeof foundBreakpoint?.width === 'number'
|
||||
? `${foundBreakpoint?.width / (typeof zoom === 'number' ? zoom : 1)}px`
|
||||
: typeof zoom === 'number'
|
||||
? `${100 / zoom}%`
|
||||
: '100%',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Preview: React.FC<
|
||||
EditViewProps & {
|
||||
popupState: ReturnType<typeof usePopupWindow>
|
||||
@@ -77,7 +29,7 @@ const Preview: React.FC<
|
||||
|
||||
const { iframeHasLoaded, iframeRef, setIframeHasLoaded } = useLivePreviewContext()
|
||||
|
||||
const { breakpoint, toolbarPosition } = useLivePreviewContext()
|
||||
const { breakpoint } = useLivePreviewContext()
|
||||
|
||||
const [fields] = useAllFormFields()
|
||||
|
||||
@@ -129,26 +81,18 @@ const Preview: React.FC<
|
||||
>
|
||||
<ToolbarArea>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
<ResponsiveWindow>
|
||||
<DeviceContainer>
|
||||
<IFrame ref={iframeRef} setIframeHasLoaded={setIframeHasLoaded} url={url} />
|
||||
</ResponsiveWindow>
|
||||
</DeviceContainer>
|
||||
</div>
|
||||
<LivePreviewToolbar
|
||||
{...props}
|
||||
iframeRef={iframeRef}
|
||||
style={{
|
||||
left: `${toolbarPosition.x}px`,
|
||||
top: `${toolbarPosition.y}px`,
|
||||
}}
|
||||
url={url}
|
||||
/>
|
||||
<LivePreviewToolbar {...props} iframeRef={iframeRef} url={url} />
|
||||
</ToolbarArea>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const PreviewWindow: React.FC<
|
||||
export const LivePreview: React.FC<
|
||||
EditViewProps & {
|
||||
popupState: ReturnType<typeof usePopupWindow>
|
||||
url?: string
|
||||
@@ -156,7 +100,7 @@ export const PreviewWindow: React.FC<
|
||||
> = (props) => {
|
||||
let url
|
||||
|
||||
let breakpoints: LivePreview['breakpoints'] = [
|
||||
let breakpoints: LivePreviewType['breakpoints'] = [
|
||||
{
|
||||
name: 'responsive',
|
||||
height: '100%',
|
||||
@@ -1,14 +1,12 @@
|
||||
@import '../../../../../scss/styles.scss';
|
||||
|
||||
.live-preview-toolbar {
|
||||
&__size {
|
||||
width: 50px;
|
||||
height: var(--base);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--theme-elevation-200);
|
||||
background: var(--theme-elevation-100);
|
||||
border-radius: 2px;
|
||||
font-size: small;
|
||||
}
|
||||
.toolbar-input {
|
||||
width: 50px;
|
||||
height: var(--base);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--theme-elevation-200);
|
||||
background: var(--theme-elevation-100);
|
||||
border-radius: 2px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
import React, { useCallback } from 'react'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
|
||||
import { useLivePreviewContext } from '../../PreviewContext/context'
|
||||
import { useLivePreviewContext } from '../../Context/context'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'live-preview-toolbar'
|
||||
const baseClass = 'toolbar-input'
|
||||
|
||||
export const PreviewFrameSizeInput: React.FC<{
|
||||
axis?: 'x' | 'y'
|
||||
}> = (props) => {
|
||||
const { axis } = props
|
||||
|
||||
const { setHeight, setWidth, size } = useLivePreviewContext()
|
||||
const { breakpoint, measuredDeviceSize, setBreakpoint, setSize, size, zoom } =
|
||||
useLivePreviewContext()
|
||||
|
||||
// const [size, setSize] = React.useState<string>(() => {
|
||||
// if (sizeToUse === 'width') {
|
||||
// return deviceSize?.width.toFixed(0)
|
||||
// }
|
||||
|
||||
// return deviceSize?.height.toFixed(0)
|
||||
// })
|
||||
|
||||
// useEffect(() => {
|
||||
// if (sizeToUse === 'width') {
|
||||
// setSize(deviceSize?.width.toFixed(0))
|
||||
// } else {
|
||||
// setSize(deviceSize?.height.toFixed(0))
|
||||
// }
|
||||
// }, [deviceSize])
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (axis === 'x') {
|
||||
setWidth(Number(e.target.value))
|
||||
} else {
|
||||
setHeight(Number(e.target.value))
|
||||
}
|
||||
},
|
||||
[axis, setWidth, setHeight],
|
||||
const [internalState, setInternalState] = React.useState<number>(
|
||||
(axis === 'x' ? measuredDeviceSize?.width : measuredDeviceSize?.height) || 0,
|
||||
)
|
||||
|
||||
const sizeValue = axis === 'x' ? size?.width : size?.height
|
||||
// when the input is changed manually, we need to set the breakpoint as `custom`
|
||||
// this will then allow us to set an explicit width and height
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let newValue = Number(e.target.value)
|
||||
|
||||
if (newValue < 0) newValue = 0
|
||||
|
||||
setInternalState(newValue)
|
||||
setBreakpoint('custom')
|
||||
|
||||
// be sure to set _both_ axis values to so that the other axis doesn't fallback to 0 on initial change
|
||||
// this is because the `responsive` size is '100%' in CSS, and `0` in initial state
|
||||
setSize({
|
||||
type: 'reset',
|
||||
value: {
|
||||
height: axis === 'y' ? newValue : Number(measuredDeviceSize?.height.toFixed(0)) * zoom,
|
||||
width: axis === 'x' ? newValue : Number(measuredDeviceSize?.width.toFixed(0)) * zoom,
|
||||
},
|
||||
})
|
||||
},
|
||||
[axis, setBreakpoint, measuredDeviceSize, setSize, zoom],
|
||||
)
|
||||
|
||||
// if the breakpoint is `responsive` then the device's div will have `100%` width and height
|
||||
// so we need to take the measurements provided by `actualDeviceSize` and sync internal state
|
||||
useEffect(() => {
|
||||
if (breakpoint === 'responsive' && measuredDeviceSize) {
|
||||
if (axis === 'x') setInternalState(Number(measuredDeviceSize.width.toFixed(0)) * zoom)
|
||||
else setInternalState(Number(measuredDeviceSize.height.toFixed(0)) * zoom)
|
||||
}
|
||||
|
||||
if (breakpoint !== 'responsive' && size) {
|
||||
setInternalState(axis === 'x' ? size.width : size.height)
|
||||
}
|
||||
}, [breakpoint, axis, measuredDeviceSize, size, zoom])
|
||||
|
||||
return (
|
||||
<input
|
||||
className={`${baseClass}__size`}
|
||||
disabled // enable this once its wired up properly
|
||||
className={baseClass}
|
||||
min={0}
|
||||
onChange={handleChange}
|
||||
step={1}
|
||||
type="number"
|
||||
value={sizeValue}
|
||||
value={internalState || 0}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useDraggable } from '@dnd-kit/core'
|
||||
import React from 'react'
|
||||
|
||||
import type { ToolbarProviderProps } from '../PreviewContext'
|
||||
import type { ToolbarProviderProps } from '../Context'
|
||||
|
||||
import { X } from '../../..'
|
||||
import { ExternalLinkIcon } from '../../../graphics/ExternalLink'
|
||||
import DragHandle from '../../../icons/Drag'
|
||||
import { useLivePreviewContext } from '../PreviewContext/context'
|
||||
import { useLivePreviewContext } from '../Context/context'
|
||||
import { PreviewFrameSizeInput } from './SizeInput'
|
||||
import './index.scss'
|
||||
|
||||
@@ -15,16 +15,15 @@ const baseClass = 'live-preview-toolbar'
|
||||
export const LivePreviewToolbar: React.FC<
|
||||
Omit<ToolbarProviderProps, 'children'> & {
|
||||
iframeRef: React.RefObject<HTMLIFrameElement>
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
popupState: { openPopupWindow },
|
||||
style,
|
||||
url,
|
||||
} = props
|
||||
|
||||
const { breakpoint, breakpoints, setBreakpoint, setZoom, zoom } = useLivePreviewContext()
|
||||
const { breakpoint, breakpoints, setBreakpoint, setZoom, toolbarPosition, zoom } =
|
||||
useLivePreviewContext()
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||
id: 'live-preview-toolbar',
|
||||
@@ -34,10 +33,15 @@ export const LivePreviewToolbar: React.FC<
|
||||
<div
|
||||
className={baseClass}
|
||||
style={{
|
||||
...style,
|
||||
transform: transform
|
||||
? `translate3d(${transform?.x || 0}px, ${transform?.y || 0}px, 0)`
|
||||
: undefined,
|
||||
left: `${toolbarPosition.x}px`,
|
||||
top: `${toolbarPosition.y}px`,
|
||||
...(transform
|
||||
? {
|
||||
transform: transform
|
||||
? `translate3d(${transform?.x || 0}px, ${transform?.y || 0}px, 0)`
|
||||
: undefined,
|
||||
}
|
||||
: {}),
|
||||
}}
|
||||
>
|
||||
<button
|
||||
@@ -61,6 +65,11 @@ export const LivePreviewToolbar: React.FC<
|
||||
{bp.label}
|
||||
</option>
|
||||
))}
|
||||
{breakpoint === 'custom' && (
|
||||
// Dynamically add this option so that it only appears when the width and height inputs are explicitly changed
|
||||
// TODO: Translate this string
|
||||
<option value="custom">Custom</option>
|
||||
)}
|
||||
</select>
|
||||
)}
|
||||
<div className={`${baseClass}__device-size`}>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { filterFields } from '../../forms/RenderFields/filterFields'
|
||||
import { fieldTypes } from '../../forms/field-types'
|
||||
import LeaveWithoutSaving from '../../modals/LeaveWithoutSaving'
|
||||
import Meta from '../../utilities/Meta'
|
||||
import { PreviewWindow } from './PreviewWindow'
|
||||
import { LivePreview } from './Preview'
|
||||
import './index.scss'
|
||||
import { usePopupWindow } from './usePopupWindow'
|
||||
|
||||
@@ -111,7 +111,7 @@ export const LivePreviewView: React.FC<EditViewProps> = (props) => {
|
||||
)}
|
||||
</Gutter>
|
||||
</div>
|
||||
<PreviewWindow {...props} popupState={popupState} url={url} />
|
||||
<LivePreview {...props} popupState={popupState} url={url} />
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
@@ -1,52 +1,75 @@
|
||||
import payload, { Payload } from '../../packages/payload/src'
|
||||
import { spawn } from 'child_process'
|
||||
import path from 'path'
|
||||
|
||||
export const startLivePreviewDemo = async (args: { payload: Payload }): Promise<void> => {
|
||||
import type { Payload } from '../../packages/payload/src'
|
||||
|
||||
const installNodeModules = async (args: { payload: Payload }): Promise<void> => {
|
||||
const { payload } = args
|
||||
|
||||
let installing = false
|
||||
let started = false
|
||||
|
||||
// Install the node modules for the Next.js app
|
||||
const installation = spawn('yarn', ['install'], {
|
||||
cwd: path.resolve(__dirname, './next-app'),
|
||||
})
|
||||
return new Promise(function (resolve) {
|
||||
// Install the node modules for the Next.js app
|
||||
const installation = spawn('yarn', ['install'], {
|
||||
cwd: path.resolve(__dirname, './next-app'),
|
||||
})
|
||||
|
||||
installation.stdout.on('data', (data) => {
|
||||
if (!installing) {
|
||||
payload.logger.info('Installing Next.js...')
|
||||
installing = true
|
||||
}
|
||||
installation.stdout.on('data', (data) => {
|
||||
if (!installing) {
|
||||
payload.logger.info('Installing Next.js...')
|
||||
installing = true
|
||||
}
|
||||
|
||||
payload.logger.info(data.toString())
|
||||
})
|
||||
payload.logger.info(data.toString())
|
||||
})
|
||||
|
||||
installation.stderr.on('data', (data) => {
|
||||
payload.logger.error(data.toString())
|
||||
})
|
||||
installation.stderr.on('data', (data) => {
|
||||
payload.logger.error(data.toString())
|
||||
})
|
||||
|
||||
installation.on('exit', (code) => {
|
||||
payload.logger.info(`Next.js exited with code ${code}`)
|
||||
})
|
||||
|
||||
// Boot up the Next.js app
|
||||
const app = spawn('yarn', ['dev'], {
|
||||
cwd: path.resolve(__dirname, './next-app'),
|
||||
})
|
||||
|
||||
app.stdout.on('data', (data) => {
|
||||
if (!started) {
|
||||
payload.logger.info('Starting Next.js...')
|
||||
started = true
|
||||
}
|
||||
|
||||
payload.logger.info(data.toString())
|
||||
})
|
||||
|
||||
app.stderr.on('data', (data) => {
|
||||
payload.logger.error(data.toString())
|
||||
})
|
||||
|
||||
app.on('exit', (code) => {
|
||||
payload.logger.info(`Next.js exited with code ${code}`)
|
||||
installation.on('exit', () => {
|
||||
payload.logger.info('Done')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const bootNextApp = async (args: { payload: Payload }): Promise<void> => {
|
||||
const { payload } = args
|
||||
|
||||
let started = false
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
// Boot up the Next.js app
|
||||
const app = spawn('yarn', ['dev'], {
|
||||
cwd: path.resolve(__dirname, './next-app'),
|
||||
})
|
||||
|
||||
app.stdout.on('data', (data) => {
|
||||
if (!started) {
|
||||
payload.logger.info('Starting Next.js...')
|
||||
started = true
|
||||
}
|
||||
|
||||
payload.logger.info(data.toString())
|
||||
|
||||
if (data.toString().includes('Ready in')) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
app.stderr.on('data', (data) => {
|
||||
payload.logger.error(data.toString())
|
||||
})
|
||||
|
||||
app.on('exit', (code) => {
|
||||
payload.logger.info(`Next.js exited with code ${code}`)
|
||||
reject()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const startLivePreviewDemo = async (args: { payload: Payload }): Promise<void> => {
|
||||
await installNodeModules(args)
|
||||
await bootNextApp(args)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user