diff --git a/assets/css/comments.css b/assets/css/comments.css index 73aaca2..4725280 100644 --- a/assets/css/comments.css +++ b/assets/css/comments.css @@ -925,11 +925,147 @@ background: none; } -/* 工具条 */ +/* 工具条:加粗 / 斜体 / 字体颜色 / 表情。 + * + * 位于输入框上方,与 textarea 同在 .comment-textarea 这个带边框的 + * 容器里 —— 工具条铺满容器顶部,输入框一聚焦整框(含工具条) + * 一起亮主色描边,视觉上是"一个编辑器",而不是两条孤立的横条。 + * 面板(色板 / 表情)从按钮**下方**弹出:按钮在容器顶部, + * 向上弹会飘到表单外面盖住上文,向下弹只盖住自己的输入区。 */ +.comment-toolbar { + display: flex; + align-items: center; + gap: 2px; + padding: 5px 9px; + border-bottom: 1px solid #edf0f4; + border-radius: var(--yq-radius, 4px) var(--yq-radius, 4px) 0 0; + background-color: #fafbfc; +} + +/* 面板的定位父级:每个带面板的按钮各包一层 */ +.comment-tool-group { + position: relative; + display: flex; + align-items: center; +} + +/* 按钮组之间的细分隔线 */ +.comment-toolbar-sep { + align-self: center; + width: 1px; + height: 16px; + margin: 0 5px; + background-color: #e2e7ee; +} + +.comment-tool-btn { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 30px; + height: 30px; + padding: 0 7px; + border: 0; + border-radius: var(--yq-radius, 4px); + background: none; + color: var(--yq-text-2, #4b5563); + font-size: 14px; + line-height: 1; + cursor: pointer; + transition: color 0.16s var(--yq-ease), background-color 0.16s var(--yq-ease); +} + +.comment-tool-btn:hover { + background-color: var(--yq-primary-soft, #eaf6ff); + color: var(--yq-primary-dark, #0089db); +} + +.comment-tool-btn:focus-visible { + outline: 2px solid var(--yq-primary, #00a2ff); + outline-offset: 1px; +} + +/* 字形:B 用粗体、I 用衬线斜体,一眼能认出对应排版效果 */ +.comment-tool-glyph { + display: inline-block; + font-family: inherit; + font-size: 14.5px; + line-height: 1; +} + +.comment-tool-glyph-bold { + font-weight: 700; +} + +.comment-tool-glyph-italic { + font-family: Georgia, "Times New Roman", serif; + font-style: italic; + font-weight: 600; +} + +/* 颜色按钮:字母 A 底下压一条渐变色条,点开是色板 */ +.comment-tool-glyph-color { + position: relative; + font-weight: 600; +} + +.comment-tool-glyph-color::after { + content: ""; + position: absolute; + left: 1px; + right: 1px; + bottom: -3px; + height: 3px; + border-radius: 2px; + background: linear-gradient(90deg, #e74c3c 0%, #f1c40f 30%, #2ecc71 55%, #00a2ff 80%, #9b59b6 100%); +} + +/* 色板:8 个圆点,从按钮下方弹出 */ +.comment-color-panel { + position: absolute; + top: calc(100% + 6px); + left: 0; + z-index: 21; + display: flex; + flex-wrap: wrap; + gap: 6px; + width: 164px; + padding: 10px; + border: 1px solid #e2e7ee; + border-radius: var(--yq-radius-lg, 6px); + background-color: #fff; + box-shadow: 0 6px 20px rgba(31, 35, 40, 0.12); +} + +.comment-color-panel[hidden] { + display: none; +} + +.comment-color-swatch { + width: 20px; + height: 20px; + padding: 0; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 50%; + cursor: pointer; + transition: transform 0.12s var(--yq-ease), box-shadow 0.12s var(--yq-ease); +} + +.comment-color-swatch:hover { + transform: scale(1.18); + box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px var(--yq-primary, #00a2ff); +} + +.comment-color-swatch:focus-visible { + outline: 2px solid var(--yq-primary, #00a2ff); + outline-offset: 1px; +} + +/* 底栏:表情移进工具条后只剩状态提示 + 提交按钮 */ .comment-bar { display: flex; align-items: center; - justify-content: space-between; + justify-content: flex-end; gap: 10px; padding: 8px 11px; border-top: 1px solid #edf0f4; @@ -937,27 +1073,18 @@ background-color: #fafbfc; } -.comment-bar-left, .comment-bar-right { display: flex; align-items: center; gap: 8px; } -.comment-bar-left { - position: relative; -} - +/* 表情按钮:并入工具条后尺寸与其它工具按钮一致(原 4px 8px 小药丸) */ .comment-smile-btn { - padding: 4px 8px; - border: 0; - border-radius: var(--yq-radius, 4px); - background: none; - color: var(--yq-text-3, #6b7280); - font-size: 17px; - line-height: 1; - cursor: pointer; - transition: color 0.16s var(--yq-ease), background-color 0.16s var(--yq-ease); + min-width: 30px; + height: 30px; + padding: 0 7px; + font-size: 16px; } .comment-smile-btn:hover { @@ -965,10 +1092,10 @@ color: var(--yq-primary-dark, #0089db); } -/* 表情面板:3 行 8 列 */ +/* 表情面板:按钮在容器顶部,改为**向下**弹出(原 bottom 定位作废) */ .comment-smile-panel { position: absolute; - bottom: calc(100% + 8px); + top: calc(100% + 6px); left: 0; z-index: 20; display: flex; diff --git a/assets/js/comments.js b/assets/js/comments.js index ce63cc7..4895de2 100644 --- a/assets/js/comments.js +++ b/assets/js/comments.js @@ -176,6 +176,76 @@ } }) + /* ── 富文本工具条(3.2.24)───────────────────────────────── + * 评论内容仍是纯文本存储;工具条只在光标/选区处插入受控标签: + * B → … I → … + * 颜色 → … + * 后端由 theme-comments.php 的 kses 白名单放行这三种标签 + * (style 属性再经 WP 核心 safecss_filter_attr 消毒,只留安全属性)。 + * + * 选区为空时插入占位文本并**选中**它 —— 用户直接打字就覆盖占位, + * 这是各家编辑器工具条的通行做法;不选中的话占位文本会留在正文里。 + */ + function wrapSelection (before, after, placeholder) { + if (!textarea) return + textarea.focus() + var s = textarea.selectionStart || 0 + var e = textarea.selectionEnd || 0 + var v = textarea.value + var sel = v.slice(s, e) + var usedPlaceholder = false + if (sel === '') { + sel = placeholder || '' + usedPlaceholder = true + } + textarea.value = v.slice(0, s) + before + sel + after + v.slice(e) + // 选中被包裹的内容(或占位文本),光标落回包裹体内 + var a = s + before.length + var b = a + sel.length + try { textarea.setSelectionRange(a, b) } catch (ex) { /* 老 IE 忽略 */ } + return usedPlaceholder + } + + // B / I:按钮上带 data-wrap-before / data-wrap-after,加新按钮不用改 JS + $$('.comment-tool-btn[data-wrap-before]', form).forEach(function (btn) { + btn.addEventListener('click', function () { + wrapSelection( + btn.getAttribute('data-wrap-before') || '', + btn.getAttribute('data-wrap-after') || '', + I18N.boldPlaceholder || '加粗文字' + ) + }) + }) + + /* 字体颜色面板:开合 + 色板点击包裹选区。 + * 与表情面板同款交互:点按钮开/关,点外面关,选中后关。 */ + var colorBtn = $('#yang-tool-color', form) + var colorPanel = $('#yang-color-panel', form) + if (colorBtn && colorPanel) { + colorBtn.addEventListener('click', function (ev) { + ev.stopPropagation() + var open = !colorPanel.hidden + colorPanel.hidden = open + colorBtn.setAttribute('aria-expanded', open ? 'false' : 'true') + }) + document.addEventListener('click', function (ev) { + if (colorPanel.hidden) return + if (colorPanel.contains(ev.target) || colorBtn.contains(ev.target)) return + colorPanel.hidden = true + colorBtn.setAttribute('aria-expanded', 'false') + }) + colorPanel.addEventListener('click', function (ev) { + var sw = ev.target.closest ? ev.target.closest('.comment-color-swatch') : null + if (!sw) return + var c = sw.getAttribute('data-color') || '#000000' + // 色值只允许 #rrggbb,面板里写死的,这里再挡一道 + if (!/^#[0-9a-fA-F]{6}$/.test(c)) return + wrapSelection('', '', I18N.colorPlaceholder || '彩色文字') + colorPanel.hidden = true + colorBtn.setAttribute('aria-expanded', 'false') + }) + } + /* ── 表情面板 ────────────────────────────────────────────── * 点表情按钮 → 把对应代码插到光标处(不是简单追加到末尾)。 * 旧实现是 `myField.value += tag`,光标本在中间也会跑到最后, diff --git a/comments.php b/comments.php index 35b574f..7d797c3 100644 --- a/comments.php +++ b/comments.php @@ -222,21 +222,92 @@ if ($yang_per_page <= 0) {