fix(submit): keep the editor iframe tall enough for restored drafts and media
The iframe height is synced explicitly, and two paths were missing it, so long content ended up clipped inside the editor's own scroll area. - restoring a local draft calls setContent with a whole article at once; the iframe stayed at 520px while the body grew to 1410px, so only the first few paragraphs appeared to survive the restore - media inserts called insertContent directly without resyncing, and images load asynchronously: the height was measured while the image was still 0px tall, leaving the paragraphs below it cut off (measured: iframe 946 vs body 1006 with a single image after long text) Fixes - resync the height right after the restore's setContent, and add SetContent to the events that already sync height and content - resync after both media callbacks (picker and embed) - add installHeightWatchers(): capture-phase load listener inside the iframe for img / iframe / video / audio, plus a ResizeObserver on the body, so the height follows assets that finish loading later Tests cover the restore path and both media paths, including async loading.
This commit is contained in:
1 parent
a5386f72c6
commit
49f1212e33
3 files changed
+83
-3
No files matched your search
@@ -118,6 +118,39 @@ export function syncEditorHeight(editor: any): void {
|
||||
iframe.setAttribute('scrolling', 'no');
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片 / 视频落地是异步的:插入那一刻高度按 0 计算,资源加载完成后正文才真正
|
||||
* 变高。这里在 iframe 内监听资源加载与尺寸变化,加载完成后重新同步一次,
|
||||
* 否则图片下方的段落会被 iframe 的固定高度裁掉。
|
||||
*
|
||||
* 只在 body 上挂一次(installBlockActions 同一条 init 路径),避免重复监听。
|
||||
*/
|
||||
export function installHeightWatchers(editor: any): void {
|
||||
const doc = editor?.getDoc?.();
|
||||
if (!doc || !doc.body || (doc.body as any).__jioHeightWatched) {
|
||||
return;
|
||||
}
|
||||
(doc.body as any).__jioHeightWatched = true;
|
||||
|
||||
// load 不冒泡,但捕获阶段能收到 img / iframe / video 的加载完成。
|
||||
doc.body.addEventListener(
|
||||
'load',
|
||||
(event: Event) => {
|
||||
const target = event.target as HTMLElement | null;
|
||||
const tag = target?.tagName;
|
||||
if (tag === 'IMG' || tag === 'IFRAME' || tag === 'VIDEO' || tag === 'AUDIO') {
|
||||
syncEditorHeight(editor);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// 图片 CSS 生效后的尺寸变化、以及响应式重排,都由 ResizeObserver 兜底。
|
||||
if (typeof (window as any).ResizeObserver === 'function') {
|
||||
new (window as any).ResizeObserver(() => syncEditorHeight(editor)).observe(doc.body);
|
||||
}
|
||||
}
|
||||
|
||||
/** 块选中状态写在块自身的 class 上,跨 iframe 重排也能保留。 */
|
||||
const SELECTED_CLASS = 'jio-creator__block-selected';
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import { BlockDialog, type BlockDialogKind, type BlockDialogResult } from './com
|
||||
import { type Draft, type Limits, initialDraft } from './draft';
|
||||
import {
|
||||
installBlockActions,
|
||||
installHeightWatchers,
|
||||
removeAdjacentBlock,
|
||||
syncEditorHeight,
|
||||
} from './editor-runtime';
|
||||
@@ -445,6 +446,7 @@ function Studio(): JSX.Element {
|
||||
}
|
||||
syncEditorHeight(editor);
|
||||
installBlockActions(editor);
|
||||
installHeightWatchers(editor);
|
||||
updateHistoryAvailability(editor);
|
||||
updateFormatValue(editor);
|
||||
updateBlockValue(editor);
|
||||
@@ -453,7 +455,7 @@ function Studio(): JSX.Element {
|
||||
updateFormatValue(editor);
|
||||
updateBlockValue(editor);
|
||||
});
|
||||
editor.on('input change Undo Redo AddUndo', () => {
|
||||
editor.on('input change Undo Redo AddUndo SetContent', () => {
|
||||
updateContent(serializeBlocks(editor.getContent()));
|
||||
updateHistoryAvailability(editor);
|
||||
syncEditorHeight(editor);
|
||||
@@ -492,7 +494,13 @@ function Studio(): JSX.Element {
|
||||
// 快照存的是保存格式(pre > code),编辑器要可编辑的形态。
|
||||
const content = hydrateBlocks(parsed.content);
|
||||
if (editorReadyRef.current) {
|
||||
editorRef.current?.setContent(content);
|
||||
const editor = editorRef.current;
|
||||
editor?.setContent(content);
|
||||
// setContent 不在 height 监听的事件列表里:正文一口气变长后
|
||||
// iframe 会停在原来的高度,恢复出来的长文只剩开头几段可见。
|
||||
if (editor && !editor.removed) {
|
||||
syncEditorHeight(editor);
|
||||
}
|
||||
} else {
|
||||
pendingRestoreContentRef.current = content;
|
||||
}
|
||||
@@ -752,6 +760,9 @@ function Studio(): JSX.Element {
|
||||
target.nodeChanged();
|
||||
updateContent(serializeBlocks(target.getContent()));
|
||||
updateHistoryAvailability(target);
|
||||
// 图片/视频按加载后的尺寸撑高正文;插入瞬间先同步一次,
|
||||
// 资源落地后由 installHeightWatchers 的 load 监听再补一次。
|
||||
syncEditorHeight(target);
|
||||
}
|
||||
},
|
||||
onEmbed: (html: string) => {
|
||||
@@ -761,6 +772,7 @@ function Studio(): JSX.Element {
|
||||
target.nodeChanged();
|
||||
updateContent(serializeBlocks(target.getContent()));
|
||||
updateHistoryAvailability(target);
|
||||
syncEditorHeight(target);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user