From 20dd127fa6661c77034713b2a5228dc13611bfd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B2=A9=E5=B2=A9?= Date: Wed, 11 Feb 2026 10:03:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(M2.6):=20=E5=9F=BA=E7=A1=80=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E7=BB=84=E4=BB=B6=20(v0.1.6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 src/content/components/DictPanel.js - 面板尺寸 400x300px,白色背景,带阴影 - 使用 Shadow DOM 封装样式 - 点击图标显示面板,图标消失 - 点击页面空白处面板消失 --- docs/QUICK_REF.md | 2 +- docs/README.md | 6 +- docs/VERSION.md | 12 +-- manifest.json | 2 +- package.json | 2 +- src/content/components/DictPanel.js | 129 ++++++++++++++++++++++++++++ src/content/selection.js | 41 ++++++++- 7 files changed, 178 insertions(+), 16 deletions(-) create mode 100644 src/content/components/DictPanel.js diff --git a/docs/QUICK_REF.md b/docs/QUICK_REF.md index fb1332e..c6e3114 100644 --- a/docs/QUICK_REF.md +++ b/docs/QUICK_REF.md @@ -7,7 +7,7 @@ ## 版本速查 ### 当前版本 -`0.1.5` → 下一目标 `0.1.6` ([M2.6](./M2.md)) +`0.1.6` → 下一目标 `0.1.7` ([M2.7](./M2.md)) ### 模块版本范围 diff --git a/docs/README.md b/docs/README.md index b25c9b7..5934bad 100644 --- a/docs/README.md +++ b/docs/README.md @@ -65,9 +65,9 @@ M11.10完成 → 1.0.0 (正式发布) ## 当前状态 -**当前版本**: `0.1.5` -**当前进度**: 10/97 (10%) -**下一任务**: [M2.6 基础面板组件](./M2.md#m26-基础面板组件--目标版本-0116) +**当前版本**: `0.1.6` +**当前进度**: 11/97 (11%) +**下一任务**: [M2.7 面板位置计算](./M2.md#m27-面板位置计算--目标版本-0117) --- diff --git a/docs/VERSION.md b/docs/VERSION.md index 7ff5c59..8634369 100644 --- a/docs/VERSION.md +++ b/docs/VERSION.md @@ -29,12 +29,12 @@ | 任务 | 版本 | 描述 | 状态 | 日期 | |------|------|------|------|------| -| M2.1 | 0.1.1 | 文本选择检测 | ✅ | 2026-02-09 | -| M2.2 | 0.1.2 | 获取选中文本坐标 | ✅ | 2026-02-09 | -| M2.3 | 0.1.3 | 沙拉图标组件 | ✅ | 2026-02-09 | -| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-09 | -| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-09 | -| M2.6 | 0.1.6 | 基础面板组件 | ⬜ | - | +| M2.1 | 0.1.1 | 文本选择检测 | ✅ | 2026-02-10 | +| M2.2 | 0.1.2 | 获取选中文本坐标 | ✅ | 2026-02-10 | +| M2.3 | 0.1.3 | 沙拉图标组件 | ✅ | 2026-02-10 | +| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-10 | +| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-10 | +| M2.6 | 0.1.6 | 基础面板组件 | ✅ | 2026-02-11 | | M2.7 | 0.1.7 | 面板位置计算 | ⬜ | - | | M2.8 | 0.1.8 | 图标-面板联动 | ⬜ | - | | M2.9 | 0.1.9 | 图标显示开关 | ⬜ | - | diff --git a/manifest.json b/manifest.json index 7a9bf84..cd7e282 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "沙拉查词", - "version": "0.1.5", + "version": "0.1.6", "description": "聚合词典划词翻译", "permissions": [ "storage", diff --git a/package.json b/package.json index 7778fd2..8057042 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salad-dict", - "version": "0.1.5", + "version": "0.1.6", "description": "聚合词典划词翻译", "private": true, "type": "module", diff --git a/src/content/components/DictPanel.js b/src/content/components/DictPanel.js new file mode 100644 index 0000000..8fb8302 --- /dev/null +++ b/src/content/components/DictPanel.js @@ -0,0 +1,129 @@ +/** + * @file 词典结果展示面板 + * @description 显示词典查询结果的基础面板组件 + */ + +const PANEL_WIDTH = 400; +const PANEL_HEIGHT = 300; + +/** + * 词典结果面板类 + */ +export class DictPanel { + constructor() { + this.element = this.createElement(); + } + + /** + * 创建面板元素 + * @returns {HTMLElement} 面板元素 + */ + createElement() { + // 创建容器 + const container = document.createElement('div'); + container.style.cssText = ` + position: fixed; + z-index: 2147483646; + display: none; + width: ${PANEL_WIDTH}px; + height: ${PANEL_HEIGHT}px; + `; + + // 创建 Shadow DOM + const shadow = container.attachShadow({ mode: 'open' }); + + // Shadow DOM 内容 + shadow.innerHTML = ` + +
+
词典结果
+
+
查询结果将显示在这里
+
+
+ `; + + // 阻止点击事件冒泡 + container.addEventListener('click', (e) => { + e.stopPropagation(); + }); + + // 添加到页面 + document.body.appendChild(container); + + return container; + } + + /** + * 显示面板在指定位置 + * @param {number} x - X坐标 + * @param {number} y - Y坐标 + */ + show(x, y) { + this.element.style.left = `${x}px`; + this.element.style.top = `${y}px`; + this.element.style.display = 'block'; + } + + /** + * 隐藏面板 + */ + hide() { + this.element.style.display = 'none'; + } + + /** + * 销毁组件 + */ + destroy() { + this.hide(); + if (this.element.parentNode) { + this.element.parentNode.removeChild(this.element); + } + } +} + +/** + * 创建并显示词典面板 + * @param {number} x - X坐标 + * @param {number} y - Y坐标 + * @returns {DictPanel} 面板实例 + */ +export function createDictPanel(x, y) { + const panel = new DictPanel(); + panel.show(x, y); + return panel; +} diff --git a/src/content/selection.js b/src/content/selection.js index 8a3bc88..98c53d8 100644 --- a/src/content/selection.js +++ b/src/content/selection.js @@ -5,8 +5,10 @@ import { logger } from './logger.js'; import { createSaladIcon } from './components/SaladIcon.js'; +import { DictPanel } from './components/DictPanel.js'; let currentIcon = null; +let currentPanel = null; /** * 获取当前选中的文本 @@ -83,6 +85,16 @@ export function hasSelection() { * @param {MouseEvent} event */ function handleMouseUp(event) { + // 如果点击的是当前图标元素,则直接返回 + if (currentIcon && (currentIcon.element == event.target || currentIcon.element.contains(event.target))) { + return; + } + + // 如果点击的是当前面板元素,则直接返回 + if (currentPanel && (currentPanel.element == event.target || currentPanel.element.contains(event.target))) { + return; + } + // 延迟执行,等待选区完成 setTimeout(() => { const selectedText = getSelectedText(); @@ -114,17 +126,31 @@ function handleMouseUp(event) { * @param {number} y - Y坐标 */ function showSaladIcon(x, y) { - // 隐藏旧图标 + // 隐藏旧图标和面板 if (currentIcon) { currentIcon.destroy(); currentIcon = null; } + if (currentPanel) { + currentPanel.destroy(); + currentPanel = null; + } // 创建新图标,传入点击回调 currentIcon = createSaladIcon(x, y, { onClick: (event) => { - logger.info('Icon click callback triggered'); - // 点击后图标不消失(后续再处理) + logger.info('Icon clicked, showing panel'); + + // 图标消失 + if (currentIcon) { + currentIcon.destroy(); + currentIcon = null; + } + + // 显示面板(位置暂时写死 0,0,后续 M2.7 完善) + currentPanel = new DictPanel(); + currentPanel.show(0, 0); + console.log('[SaladDict] Panel shown at (0, 0)'); } }); @@ -132,7 +158,7 @@ function showSaladIcon(x, y) { } /** - * 处理页面点击事件(用于隐藏图标) + * 处理页面点击事件(用于隐藏图标和面板) * @param {MouseEvent} event */ function handleDocumentClick(event) { @@ -142,6 +168,13 @@ function handleDocumentClick(event) { currentIcon = null; logger.info('Icon hidden by document click'); } + + // 如果点击的不是面板元素,则隐藏面板 + if (currentPanel && currentPanel.element !== event.target && !currentPanel.element.contains(event.target)) { + currentPanel.destroy(); + currentPanel = null; + logger.info('Panel hidden by document click'); + } } /**