Commit 8c845e28 by Яков

update iframe and video

parent e1b05324
{ {
"name": "react-ag-qeditor", "name": "react-ag-qeditor",
"version": "1.1.24", "version": "1.1.25",
"description": "WYSIWYG html editor", "description": "WYSIWYG html editor",
"author": "atma", "author": "atma",
"license": "MIT", "license": "MIT",
......
...@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => { ...@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
return style return style
} }
const getOuterAlignStyle = (align) => {
return {
width: '100%',
display: 'block',
textAlign: align === 'center' ? 'center' : align === 'right' ? 'right' : 'left',
}
}
const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected }) => { const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected }) => {
const wrapperRef = useRef(null) const outerRef = useRef(null)
const innerRef = useRef(null)
const iframeRef = useRef(null) const iframeRef = useRef(null)
const [showAlignMenu, setShowAlignMenu] = useState(false) const [showAlignMenu, setShowAlignMenu] = useState(false)
...@@ -36,10 +45,9 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected ...@@ -36,10 +45,9 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
const paddingRight = parseFloat(editorStyles.paddingRight) || 0 const paddingRight = parseFloat(editorStyles.paddingRight) || 0
const availableEditorWidth = fullEditorWidth - paddingLeft - paddingRight const availableEditorWidth = fullEditorWidth - paddingLeft - paddingRight
// важно: стартуем от parentElement, чтобы не схватить wrapperRef.current // важно: стартуем от parentElement, чтобы не схватить innerRef.current
const startEl = wrapperRef.current?.parentElement || editorContent const startEl = outerRef.current?.parentElement || editorContent
const container = const container = startEl.closest('li, blockquote, td, p, div') || editorContent
startEl.closest('li, blockquote, td, p, div') || editorContent
const containerStyles = window.getComputedStyle(container) const containerStyles = window.getComputedStyle(container)
const containerPaddingLeft = parseFloat(containerStyles.paddingLeft) || 0 const containerPaddingLeft = parseFloat(containerStyles.paddingLeft) || 0
...@@ -49,7 +57,6 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected ...@@ -49,7 +57,6 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
return { width: containerWidth, availableSpace: availableEditorWidth } return { width: containerWidth, availableSpace: availableEditorWidth }
} }
const safeUpdateAttributes = (newAttrs) => { const safeUpdateAttributes = (newAttrs) => {
const { width: containerWidth, availableSpace } = getEditorDimensions() const { width: containerWidth, availableSpace } = getEditorDimensions()
...@@ -57,7 +64,8 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected ...@@ -57,7 +64,8 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
let height = newAttrs.height ?? node.attrs.height let height = newAttrs.height ?? node.attrs.height
if (typeof width === 'number' && typeof height === 'number') { if (typeof width === 'number' && typeof height === 'number') {
const maxWidth = node.attrs.align === 'center' ? containerWidth : availableSpace const maxWidth = (newAttrs.align ?? node.attrs.align) === 'center' ? containerWidth : availableSpace
if (width > maxWidth) { if (width > maxWidth) {
const ratio = maxWidth / width const ratio = maxWidth / width
width = maxWidth width = maxWidth
...@@ -117,14 +125,14 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected ...@@ -117,14 +125,14 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
console.warn('getPos() failed:', err) console.warn('getPos() failed:', err)
} }
const startWidth = node.attrs.width || iframeRef.current?.clientWidth || 560 const startWidth = node.attrs.width || innerRef.current?.clientWidth || 560
const startHeight = node.attrs.height || iframeRef.current?.clientHeight || 315 const startHeight = node.attrs.height || innerRef.current?.clientHeight || 315
const aspectRatio = startWidth / startHeight const aspectRatio = startWidth / startHeight
const startX = e.clientX const startX = e.clientX
const startY = e.clientY const startY = e.clientY
const { width: containerWidth, availableSpace } = getEditorDimensions() const { width: containerWidth, availableSpace } = getEditorDimensions()
const maxWidth = node.attrs.align === 'center' ? containerWidth : availableSpace const maxWidth = (node.attrs.align || 'left') === 'center' ? containerWidth : availableSpace
const onMouseMove = (ev) => { const onMouseMove = (ev) => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
...@@ -177,145 +185,139 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected ...@@ -177,145 +185,139 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
const align = node.attrs.align || 'left' const align = node.attrs.align || 'left'
const wrapperStyle = { const outerStyle = getOuterAlignStyle(align)
const innerStyle = {
position: 'relative', position: 'relative',
display: 'inline-block',
width: node.attrs.width ? `${node.attrs.width}px` : undefined, width: node.attrs.width ? `${node.attrs.width}px` : undefined,
height: node.attrs.height ? `${node.attrs.height}px` : undefined, height: node.attrs.height ? `${node.attrs.height}px` : undefined,
// align styles inside editor
display: align === 'center' ? 'block' : 'inline-block',
marginLeft: align === 'center' ? 'auto' : undefined,
marginRight: align === 'center' ? 'auto' : undefined,
float: align === 'left' ? 'left' : align === 'right' ? 'right' : 'none',
// чтобы текст красиво обтекал
marginInlineEnd: align === 'left' ? '1rem' : undefined,
marginInlineStart: align === 'right' ? '1rem' : undefined,
} }
return ( return (
<NodeViewWrapper <NodeViewWrapper
ref={wrapperRef} ref={outerRef}
as="div" as="div"
className="atma-iframe-wrapper" className="atma-iframe-wrapper"
style={wrapperStyle} style={outerStyle}
data-align={node.attrs.align || 'left'} data-align={align}
> >
<iframe <div ref={innerRef} style={innerStyle}>
ref={iframeRef} <iframe
src={node.attrs.src} ref={iframeRef}
frameBorder={node.attrs.frameborder ?? 0} src={node.attrs.src}
allowFullScreen frameBorder={node.attrs.frameborder ?? 0}
allow="fullscreen" allowFullScreen
style={{ allow="fullscreen"
width: '100%', style={{
height: '100%', width: '100%',
pointerEvents: editor.isEditable ? 'none' : 'auto', height: '100%',
}} pointerEvents: editor.isEditable ? 'none' : 'auto',
/> }}
/>
{(selected || isResizing) && (
<Fragment> {(selected || isResizing) && (
<button <Fragment>
type="button" <button
onClick={deleteNode} type="button"
style={{ onClick={deleteNode}
position: 'absolute',
top: 4,
right: 4,
zIndex: 30,
backgroundColor: 'white',
border: '1px solid #d9d9d9',
borderRadius: '50%',
width: 20,
height: 20,
fontSize: 12,
lineHeight: 1,
padding: '0px 0px 2px 0px',
cursor: 'pointer',
}}
>
×
</button>
{['nw', 'ne', 'sw', 'se'].map((d) => (
<div
key={d}
onMouseDown={handleResizeStart(d)}
style={{ style={{
position: 'absolute', position: 'absolute',
width: 12, top: 4,
height: 12, right: 4,
backgroundColor: BORDER_COLOR, zIndex: 30,
border: '1px solid white', backgroundColor: 'white',
[d[0] === 'n' ? 'top' : 'bottom']: -6, border: '1px solid #d9d9d9',
[d[1] === 'w' ? 'left' : 'right']: -6, borderRadius: '50%',
cursor: `${d}-resize`, width: 20,
zIndex: 10, height: 20,
fontSize: 12,
lineHeight: 1,
padding: '0px 0px 2px 0px',
cursor: 'pointer',
}}
>
×
</button>
{['nw', 'ne', 'sw', 'se'].map((d) => (
<div
key={d}
onMouseDown={handleResizeStart(d)}
style={{
position: 'absolute',
width: 12,
height: 12,
backgroundColor: BORDER_COLOR,
border: '1px solid white',
[d[0] === 'n' ? 'top' : 'bottom']: -6,
[d[1] === 'w' ? 'left' : 'right']: -6,
cursor: `${d}-resize`,
zIndex: 10,
}}
/>
))}
{showAlignMenu && (
<div
style={{
position: 'absolute',
top: -40,
left: '50%',
transform: 'translateX(-50%)',
backgroundColor: 'white',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
borderRadius: 4,
padding: 4,
zIndex: 20,
display: 'flex',
}}
>
{ALIGN_OPTIONS.map((a) => (
<button
type="button"
key={a}
onClick={() => handleAlign(a)}
style={{
margin: '0 2px',
padding: '10px 8px',
background: align === a ? '#e6f7ff' : 'transparent',
border: '1px solid #d9d9d9',
borderRadius: 2,
cursor: 'pointer',
}}
>
{a}
</button>
))}
</div>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation()
setShowAlignMenu((v) => !v)
}} }}
/>
))}
{showAlignMenu && (
<div
style={{ style={{
position: 'absolute', position: 'absolute',
top: -40, top: -30,
left: '50%', left: 'calc(50% - 6px)',
transform: 'translateX(-50%)', transform: 'translateX(-50%)',
backgroundColor: 'white', backgroundColor: 'white',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)', border: `1px solid ${BORDER_COLOR}`,
borderRadius: 4, borderRadius: 4,
padding: 4, padding: '8px 8px',
zIndex: 20, cursor: 'pointer',
display: 'flex', fontSize: 12,
zIndex: 10,
}} }}
> >
{ALIGN_OPTIONS.map((align) => ( Align
<button </button>
type="button" </Fragment>
key={align} )}
onClick={() => handleAlign(align)} </div>
style={{
margin: '0 2px',
padding: '10px 8px',
background: node.attrs.align === align ? '#e6f7ff' : 'transparent',
border: '1px solid #d9d9d9',
borderRadius: 2,
cursor: 'pointer',
}}
>
{align}
</button>
))}
</div>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation()
setShowAlignMenu((v) => !v)
}}
style={{
position: 'absolute',
top: -30,
left: 'calc(50% - 6px)',
transform: 'translateX(-50%)',
backgroundColor: 'white',
border: `1px solid ${BORDER_COLOR}`,
borderRadius: 4,
padding: '8px 8px',
cursor: 'pointer',
fontSize: 12,
zIndex: 10,
}}
>
Align
</button>
</Fragment>
)}
</NodeViewWrapper> </NodeViewWrapper>
) )
} }
......
...@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => { ...@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
return style return style
} }
const getOuterAlignStyle = (align) => {
return {
width: '100%',
display: 'block',
textAlign: align === 'center' ? 'center' : align === 'right' ? 'right' : 'left',
}
}
const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }) => { const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }) => {
const wrapperRef = useRef(null) const outerRef = useRef(null)
const innerRef = useRef(null)
const videoRef = useRef(null) const videoRef = useRef(null)
const [showAlignMenu, setShowAlignMenu] = useState(false) const [showAlignMenu, setShowAlignMenu] = useState(false)
...@@ -36,7 +45,9 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected } ...@@ -36,7 +45,9 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
const paddingRight = parseFloat(editorStyles.paddingRight) || 0 const paddingRight = parseFloat(editorStyles.paddingRight) || 0
const availableEditorWidth = fullEditorWidth - paddingLeft - paddingRight const availableEditorWidth = fullEditorWidth - paddingLeft - paddingRight
const container = wrapperRef.current?.closest('li, blockquote, td, p, div') || editorContent const startEl = outerRef.current?.parentElement || editorContent
const container = startEl.closest('li, blockquote, td, p, div') || editorContent
const containerStyles = window.getComputedStyle(container) const containerStyles = window.getComputedStyle(container)
const containerPaddingLeft = parseFloat(containerStyles.paddingLeft) || 0 const containerPaddingLeft = parseFloat(containerStyles.paddingLeft) || 0
const containerPaddingRight = parseFloat(containerStyles.paddingRight) || 0 const containerPaddingRight = parseFloat(containerStyles.paddingRight) || 0
...@@ -52,7 +63,8 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected } ...@@ -52,7 +63,8 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
let height = newAttrs.height ?? node.attrs.height let height = newAttrs.height ?? node.attrs.height
if (typeof width === 'number' && typeof height === 'number') { if (typeof width === 'number' && typeof height === 'number') {
const maxWidth = node.attrs.align === 'center' ? containerWidth : availableSpace const maxWidth = (newAttrs.align ?? node.attrs.align) === 'center' ? containerWidth : availableSpace
if (width > maxWidth) { if (width > maxWidth) {
const ratio = maxWidth / width const ratio = maxWidth / width
width = maxWidth width = maxWidth
...@@ -112,14 +124,14 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected } ...@@ -112,14 +124,14 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
console.warn('getPos() failed:', err) console.warn('getPos() failed:', err)
} }
const startWidth = node.attrs.width || videoRef.current?.clientWidth || 640 const startWidth = node.attrs.width || innerRef.current?.clientWidth || 640
const startHeight = node.attrs.height || videoRef.current?.clientHeight || 360 const startHeight = node.attrs.height || innerRef.current?.clientHeight || 360
const aspectRatio = startWidth / startHeight const aspectRatio = startWidth / startHeight
const startX = e.clientX const startX = e.clientX
const startY = e.clientY const startY = e.clientY
const { width: containerWidth, availableSpace } = getEditorDimensions() const { width: containerWidth, availableSpace } = getEditorDimensions()
const maxWidth = node.attrs.align === 'center' ? containerWidth : availableSpace const maxWidth = (node.attrs.align || 'left') === 'center' ? containerWidth : availableSpace
const onMouseMove = (ev) => { const onMouseMove = (ev) => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
...@@ -170,134 +182,140 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected } ...@@ -170,134 +182,140 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
} }
} }
const wrapperStyle = { const align = node.attrs.align || 'left'
const outerStyle = getOuterAlignStyle(align)
const innerStyle = {
position: 'relative', position: 'relative',
display: node.attrs.align === 'center' ? 'block' : 'inline-block', display: 'inline-block',
width: node.attrs.width ? `${node.attrs.width}px` : undefined, width: node.attrs.width ? `${node.attrs.width}px` : undefined,
height: node.attrs.height ? `${node.attrs.height}px` : undefined, height: node.attrs.height ? `${node.attrs.height}px` : undefined,
} }
return ( return (
<NodeViewWrapper <NodeViewWrapper
ref={wrapperRef} ref={outerRef}
as="div" as="div"
className="atma-video-wrapper" className="atma-video-wrapper"
style={wrapperStyle} style={outerStyle}
data-align={node.attrs.align || 'left'} data-align={align}
> >
<video <div ref={innerRef} style={innerStyle}>
ref={videoRef} <video
src={node.attrs.src} ref={videoRef}
poster={node.attrs.poster} src={node.attrs.src}
controls={node.attrs.controls !== false} poster={node.attrs.poster}
style={{ controls={node.attrs.controls !== false}
width: '100%', style={{
height: '100%', width: '100%',
pointerEvents: editor.isEditable ? 'none' : 'auto', height: '100%',
}} pointerEvents: editor.isEditable ? 'none' : 'auto',
/> }}
/>
{(selected || isResizing) && (
<Fragment> {(selected || isResizing) && (
<button <Fragment>
type="button" <button
onClick={deleteNode} type="button"
style={{ onClick={deleteNode}
position: 'absolute',
top: 4,
right: 4,
zIndex: 30,
backgroundColor: 'white',
border: '1px solid #d9d9d9',
borderRadius: '50%',
width: 20,
height: 20,
fontSize: 12,
lineHeight: 1,
padding: '0px 0px 2px 0px',
cursor: 'pointer',
}}
>
×
</button>
{['nw', 'ne', 'sw', 'se'].map((d) => (
<div
key={d}
onMouseDown={handleResizeStart(d)}
style={{ style={{
position: 'absolute', position: 'absolute',
width: 12, top: 4,
height: 12, right: 4,
backgroundColor: BORDER_COLOR, zIndex: 30,
border: '1px solid white', backgroundColor: 'white',
[d[0] === 'n' ? 'top' : 'bottom']: -6, border: '1px solid #d9d9d9',
[d[1] === 'w' ? 'left' : 'right']: -6, borderRadius: '50%',
cursor: `${d}-resize`, width: 20,
zIndex: 10, height: 20,
fontSize: 12,
lineHeight: 1,
padding: '0px 0px 2px 0px',
cursor: 'pointer',
}}
>
×
</button>
{['nw', 'ne', 'sw', 'se'].map((d) => (
<div
key={d}
onMouseDown={handleResizeStart(d)}
style={{
position: 'absolute',
width: 12,
height: 12,
backgroundColor: BORDER_COLOR,
border: '1px solid white',
[d[0] === 'n' ? 'top' : 'bottom']: -6,
[d[1] === 'w' ? 'left' : 'right']: -6,
cursor: `${d}-resize`,
zIndex: 10,
}}
/>
))}
{showAlignMenu && (
<div
style={{
position: 'absolute',
top: -40,
left: '50%',
transform: 'translateX(-50%)',
backgroundColor: 'white',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
borderRadius: 4,
padding: 4,
zIndex: 20,
display: 'flex',
}}
>
{ALIGN_OPTIONS.map((a) => (
<button
type="button"
key={a}
onClick={() => handleAlign(a)}
style={{
margin: '0 2px',
padding: '10px 8px',
background: align === a ? '#e6f7ff' : 'transparent',
border: '1px solid #d9d9d9',
borderRadius: 2,
cursor: 'pointer',
}}
>
{a}
</button>
))}
</div>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation()
setShowAlignMenu((v) => !v)
}} }}
/>
))}
{showAlignMenu && (
<div
style={{ style={{
position: 'absolute', position: 'absolute',
top: -40, top: -30,
left: '50%', left: 'calc(50% - 6px)',
transform: 'translateX(-50%)', transform: 'translateX(-50%)',
backgroundColor: 'white', backgroundColor: 'white',
boxShadow: '0 2px 8px rgba(0,0,0,0.15)', border: `1px solid ${BORDER_COLOR}`,
borderRadius: 4, borderRadius: 4,
padding: 4, padding: '8px 8px',
zIndex: 20, cursor: 'pointer',
display: 'flex', fontSize: 12,
zIndex: 10,
}} }}
> >
{ALIGN_OPTIONS.map((align) => ( Align
<button </button>
type="button" </Fragment>
key={align} )}
onClick={() => handleAlign(align)} </div>
style={{
margin: '0 2px',
padding: '10px 8px',
background: node.attrs.align === align ? '#e6f7ff' : 'transparent',
border: '1px solid #d9d9d9',
borderRadius: 2,
cursor: 'pointer',
}}
>
{align}
</button>
))}
</div>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation()
setShowAlignMenu((v) => !v)
}}
style={{
position: 'absolute',
top: -30,
left: 'calc(50% - 6px)',
transform: 'translateX(-50%)',
backgroundColor: 'white',
border: `1px solid ${BORDER_COLOR}`,
borderRadius: 4,
padding: '8px 8px',
cursor: 'pointer',
fontSize: 12,
zIndex: 10,
}}
>
Align
</button>
</Fragment>
)}
</NodeViewWrapper> </NodeViewWrapper>
) )
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment