Refactor TemplateEditor component to utilize TemplatePreview for improved preview functionality; remove unnecessary state and effects, enhancing code clarity and maintainability.
This commit is contained in:
parent
b6cd1bac0b
commit
c7ddcd36e3
@ -1,14 +1,11 @@
|
||||
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Flex, Alert, Card, Spin, Splitter, Button, Modal, Input } from 'antd'
|
||||
import { Flex, Alert, Card, Spin, Splitter, Button, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon.jsx'
|
||||
import CheckCircleIcon from '../../Icons/CheckCircleIcon.jsx'
|
||||
import ObjectProperty from '../common/ObjectProperty.jsx'
|
||||
import { ApiServerContext } from '../context/ApiServerContext.jsx'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||
import TemplatePreview from './TemplatePreview.jsx'
|
||||
|
||||
const TemplateEditor = ({
|
||||
objectData,
|
||||
@ -17,134 +14,36 @@ const TemplateEditor = ({
|
||||
isEditing,
|
||||
style
|
||||
}) => {
|
||||
const iframeRef = useRef(null)
|
||||
const { fetchTemplatePreview } = useContext(ApiServerContext)
|
||||
const [testObjectOpen, setTestObjectOpen] = useState(false)
|
||||
const [previewMessage, setPreviewMessage] = useState('No issues found.')
|
||||
const [previewError, setPreviewError] = useState(false)
|
||||
const [previewContent, setPreviewContent] = useState('')
|
||||
const [reloadLoading, setReloadLoading] = useState(false)
|
||||
const [previewScale, setPreviewScale] = useState(1)
|
||||
|
||||
const updatePreviewContent = (html) => {
|
||||
if (iframeRef.current) {
|
||||
// Save current scroll position
|
||||
const scrollY = iframeRef.current.contentWindow.scrollY
|
||||
const scrollX = iframeRef.current.contentWindow.scrollX
|
||||
|
||||
// Update srcDoc
|
||||
setPreviewContent(html)
|
||||
|
||||
// Restore scroll position after iframe loads new content
|
||||
const handleLoad = () => {
|
||||
iframeRef.current.contentWindow.scrollTo(scrollX, scrollY)
|
||||
iframeRef.current.removeEventListener('load', handleLoad)
|
||||
const handlePreviewMessage = (message, isError) => {
|
||||
setPreviewMessage(message)
|
||||
setPreviewError(isError)
|
||||
}
|
||||
iframeRef.current.addEventListener('load', handleLoad)
|
||||
}
|
||||
}
|
||||
|
||||
const reloadPreview = useCallback(
|
||||
(content, testObject = {}, scale = 1) => {
|
||||
fetchTemplatePreview(
|
||||
objectData._id,
|
||||
content,
|
||||
testObject,
|
||||
scale,
|
||||
(result) => {
|
||||
setReloadLoading(false)
|
||||
if (result?.error) {
|
||||
setPreviewError(true)
|
||||
setPreviewMessage(result.error)
|
||||
} else {
|
||||
setPreviewError(false)
|
||||
updatePreviewContent(result.html)
|
||||
setPreviewMessage('No issues found.')
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
[fetchTemplatePreview, objectData?._id]
|
||||
)
|
||||
|
||||
// Move useEffect to component level and use state to track objectData changes
|
||||
useEffect(() => {
|
||||
if (objectData) {
|
||||
console.log('PreviewScale', previewScale)
|
||||
reloadPreview(objectData.content, objectData.testObject, previewScale)
|
||||
}
|
||||
}, [objectData, previewScale, reloadPreview])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Splitter className={'farmcontrol-splitter'}>
|
||||
{collapseState.preview == true && (
|
||||
<Splitter.Panel style={{ height: '100%' }}>
|
||||
<Spin
|
||||
spinning={loading || reloadLoading}
|
||||
indicator={<LoadingOutlined />}
|
||||
<Card
|
||||
spinning={loading}
|
||||
style={style}
|
||||
styles={{ body: { height: '100%' } }}
|
||||
>
|
||||
<Card style={style} styles={{ body: { height: '100%' } }}>
|
||||
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||
<Flex gap={'small'}>
|
||||
{objectData?.objectType ? (
|
||||
<ObjectProperty
|
||||
objectType={objectData?.objectType}
|
||||
name={'testObject'}
|
||||
isEditing={true}
|
||||
objectData={objectData}
|
||||
disabled={!isEditing || objectData?.global}
|
||||
type={'object'}
|
||||
<TemplatePreview
|
||||
objectData={objectData?.testObject}
|
||||
documentTemplate={objectData}
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
style={style}
|
||||
onTestObjectOpen={() => setTestObjectOpen(true)}
|
||||
onPreviewMessage={handlePreviewMessage}
|
||||
showTestObject={true}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<Input disabled={true} />
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
icon={<InfoCircleIcon />}
|
||||
disabled={objectData?.global}
|
||||
onClick={() => {
|
||||
setTestObjectOpen(true)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<PlusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => prev + 0.05)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<MinusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => prev - 0.05)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
readOnly={true}
|
||||
style={{ width: '65px' }}
|
||||
onClick={() => {
|
||||
setPreviewScale(1)
|
||||
}}
|
||||
>
|
||||
{previewScale.toFixed(2)}x
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
srcDoc={previewContent}
|
||||
frameBorder='0'
|
||||
style={{
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
border: '1px solid #85858541',
|
||||
overflow: 'auto'
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Spin>
|
||||
</Splitter.Panel>
|
||||
)}
|
||||
{collapseState.editor == true && (
|
||||
|
||||
158
src/components/Dashboard/common/TemplatePreview.jsx
Normal file
158
src/components/Dashboard/common/TemplatePreview.jsx
Normal file
@ -0,0 +1,158 @@
|
||||
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Flex, Button, Input } from 'antd'
|
||||
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||
import ObjectProperty from '../common/ObjectProperty.jsx'
|
||||
import { ApiServerContext } from '../context/ApiServerContext.jsx'
|
||||
|
||||
const TemplatePreview = ({
|
||||
objectData,
|
||||
documentTemplate,
|
||||
loading,
|
||||
isEditing,
|
||||
onTestObjectOpen,
|
||||
onPreviewMessage,
|
||||
showTestObject = false
|
||||
}) => {
|
||||
const iframeRef = useRef(null)
|
||||
const { fetchTemplatePreview } = useContext(ApiServerContext)
|
||||
const [previewContent, setPreviewContent] = useState('')
|
||||
const [reloadLoading, setReloadLoading] = useState(false)
|
||||
const [previewScale, setPreviewScale] = useState(1)
|
||||
|
||||
const updatePreviewContent = (html) => {
|
||||
if (iframeRef.current) {
|
||||
// Save current scroll position
|
||||
const scrollY = iframeRef.current.contentWindow.scrollY
|
||||
const scrollX = iframeRef.current.contentWindow.scrollX
|
||||
|
||||
// Update srcDoc
|
||||
setPreviewContent(html)
|
||||
|
||||
// Restore scroll position after iframe loads new content
|
||||
const handleLoad = () => {
|
||||
iframeRef.current.contentWindow.scrollTo(scrollX, scrollY)
|
||||
iframeRef.current.removeEventListener('load', handleLoad)
|
||||
}
|
||||
iframeRef.current.addEventListener('load', handleLoad)
|
||||
}
|
||||
}
|
||||
|
||||
const reloadPreview = useCallback(
|
||||
(content, testObject = {}, scale = 1) => {
|
||||
if (!objectData?._id) {
|
||||
onPreviewMessage('No object data available for preview.', true)
|
||||
return
|
||||
}
|
||||
|
||||
setReloadLoading(true)
|
||||
fetchTemplatePreview(
|
||||
documentTemplate._id,
|
||||
content,
|
||||
testObject,
|
||||
scale,
|
||||
(result) => {
|
||||
setReloadLoading(false)
|
||||
if (result?.error) {
|
||||
// Handle error through parent component
|
||||
onPreviewMessage(result.error, true)
|
||||
} else {
|
||||
updatePreviewContent(result.html)
|
||||
onPreviewMessage('No issues found.', false)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
[fetchTemplatePreview, objectData?._id, onPreviewMessage]
|
||||
)
|
||||
|
||||
// Move useEffect to component level and use state to track objectData changes
|
||||
useEffect(() => {
|
||||
if (objectData && documentTemplate?.content) {
|
||||
console.log('PreviewScale', previewScale)
|
||||
reloadPreview(documentTemplate.content, objectData, previewScale)
|
||||
}
|
||||
}, [objectData, documentTemplate, previewScale, reloadPreview])
|
||||
|
||||
return (
|
||||
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||
<Flex gap={'small'}>
|
||||
{showTestObject == true ? (
|
||||
<>
|
||||
{documentTemplate?.objectType ? (
|
||||
<ObjectProperty
|
||||
objectType={documentTemplate?.objectType}
|
||||
name={'testObject'}
|
||||
isEditing={true}
|
||||
objectData={objectData}
|
||||
disabled={!isEditing || objectData?.global}
|
||||
type={'object'}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<Input disabled={true} />
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
icon={<InfoCircleIcon />}
|
||||
disabled={objectData?.global}
|
||||
onClick={() => {
|
||||
onTestObjectOpen()
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<Button
|
||||
icon={<PlusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => prev + 0.05)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<MinusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => prev - 0.05)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
readOnly={true}
|
||||
style={{ width: '65px' }}
|
||||
loading={loading || reloadLoading}
|
||||
disabled={loading || reloadLoading}
|
||||
onClick={() => {
|
||||
setPreviewScale(1)
|
||||
}}
|
||||
>
|
||||
{previewScale.toFixed(2)}x
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
srcDoc={previewContent}
|
||||
frameBorder='0'
|
||||
style={{
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
border: '1px solid #85858541',
|
||||
overflow: 'auto'
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
TemplatePreview.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
objectData: PropTypes.object,
|
||||
documentTemplate: PropTypes.object,
|
||||
isEditing: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
showTestObject: PropTypes.bool,
|
||||
onTestObjectOpen: PropTypes.func.isRequired,
|
||||
onPreviewMessage: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default TemplatePreview
|
||||
Loading…
x
Reference in New Issue
Block a user