feat(M2.6): 基础面板组件 (v0.1.6)
- 创建 src/content/components/DictPanel.js - 面板尺寸 400x300px,白色背景,带阴影 - 使用 Shadow DOM 封装样式 - 点击图标显示面板,图标消失 - 点击页面空白处面板消失
This commit is contained in:
parent
4e12552437
commit
20dd127fa6
@ -7,7 +7,7 @@
|
||||
## 版本速查
|
||||
|
||||
### 当前版本
|
||||
`0.1.5` → 下一目标 `0.1.6` ([M2.6](./M2.md))
|
||||
`0.1.6` → 下一目标 `0.1.7` ([M2.7](./M2.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)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -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 | 图标显示开关 | ⬜ | - |
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "沙拉查词",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"description": "聚合词典划词翻译",
|
||||
"permissions": [
|
||||
"storage",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "salad-dict",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"description": "聚合词典划词翻译",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
129
src/content/components/DictPanel.js
Normal file
129
src/content/components/DictPanel.js
Normal file
@ -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 = `
|
||||
<style>
|
||||
.panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 12px 16px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
}
|
||||
</style>
|
||||
<div class="panel">
|
||||
<div class="header">词典结果</div>
|
||||
<div class="content">
|
||||
<div class="placeholder">查询结果将显示在这里</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 阻止点击事件冒泡
|
||||
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;
|
||||
}
|
||||
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user