fix(submit): show body placeholder on first load and restore caret on blank clicks

两个前台投稿编辑器的缺陷,根因都在占位符的实现方式:

1. 首次加载不显示占位符,输入一个再删掉才出现。
   旧实现往正文插了一个 data-mce-bogus 占位段落,而 TinyMCE 在
   setContent(studio 初始化时必调)与清理 bogus 元素时会把它移除。

2. 光标在块级结构里时,点击正文空白处无法落回正文。
   占位段落占着首行、且 pointer-events:none,点击落不到可编辑元素上;
   正文以表格 / 代码块这类结构块结尾时也没有可承载光标的段落。

改为 front-submit-editor-basics.js(替换 front-submit-placeholder.js):
- 占位符改成 body 属性 + content CSS 的 ::before,正文 DOM 不含占位节点;
- 点击空白处(事件目标是 body)且光标不在普通文本里时,补一个可编辑尾段
  并把光标放进去;
- 两份 content CSS 让 html/body 撑满编辑区,studio 的 syncEditorHeight
  以编辑器声明高度为下限,空正文时 iframe 不会缩成一行。

新增 tests/widget-system/submit-placeholder-behavior.test.cjs(jsdom 跑真实插件)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-09 20:38:02 +08:00
1 parent c2733017fd
commit 5afa7ab9df
10 files changed
+428 -172

No files matched your search

@@ -22,14 +22,33 @@ p { margin: 0 0 1.1em; }
/*
* 正文占位符:TinyMCE 4.9 没有内置 placeholder 插件,由主题插件
* (front-submit-placeholder.js)在正文为空时插入这个不可编辑的段落。
* (front-submit-editor-basics.js)在正文为空时给 body 打 data-jio-empty,
* 这里用 ::before 渲染提示。不插 DOM 节点,避免被 setContent 清掉、
* 也避免占住首行挡住点击。
*/
.jio-editor-placeholder {
body[data-jio-empty="1"]::before {
position: absolute;
top: 0;
left: 0;
color: #b9bfbc;
cursor: text;
content: attr(data-jio-placeholder);
pointer-events: none;
user-select: none;
}
/*
* body 撑满编辑区:内容很短时点击正文下方的空白区域也应落光标。
* iframe 高度由 syncEditorHeight() 跟随内容,这里保证 body 至少铺满。
*/
html,
body {
min-height: 100%;
}
body {
box-sizing: border-box;
}
h1, h2, h3, h4, h5, h6 { margin: 1.5em 0 0.65em; line-height: 1.4; }
img, iframe { max-width: 100%; }
img { height: auto; }
+6 -1
View File
@@ -87,6 +87,10 @@ export function removeAdjacentBlock(editor: any, event: any): void {
*
* TinyMCE 的 iframe 不会自动撑高;这里测量内容高度并同步容器,
* 页面整体滚动由外层工作台负责。
*
* 下限取编辑器初始化时声明的高度(studio 为 520),这样正文很短时 iframe
* 也保持一个完整写作区,空白处由 content CSS 的 body min-height 撑满,
* 点击正文下方仍能落光标。
*/
export function syncEditorHeight(editor: any): void {
if (!editor || editor.removed) {
@@ -97,8 +101,9 @@ export function syncEditorHeight(editor: any): void {
if (!doc || !iframe) {
return;
}
const floor = Math.max(320, Number(editor.getParam?.('height', 0)) || 0);
const height = Math.max(
320,
floor,
doc.documentElement.scrollHeight,
doc.body ? doc.body.scrollHeight : 0
);
+3 -3
View File
@@ -376,9 +376,9 @@ function Studio(): JSX.Element {
toolbar2: '',
toolbar3: '',
plugins: 'paste,link,lists,wordpress,wplink',
// WordPress 打包的 TinyMCE 没有 placeholder 插件,占位符由主题插件实现。
external_plugins: bootstrap.creator?.placeholderPlugin
? { jio_placeholder: bootstrap.creator.placeholderPlugin }
// 占位符与空白处落光标由主题插件实现(TinyMCE 4.9 没有 placeholder 插件)。
external_plugins: bootstrap.creator?.editorBasicsPlugin
? { jio_editor_basics: bootstrap.creator.editorBasicsPlugin }
: undefined,
wpautop: true,
body_class: 'jio-creator__tinymce',