chore: renames pages to views

This commit is contained in:
Jacob Fletcher
2024-03-04 10:07:44 -05:00
parent bef3ebf9cc
commit 8b29723317
156 changed files with 17 additions and 18 deletions

View File

@@ -0,0 +1,38 @@
import type { CollisionDetection } from '@dnd-kit/core'
import { rectIntersection } from '@dnd-kit/core'
// If the toolbar exits the preview area, we need to reset its position
// This will prevent the toolbar from getting stuck outside the preview area
export const customCollisionDetection: CollisionDetection = ({
collisionRect,
droppableContainers,
...args
}) => {
const droppableContainer = droppableContainers.find(({ id }) => id === 'live-preview-area')
const rectIntersectionCollisions = rectIntersection({
...args,
collisionRect,
droppableContainers: [droppableContainer],
})
// Collision detection algorithms return an array of collisions
if (rectIntersectionCollisions.length === 0) {
// The preview area is not intersecting, return early
return rectIntersectionCollisions
}
// Compute whether the draggable element is completely contained within the preview area
const previewAreaRect = droppableContainer?.rect?.current
const isContained =
collisionRect.top >= previewAreaRect.top &&
collisionRect.left >= previewAreaRect.left &&
collisionRect.bottom <= previewAreaRect.bottom &&
collisionRect.right <= previewAreaRect.right
if (isContained) {
return rectIntersectionCollisions
}
}

View File

@@ -0,0 +1,84 @@
import type { LivePreviewConfig } from 'payload/config'
import type { fieldSchemaToJSON } from 'payload/utilities'
import type { Dispatch } from 'react'
import { createContext, useContext } from 'react'
import type { usePopupWindow } from '../usePopupWindow'
import type { SizeReducerAction } from './sizeReducer'
export interface LivePreviewContextType {
appIsReady: boolean
breakpoint: LivePreviewConfig['breakpoints'][number]['name']
breakpoints: LivePreviewConfig['breakpoints']
fieldSchemaJSON?: ReturnType<typeof fieldSchemaToJSON>
iframeHasLoaded: boolean
iframeRef: React.RefObject<HTMLIFrameElement>
isPopupOpen: boolean
measuredDeviceSize: {
height: number
width: number
}
openPopupWindow: ReturnType<typeof usePopupWindow>['openPopupWindow']
popupRef?: React.MutableRefObject<Window | null>
previewWindowType: 'iframe' | 'popup'
setAppIsReady: (appIsReady: boolean) => void
setBreakpoint: (breakpoint: LivePreviewConfig['breakpoints'][number]['name']) => void
setHeight: (height: number) => void
setIframeHasLoaded: (loaded: boolean) => void
setMeasuredDeviceSize: (size: { height: number; width: number }) => void
setPreviewWindowType: (previewWindowType: 'iframe' | 'popup') => void
setSize: Dispatch<SizeReducerAction>
setToolbarPosition: (position: { x: number; y: number }) => void
setWidth: (width: number) => void
setZoom: (zoom: number) => void
size: {
height: number
width: number
}
toolbarPosition: {
x: number
y: number
}
url: string | undefined
zoom: number
}
export const LivePreviewContext = createContext<LivePreviewContextType>({
appIsReady: false,
breakpoint: undefined,
breakpoints: undefined,
fieldSchemaJSON: undefined,
iframeHasLoaded: false,
iframeRef: undefined,
isPopupOpen: false,
measuredDeviceSize: {
height: 0,
width: 0,
},
openPopupWindow: () => {},
popupRef: undefined,
previewWindowType: 'iframe',
setAppIsReady: () => {},
setBreakpoint: () => {},
setHeight: () => {},
setIframeHasLoaded: () => {},
setMeasuredDeviceSize: () => {},
setPreviewWindowType: () => {},
setSize: () => {},
setToolbarPosition: () => {},
setWidth: () => {},
setZoom: () => {},
size: {
height: 0,
width: 0,
},
toolbarPosition: {
x: 0,
y: 0,
},
url: undefined,
zoom: 1,
})
export const useLivePreviewContext = () => useContext(LivePreviewContext)

View File

