feat(M2.7): 面板位置计算 (v0.1.7)
- 面板智能定位,不超出视口 - 右侧/下方超出时自动显示在左侧/上方 - 面板边界与视口保留 10px 边距 - 支持拖拽面板头部更改位置
This commit is contained in:
parent
20dd127fa6
commit
ee531f4987
@ -69,6 +69,7 @@
|
|||||||
- [ ] 如果右侧超出视口,显示在左侧
|
- [ ] 如果右侧超出视口,显示在左侧
|
||||||
- [ ] 如果下方超出视口,显示在上方
|
- [ ] 如果下方超出视口,显示在上方
|
||||||
- [ ] 面板边界与视口保留 10px 边距
|
- [ ] 面板边界与视口保留 10px 边距
|
||||||
|
- [ ] 可以通过拖拽面板头部更改面板位置
|
||||||
|
|
||||||
### M2.8 图标-面板联动 [目标版本: 0.1.8]
|
### M2.8 图标-面板联动 [目标版本: 0.1.8]
|
||||||
**任务**: 点击图标显示面板
|
**任务**: 点击图标显示面板
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
## 版本速查
|
## 版本速查
|
||||||
|
|
||||||
### 当前版本
|
### 当前版本
|
||||||
`0.1.6` → 下一目标 `0.1.7` ([M2.7](./M2.md))
|
`0.1.7` → 下一目标 `0.1.8` ([M2.8](./M2.md))
|
||||||
|
|
||||||
### 模块版本范围
|
### 模块版本范围
|
||||||
|
|
||||||
|
|||||||
@ -65,9 +65,9 @@ M11.10完成 → 1.0.0 (正式发布)
|
|||||||
|
|
||||||
## 当前状态
|
## 当前状态
|
||||||
|
|
||||||
**当前版本**: `0.1.6`
|
**当前版本**: `0.1.7`
|
||||||
**当前进度**: 11/97 (11%)
|
**当前进度**: 12/97 (12%)
|
||||||
**下一任务**: [M2.7 面板位置计算](./M2.md#m27-面板位置计算--目标版本-0117)
|
**下一任务**: [M2.8 图标-面板联动](./M2.md#m28-图标-面板联动--目标版本-0118)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -105,8 +105,9 @@ M11.10完成 → 1.0.0 (正式发布)
|
|||||||
|
|
||||||
1. **阅读规范**: 开始前阅读 [AGENTS.md](./AGENTS.md) 和 [QUICK_REF.md](./QUICK_REF.md)
|
1. **阅读规范**: 开始前阅读 [AGENTS.md](./AGENTS.md) 和 [QUICK_REF.md](./QUICK_REF.md)
|
||||||
2. **查看模块**: 根据当前版本打开对应的模块文件(如 [M1.md](./M1.md))
|
2. **查看模块**: 根据当前版本打开对应的模块文件(如 [M1.md](./M1.md))
|
||||||
3. **开发实现**: 按任务顺序实现,每个任务完成更新版本号
|
3. **开发实现**: 按任务顺序实现需求
|
||||||
4. **提交验收**: 用户确认后更新 VERSION.md 并提交 commit
|
4. **更新文档**:任务完成后更新`docs/README.md`的`当前状态`小节,更新`docs/VERSION.md`中对应人物的`状态`和`日期`,更新`docs/QUICK_REF.md`的`当前版本`小节,更新`package.json`和`manifest.json`的版本号
|
||||||
|
5. **提交验收**: 用户确认后提交代码
|
||||||
|
|
||||||
### Commit 格式
|
### Commit 格式
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -4,15 +4,6 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 当前版本
|
|
||||||
|
|
||||||
**版本**: `0.0.0`
|
|
||||||
**状态**: 🟡 初始状态
|
|
||||||
**进度**: 0/97 (0%)
|
|
||||||
**下一目标**: [M1.1 项目初始化](./M1.md#m11)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## M1 基础架构 (0.0.1 - 0.0.5)
|
## M1 基础架构 (0.0.1 - 0.0.5)
|
||||||
|
|
||||||
| 任务 | 版本 | 描述 | 状态 | 日期 |
|
| 任务 | 版本 | 描述 | 状态 | 日期 |
|
||||||
@ -35,7 +26,7 @@
|
|||||||
| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-10 |
|
| M2.4 | 0.1.4 | 图标定位显示 | ✅ | 2026-02-10 |
|
||||||
| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-10 |
|
| M2.5 | 0.1.5 | 图标点击事件 | ✅ | 2026-02-10 |
|
||||||
| M2.6 | 0.1.6 | 基础面板组件 | ✅ | 2026-02-11 |
|
| M2.6 | 0.1.6 | 基础面板组件 | ✅ | 2026-02-11 |
|
||||||
| M2.7 | 0.1.7 | 面板位置计算 | ⬜ | - |
|
| M2.7 | 0.1.7 | 面板位置计算 | ✅ | 2026-02-09 |
|
||||||
| M2.8 | 0.1.8 | 图标-面板联动 | ⬜ | - |
|
| M2.8 | 0.1.8 | 图标-面板联动 | ⬜ | - |
|
||||||
| M2.9 | 0.1.9 | 图标显示开关 | ⬜ | - |
|
| M2.9 | 0.1.9 | 图标显示开关 | ⬜ | - |
|
||||||
|
|
||||||
@ -185,11 +176,3 @@
|
|||||||
| M11.9 | 0.10.9 | 打包配置 | ⬜ | - |
|
| M11.9 | 0.10.9 | 打包配置 | ⬜ | - |
|
||||||
| M11.10 | 1.0.0 | 图标和素材 | ⬜ | - |
|
| M11.10 | 1.0.0 | 图标和素材 | ⬜ | - |
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 统计
|
|
||||||
|
|
||||||
- **总计**: 97 个任务
|
|
||||||
- **已完成**: 0
|
|
||||||
- **剩余**: 97
|
|
||||||
- **当前进度**: 0%
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "沙拉查词",
|
"name": "沙拉查词",
|
||||||
"version": "0.1.6",
|
"version": "0.1.7",
|
||||||
"description": "聚合词典划词翻译",
|
"description": "聚合词典划词翻译",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "salad-dict",
|
"name": "salad-dict",
|
||||||
"version": "0.1.6",
|
"version": "0.1.7",
|
||||||
"description": "聚合词典划词翻译",
|
"description": "聚合词典划词翻译",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
const PANEL_WIDTH = 400;
|
const PANEL_WIDTH = 400;
|
||||||
const PANEL_HEIGHT = 300;
|
const PANEL_HEIGHT = 300;
|
||||||
|
const VIEWPORT_MARGIN = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 词典结果面板类
|
* 词典结果面板类
|
||||||
@ -12,6 +13,9 @@ const PANEL_HEIGHT = 300;
|
|||||||
export class DictPanel {
|
export class DictPanel {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.element = this.createElement();
|
this.element = this.createElement();
|
||||||
|
this.isDragging = false;
|
||||||
|
this.dragOffset = { x: 0, y: 0 };
|
||||||
|
this.setupDragHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,6 +58,12 @@ export class DictPanel {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header:hover {
|
||||||
|
background-color: #45a049;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -88,13 +98,111 @@ export class DictPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示面板在指定位置
|
* 设置拖拽事件处理
|
||||||
|
*/
|
||||||
|
setupDragHandlers() {
|
||||||
|
const header = this.element.shadowRoot.querySelector('.header');
|
||||||
|
|
||||||
|
// 鼠标按下开始拖拽
|
||||||
|
header.addEventListener('mousedown', (e) => {
|
||||||
|
this.isDragging = true;
|
||||||
|
this.dragOffset.x = e.clientX - this.element.offsetLeft;
|
||||||
|
this.dragOffset.y = e.clientY - this.element.offsetTop;
|
||||||
|
|
||||||
|
// 阻止默认行为和冒泡
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
console.log('[SaladDict] Panel drag start');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 鼠标移动时更新位置
|
||||||
|
const handleMouseMove = (e) => {
|
||||||
|
if (!this.isDragging) return;
|
||||||
|
|
||||||
|
let newX = e.clientX - this.dragOffset.x;
|
||||||
|
let newY = e.clientY - this.dragOffset.y;
|
||||||
|
|
||||||
|
// 限制不超出视口
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
newX = Math.max(VIEWPORT_MARGIN, Math.min(newX, viewportWidth - PANEL_WIDTH - VIEWPORT_MARGIN));
|
||||||
|
newY = Math.max(VIEWPORT_MARGIN, Math.min(newY, viewportHeight - PANEL_HEIGHT - VIEWPORT_MARGIN));
|
||||||
|
|
||||||
|
this.element.style.left = `${newX}px`;
|
||||||
|
this.element.style.top = `${newY}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标抬起结束拖拽
|
||||||
|
const handleMouseUp = () => {
|
||||||
|
if (this.isDragging) {
|
||||||
|
this.isDragging = false;
|
||||||
|
console.log('[SaladDict] Panel drag end');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleMouseMove);
|
||||||
|
document.addEventListener('mouseup', handleMouseUp);
|
||||||
|
|
||||||
|
// 保存引用以便销毁时移除
|
||||||
|
this._dragHandlers = { handleMouseMove, handleMouseUp };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算面板位置,确保不超出视口
|
||||||
|
* @param {number} x - 目标X坐标(图标右下方)
|
||||||
|
* @param {number} y - 目标Y坐标
|
||||||
|
* @returns {Object} {x, y} 调整后的坐标
|
||||||
|
*/
|
||||||
|
calculatePosition(x, y) {
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
|
||||||
|
// 默认显示在图标右下方
|
||||||
|
let panelX = x;
|
||||||
|
let panelY = y;
|
||||||
|
|
||||||
|
// 检查右侧是否超出视口
|
||||||
|
if (panelX + PANEL_WIDTH + VIEWPORT_MARGIN > viewportWidth) {
|
||||||
|
// 显示在左侧
|
||||||
|
panelX = x - PANEL_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查下方是否超出视口
|
||||||
|
if (panelY + PANEL_HEIGHT + VIEWPORT_MARGIN > viewportHeight) {
|
||||||
|
// 显示在上方
|
||||||
|
panelY = y - PANEL_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保不超出左边界
|
||||||
|
panelX = Math.max(VIEWPORT_MARGIN, panelX);
|
||||||
|
|
||||||
|
// 确保不超出上边界
|
||||||
|
panelY = Math.max(VIEWPORT_MARGIN, panelY);
|
||||||
|
|
||||||
|
// 确保不超出右边界(如果从左侧显示仍超出)
|
||||||
|
if (panelX + PANEL_WIDTH > viewportWidth - VIEWPORT_MARGIN) {
|
||||||
|
panelX = viewportWidth - PANEL_WIDTH - VIEWPORT_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保不超出下边界(如果从上方显示仍超出)
|
||||||
|
if (panelY + PANEL_HEIGHT > viewportHeight - VIEWPORT_MARGIN) {
|
||||||
|
panelY = viewportHeight - PANEL_HEIGHT - VIEWPORT_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: panelX, y: panelY };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示面板在指定位置(自动调整不超出视口)
|
||||||
* @param {number} x - X坐标
|
* @param {number} x - X坐标
|
||||||
* @param {number} y - Y坐标
|
* @param {number} y - Y坐标
|
||||||
*/
|
*/
|
||||||
show(x, y) {
|
show(x, y) {
|
||||||
this.element.style.left = `${x}px`;
|
const position = this.calculatePosition(x, y);
|
||||||
this.element.style.top = `${y}px`;
|
this.element.style.left = `${position.x}px`;
|
||||||
|
this.element.style.top = `${position.y}px`;
|
||||||
this.element.style.display = 'block';
|
this.element.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +218,13 @@ export class DictPanel {
|
|||||||
*/
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
|
// 移除拖拽事件监听
|
||||||
|
if (this._dragHandlers) {
|
||||||
|
document.removeEventListener('mousemove', this._dragHandlers.handleMouseMove);
|
||||||
|
document.removeEventListener('mouseup', this._dragHandlers.handleMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.element.parentNode) {
|
if (this.element.parentNode) {
|
||||||
this.element.parentNode.removeChild(this.element);
|
this.element.parentNode.removeChild(this.element);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -147,10 +147,12 @@ function showSaladIcon(x, y) {
|
|||||||
currentIcon = null;
|
currentIcon = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示面板(位置暂时写死 0,0,后续 M2.7 完善)
|
// 显示面板在图标右下方
|
||||||
|
const panelX = x + 24; // 图标宽度
|
||||||
|
const panelY = y + 24; // 图标高度
|
||||||
currentPanel = new DictPanel();
|
currentPanel = new DictPanel();
|
||||||
currentPanel.show(0, 0);
|
currentPanel.show(panelX, panelY);
|
||||||
console.log('[SaladDict] Panel shown at (0, 0)');
|
console.log('[SaladDict] Panel shown at:', panelX, panelY);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user