Add Pan Icon and Pan Filled Icon Components for Template Preview
- Introduced new PanIcon and PanFilledIcon components to enhance the TemplatePreview functionality. - Implemented panning feature in TemplatePreview, allowing users to navigate through content more intuitively. - Updated TemplatePreview to toggle panning mode with visual feedback using the new icons. - Enhanced user experience by managing pointer events and cursor styles during panning.
This commit is contained in:
parent
fede8af6aa
commit
53d488011b
7
assets/icons/panfilledicon.svg
Normal file
7
assets/icons/panfilledicon.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.935415,0,0,0.935415,2.292349,1.999999)">
|
||||
<path d="M23.559,56.897C33.746,67.068 47.027,66.068 56.324,56.787C62.652,50.459 64.606,43.662 62.965,35.615C62.027,29.131 57.996,21.49 55.684,17.1C54.012,13.865 51.918,9.693 50.762,8.178C49.465,6.443 47.402,6.068 45.73,7.318C43.809,8.709 43.543,10.803 44.496,13.521L48.309,23.537C48.621,24.365 48.574,24.818 48.262,25.131C47.934,25.443 47.48,25.521 46.84,24.881L23.199,1.209C21.559,-0.4 19.137,-0.4 17.527,1.193C15.934,2.803 15.934,5.256 17.543,6.865L35.215,24.537C34.512,24.896 33.809,25.271 33.09,25.74L12.824,5.475C11.199,3.834 8.777,3.834 7.168,5.475C5.559,7.084 5.559,9.521 7.168,11.131L27.199,31.162C26.684,31.787 26.152,32.443 25.684,33.084L7.246,14.631C5.637,13.037 3.199,13.021 1.574,14.615C-0.004,16.225 -0.004,18.678 1.59,20.287L21.574,40.256C21.23,41.131 20.996,41.975 20.762,42.803L6.84,28.881C5.246,27.24 2.809,27.24 1.184,28.834C-0.395,30.475 -0.395,32.912 1.184,34.537L23.559,56.897Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
7
assets/icons/panicon.svg
Normal file
7
assets/icons/panicon.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.89584,0,0,0.89584,2.323959,1.951688)">
|
||||
<path d="M58.333,59.078C67.13,50.281 68.599,39.468 62.739,27.046L55.505,11.796C54.911,10.546 54.208,9.531 53.38,8.734C50.583,5.921 46.38,5.843 43.489,8.718C41.864,10.343 41.536,12.781 42.083,15.265L43.192,17.953C43.224,18.062 43.224,18.156 43.177,18.234C43.067,18.343 42.942,18.281 42.864,18.203L27.052,2.39C23.927,-0.766 19.692,-0.704 16.552,2.453C15.802,3.171 15.333,4.078 15.099,5.046C12.271,3.484 9.239,3.953 6.817,6.359C4.521,8.64 4.083,11.765 5.521,14.546C4.63,14.718 3.817,15.171 3.161,15.828C0.239,18.734 0.224,22.812 3.192,25.765L4.536,27.125C3.692,27.265 2.833,27.687 2.146,28.359C-0.776,31.312 -0.714,35.593 2.333,38.593L22.224,58.515C33.458,69.765 47.614,69.781 58.333,59.078ZM53.302,54.328C45.224,62.406 35.177,62.281 26.411,53.484L7.505,34.562C6.833,33.89 6.802,32.984 7.458,32.343C8.099,31.703 9.036,31.687 9.724,32.359L21.239,43.874C22.395,45.031 24.192,44.968 25.302,43.859C26.427,42.734 26.505,40.937 25.349,39.765L8.005,22.406C7.317,21.734 7.286,20.843 7.958,20.218C8.567,19.546 9.505,19.562 10.192,20.218L26.349,36.39C27.505,37.546 29.317,37.468 30.442,36.359C31.536,35.234 31.614,33.406 30.474,32.265L11.411,13.218C10.724,12.546 10.724,11.624 11.364,10.968C11.989,10.343 12.927,10.328 13.599,10.999L31.974,29.39C33.114,30.5 34.911,30.468 36.036,29.343C37.177,28.25 37.224,26.39 36.083,25.265L20.599,9.781C19.942,9.109 19.927,8.234 20.567,7.578C21.208,6.921 22.13,6.921 22.786,7.593L45.27,30.031C46.802,31.593 48.755,31.39 49.974,30.109C50.989,29.078 51.474,27.562 50.77,25.75L46.802,15.374C46.38,14.343 46.817,13.531 47.567,13.187C48.411,12.828 49.208,13.187 49.677,14.187L56.864,29.843C61.536,40.093 60.067,47.546 53.302,54.328Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@ -11,6 +11,8 @@ import { Flex, Button, Input, Select } from 'antd'
|
||||
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||
import PanIcon from '../../Icons/PanIcon.jsx'
|
||||
import PanFilledIcon from '../../Icons/PanFilledIcon.jsx'
|
||||
import ObjectProperty from '../common/ObjectProperty.jsx'
|
||||
import ScrollBox from './ScrollBox.jsx'
|
||||
import { ApiServerContext } from '../context/ApiServerContext.jsx'
|
||||
@ -39,6 +41,9 @@ const TemplatePreview = ({
|
||||
const [reloadLoading, setReloadLoading] = useState(false)
|
||||
const [previewScale, setPreviewScale] = useState(1)
|
||||
const [previewType, setPreviewType] = useState('HTML')
|
||||
const [panMode, setPanMode] = useState(false)
|
||||
const [isPanning, setIsPanning] = useState(false)
|
||||
const panStartRef = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 })
|
||||
const [iframeSize, setIframeSize] = useState({
|
||||
width: '100%',
|
||||
height: 'auto'
|
||||
@ -236,6 +241,74 @@ const TemplatePreview = ({
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!panMode) {
|
||||
setIsPanning(false)
|
||||
}
|
||||
}, [panMode])
|
||||
|
||||
useEffect(() => {
|
||||
const scrollEl = scrollElementRef.current
|
||||
if (!panMode || !scrollEl || previewType != 'HTML') {
|
||||
return
|
||||
}
|
||||
|
||||
const handlePointerDown = (event) => {
|
||||
if (event.pointerType === 'mouse' && event.button !== 0) {
|
||||
return
|
||||
}
|
||||
event.preventDefault()
|
||||
panStartRef.current = {
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
scrollLeft: scrollEl.scrollLeft,
|
||||
scrollTop: scrollEl.scrollTop
|
||||
}
|
||||
setIsPanning(true)
|
||||
}
|
||||
|
||||
scrollEl.addEventListener('pointerdown', handlePointerDown)
|
||||
return () => {
|
||||
scrollEl.removeEventListener('pointerdown', handlePointerDown)
|
||||
}
|
||||
}, [panMode, previewType])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPanning) {
|
||||
return
|
||||
}
|
||||
|
||||
const previousCursor = document.body.style.cursor
|
||||
const previousUserSelect = document.body.style.userSelect
|
||||
document.body.style.cursor = 'grabbing'
|
||||
document.body.style.userSelect = 'none'
|
||||
|
||||
const handlePointerMove = (event) => {
|
||||
const scrollEl = scrollElementRef.current
|
||||
if (!scrollEl) {
|
||||
return
|
||||
}
|
||||
const { x, y, scrollLeft, scrollTop } = panStartRef.current
|
||||
scrollEl.scrollLeft = scrollLeft - (event.clientX - x)
|
||||
scrollEl.scrollTop = scrollTop - (event.clientY - y)
|
||||
}
|
||||
|
||||
const handlePointerUp = () => {
|
||||
setIsPanning(false)
|
||||
}
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove)
|
||||
window.addEventListener('pointerup', handlePointerUp)
|
||||
window.addEventListener('pointercancel', handlePointerUp)
|
||||
return () => {
|
||||
document.body.style.cursor = previousCursor
|
||||
document.body.style.userSelect = previousUserSelect
|
||||
window.removeEventListener('pointermove', handlePointerMove)
|
||||
window.removeEventListener('pointerup', handlePointerUp)
|
||||
window.removeEventListener('pointercancel', handlePointerUp)
|
||||
}
|
||||
}, [isPanning])
|
||||
|
||||
return (
|
||||
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||
<Flex gap={'small'}>
|
||||
@ -266,6 +339,19 @@ const TemplatePreview = ({
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
icon={
|
||||
panMode ? (
|
||||
<PanFilledIcon style={{ color: 'var(--color-primary)' }} />
|
||||
) : (
|
||||
<PanIcon />
|
||||
)
|
||||
}
|
||||
disabled={loading || reloadLoading || previewType == 'PDF'}
|
||||
onClick={() => {
|
||||
setPanMode((prev) => !prev)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<PlusIcon />}
|
||||
onClick={() => {
|
||||
@ -314,11 +400,22 @@ const TemplatePreview = ({
|
||||
}}
|
||||
>
|
||||
{previewType == 'HTML' ? (
|
||||
<ScrollBox scrollableNodeProps={{ ref: scrollElementRef }}>
|
||||
<ScrollBox
|
||||
scrollableNodeProps={{ ref: scrollElementRef }}
|
||||
style={{
|
||||
cursor: panMode ? (isPanning ? 'grabbing' : 'grab') : undefined,
|
||||
touchAction: panMode ? 'none' : undefined
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: iframeSize.width,
|
||||
height: iframeSize.height
|
||||
height: iframeSize.height,
|
||||
cursor: panMode
|
||||
? isPanning
|
||||
? 'grabbing'
|
||||
: 'grab'
|
||||
: undefined
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
@ -332,7 +429,8 @@ const TemplatePreview = ({
|
||||
height: iframeSize.height === 'auto' ? 150 : '100%',
|
||||
display: 'block',
|
||||
border: 0,
|
||||
overflow: 'hidden'
|
||||
overflow: 'hidden',
|
||||
pointerEvents: panMode ? 'none' : undefined
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
8
src/components/Icons/PanFilledIcon.jsx
Normal file
8
src/components/Icons/PanFilledIcon.jsx
Normal file
@ -0,0 +1,8 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/panfilledicon.svg?react'
|
||||
|
||||
const PanFilledIcon = (props) => (
|
||||
<Icon component={CustomIconSvg} {...props} />
|
||||
)
|
||||
|
||||
export default PanFilledIcon
|
||||
6
src/components/Icons/PanIcon.jsx
Normal file
6
src/components/Icons/PanIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/panicon.svg?react'
|
||||
|
||||
const PanIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default PanIcon
|
||||
Loading…
x
Reference in New Issue
Block a user