@@ -0,0 +1,198 @@
'use client'
import type { EditViewProps, LivePreviewConfig } from 'payload/config'
import { DndContext } from '@dnd-kit/core'
import React, { useCallback, useEffect, useState } from 'react'
import type { usePopupWindow } from '../usePopupWindow'
import { customCollisionDetection } from './collisionDetection'
import { LivePreviewContext } from './context'
import { sizeReducer } from './sizeReducer'
export type LivePreviewProviderProps = EditViewProps & {
appIsReady?: boolean
breakpoints?: LivePreviewConfig['breakpoints']
children: React.ReactNode
deviceSize?: {
height: number
width: number
}
isPopupOpen?: boolean
openPopupWindow?: ReturnType<typeof usePopupWindow>['openPopupWindow']
popupRef?: React.MutableRefObject<Window>
url?: string
}
export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = (props) => {
const { breakpoints, children, isPopupOpen, openPopupWindow, popupRef, url } = props
const [previewWindowType, setPreviewWindowType] = useState<'iframe' | 'popup'>('iframe')
const [appIsReady, setAppIsReady] = useState(false)
const iframeRef = React.useRef<HTMLIFrameElement>(null)
const [iframeHasLoaded, setIframeHasLoaded] = useState(false)
const [zoom, setZoom] = useState(1)
const [position, setPosition] = useState({ x: 0, y: 0 })
const [size, setSize] = React.useReducer(sizeReducer, { height: 0, width: 0 })
const [measuredDeviceSize, setMeasuredDeviceSize] = useState({
height: 0,
width: 0,
})
const [breakpoint, setBreakpoint] =
React.useState<LivePreviewConfig['breakpoints'][0]['name']>('responsive')
// const [fieldSchemaJSON] = useState(() => {
// let fields: Field[]
// if ('collection' in props) {
// const { collection } = props
// fields = collection.fields
// }
// if ('global' in props) {
// const { global } = props
// fields = global.fields
// }
// return fieldSchemaToJSON(fields)
// })
// 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
// otherwise reset it back to the previous position
// TODO: reset to the nearest edge of the preview area
if (ev.over && ev.over.id === 'live-preview-area') {
const newPos = {
x: position.x + ev.delta.x,
y: position.y + ev.delta.y,
}
setPosition(newPos)
} else {
// reset
}
}
const setWidth = useCallback(
(width) => {
setSize({ type: 'width', value: width })
},
[setSize],
)
const setHeight = useCallback(
(height) => {
setSize({ type: 'height', value: height })
},
[setSize],
)
// 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(() => {
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: foundBreakpoint.height,
width: foundBreakpoint.width,
},
})
}
}, [breakpoint, breakpoints])
// Receive the `ready` message from the popup window
// This indicates that the app is ready to receive `window.postMessage` events
// This is also the only cross-origin way of detecting when a popup window has loaded
// Unlike iframe elements which have an `onLoad` handler, there is no way to access `window.open` on popups
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (
url?.startsWith(event.origin) &&
event.data &&
typeof event.data === 'object' &&
event.data.type === 'payload-live-preview'
) {
if (event.data.ready) {
setAppIsReady(true)
}
}
}
window.addEventListener('message', handleMessage)
return () => {
window.removeEventListener('message', handleMessage)
}
}, [url])
const handleWindowChange = useCallback(
(type: 'iframe' | 'popup') => {
setAppIsReady(false)
setPreviewWindowType(type)
if (type === 'popup') openPopupWindow()
},
[openPopupWindow],
)
// when the user closes the popup window, switch back to the iframe
// the `usePopupWindow` reports the `isPopupOpen` state for us to use here
useEffect(() => {
if (!isPopupOpen) {
handleWindowChange('iframe')
}
}, [isPopupOpen, handleWindowChange])
return (
<LivePreviewContext.Provider
value={{
appIsReady,
breakpoint,
breakpoints,
fieldSchemaJSON: [],
iframeHasLoaded,
iframeRef,
isPopupOpen,
measuredDeviceSize,
openPopupWindow,
popupRef,
previewWindowType,
setAppIsReady,
setBreakpoint,
setHeight,
setIframeHasLoaded,
setMeasuredDeviceSize,
setPreviewWindowType: handleWindowChange,
setSize,
setToolbarPosition: setPosition,
setWidth,
setZoom,
size,
toolbarPosition: position,
url,
zoom,
}}
>
<DndContext collisionDetection={customCollisionDetection} onDragEnd={handleDragEnd}>
{children}
</DndContext>
</LivePreviewContext.Provider>
)
}

View File

@@ -0,0 +1,39 @@
// export const sizeReducer: (state, action) => {
// switch (action.type) {
// case 'width':
// return { ...state, width: action.value }
// case 'height':
// return { ...state, height: action.value }
// default:
// return { ...state, ...(action?.value || {}) }
// }
// },
type SizeReducerState = {
height: number
width: number
}
export type SizeReducerAction =
| {
type: 'height' | 'width'
value: number
}
| {
type: 'reset'
value: {
height: number
width: number
}
}
export const sizeReducer = (state: SizeReducerState, action: SizeReducerAction) => {
switch (action.type) {
case 'width':
return { ...state, width: action.value }
case 'height':
return { ...state, height: action.value }
default:
return { ...state, ...(action?.value || {}) }
}
}