refactor: md-pins

This commit is contained in:
hypercross 2026-02-27 00:27:04 +08:00
parent f1a55bf83e
commit ca1801e1a7
3 changed files with 3 additions and 129 deletions

View File

@ -2,8 +2,7 @@
import './dice'; import './dice';
import './table'; import './table';
import './md-link'; import './md-link';
import './md-pin'; import './md-pins';
import './md-pin-editor';
// 导出组件 // 导出组件
export { Article } from './Article'; export { Article } from './Article';

View File

@ -1,125 +0,0 @@
import { customElement, noShadowDOM } from "solid-element";
import { createSignal, onMount, onCleanup, Show } from "solid-js";
customElement("md-pin", { x: 0, y: 0 }, (props, { element }) => {
noShadowDOM();
const [position, setPosition] = createSignal<{ top: string; left: string }>({ top: "0", left: "0" });
const [visible, setVisible] = createSignal(false);
const [transformStyle, setTransformStyle] = createSignal<string>("");
let pinContainer: HTMLSpanElement | undefined;
let targetImage: HTMLImageElement | undefined;
let resizeObserver: ResizeObserver | undefined;
// 从 element 的 textContent 获取 pin 标签内容
const label = element?.textContent?.trim() || "";
// 隐藏原始文本内容
if (element) {
element.textContent = "";
}
// 查找上方最近的图片
const findNearestImage = (): HTMLImageElement | null => {
if (!element) return null;
// 从当前元素向上查找
let current: Element | null = element;
while (current) {
// 在当前元素的之前兄弟节点中查找图片
let sibling: Element | null = current.previousElementSibling;
while (sibling) {
// 检查是否是图片元素
const img = sibling.querySelector('img');
if (img) return img;
// 检查元素本身是否有图片相关的类或标签
if (sibling.tagName === 'IMG') return sibling as HTMLImageElement;
sibling = sibling.previousElementSibling;
}
current = current.parentElement;
}
return null;
};
// 更新 pin 位置和容器样式
const updatePosition = () => {
if (!targetImage || !pinContainer) return;
const imgRect = targetImage.getBoundingClientRect();
const containerRect = pinContainer.parentElement.getBoundingClientRect();
// 计算图片左上角相对于容器原始位置的偏移
const offsetX = imgRect.left - containerRect.left;
const offsetY = imgRect.top - containerRect.top;
// 使用 transform 将容器移动到图片位置
setTransformStyle(`translate(${offsetX}px, ${offsetY}px)`);
// 计算 pin 在图片内的相对位置x/y 是百分比)
const x = typeof props.x === 'number' ? props.x : parseFloat(props.x) || 0;
const y = typeof props.y === 'number' ? props.y : parseFloat(props.y) || 0;
const left = (x / 100) * imgRect.width;
const top = (y / 100) * imgRect.height;
setPosition({
left: `${left}px`,
top: `${top}px`
});
};
onMount(() => {
// 查找目标图片
targetImage = findNearestImage();
if (targetImage) {
// 初始定位
updatePosition();
// 使用 ResizeObserver 监听图片大小变化
resizeObserver = new ResizeObserver(() => {
updatePosition();
});
resizeObserver.observe(targetImage);
// 延迟显示以等待位置计算完成
requestAnimationFrame(() => {
setVisible(true);
});
} else {
console.warn('md-pin: 未找到目标图片');
}
});
onCleanup(() => {
if (resizeObserver && targetImage) {
resizeObserver.unobserve(targetImage);
}
});
return (
<div
ref={pinContainer}
class="md-pin-container"
style={{ transform: transformStyle(), 'transform-origin': 'top left' }}
>
<Show when={visible() && targetImage}>
<span
class="md-pin absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-auto
bg-red-500 text-white text-xs font-bold rounded-full w-6 h-6
flex items-center justify-center shadow-lg
z-10"
style={{
left: position().left,
top: position().top
}}
>
{label || '📍'}
</span>
</Show>
</div>
);
});

View File

@ -63,7 +63,7 @@ function findNextUnusedLabel(pins: Pin[]): string {
return generateLabel(pins.length); return generateLabel(pins.length);
} }
customElement("md-pin-editor", { pins: "", fixed: false }, (props, { element }) => { customElement("md-pins", { pins: "", fixed: false }, (props, { element }) => {
noShadowDOM(); noShadowDOM();
const [pins, setPins] = createSignal<Pin[]>([]); const [pins, setPins] = createSignal<Pin[]>([]);
@ -139,7 +139,7 @@ customElement("md-pin-editor", { pins: "", fixed: false }, (props, { element })
// 复制所有 pin 为 :md-editor-pin 格式 // 复制所有 pin 为 :md-editor-pin 格式
const copyPins = () => { const copyPins = () => {
const pinsStr = formatPins(pins()); const pinsStr = formatPins(pins());
const text = `:md-pin-editor[${rawSrc}]{pins="${pinsStr}" fixed}`; const text = `:md-pins[${rawSrc}]{pins="${pinsStr}" fixed}`;
navigator.clipboard.writeText(text).then(() => { navigator.clipboard.writeText(text).then(() => {
setShowToast(true); setShowToast(true);