data-preview 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/data-preview.es.js +206 -0
- package/dist/data-preview.umd.js +32 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YOUR_NAME
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# data-preview
|
|
2
|
+
|
|
3
|
+
AI-native semantic preview runtime for modern web.
|
|
4
|
+
|
|
5
|
+
一个零依赖、框架无关、支持 AI 生成页面的智能预览运行时。
|
|
6
|
+
|
|
7
|
+
------
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Zero Dependency
|
|
12
|
+
- Framework Agnostic
|
|
13
|
+
- Hover Preview(PC)/ Long Press Preview(Mobile)
|
|
14
|
+
- Smart Positioning — 自动适应窗口边界与图片比例
|
|
15
|
+
- SPA Compatible — 内置 `destroy()` 支持路由卸载
|
|
16
|
+
- Auto Dark Mode
|
|
17
|
+
- Dynamic DOM — 内置 MutationObserver,动态内容自动绑定
|
|
18
|
+
- Semantic Protocol Driven
|
|
19
|
+
|
|
20
|
+
------
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install data-preview
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
------
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
#### CDN方式
|
|
33
|
+
|
|
34
|
+
直接集成
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<script src="https://unpkg.com/data-preview/dist/data-preview.umd.js"></script>
|
|
38
|
+
<script>
|
|
39
|
+
DataPreview.init()
|
|
40
|
+
</script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
#### ESM
|
|
44
|
+
|
|
45
|
+
现在模块化集成,需要编译
|
|
46
|
+
|
|
47
|
+
##### Native HTML
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<img src="./thumb.jpg" data-preview />
|
|
51
|
+
|
|
52
|
+
<script type="module">
|
|
53
|
+
import DataPreview from 'data-preview'
|
|
54
|
+
DataPreview.init()
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
##### React
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
import { useEffect } from 'react'
|
|
62
|
+
import DataPreview from 'data-preview'
|
|
63
|
+
|
|
64
|
+
export default function App() {
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
DataPreview.init()
|
|
67
|
+
return () => DataPreview.destroy()
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
return <img src="/thumb.jpg" data-preview data-preview-src="/large.jpg" />
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
##### Vue
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<script setup>
|
|
78
|
+
import { onMounted, onUnmounted } from 'vue'
|
|
79
|
+
import DataPreview from 'data-preview'
|
|
80
|
+
|
|
81
|
+
onMounted(() => DataPreview.init())
|
|
82
|
+
onUnmounted(() => DataPreview.destroy())
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<img src="/thumb.jpg" data-preview data-preview-src="/large.jpg" />
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
------
|
|
91
|
+
|
|
92
|
+
## Semantic Protocol
|
|
93
|
+
|
|
94
|
+
data-preview 基于 `data-preview` 语义协议工作,只需在元素上声明属性,runtime 自动增强交互能力。
|
|
95
|
+
|
|
96
|
+
#### 最简用法
|
|
97
|
+
|
|
98
|
+
```html
|
|
99
|
+
<img src="demo.jpg" data-preview />
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### 原图与缩略图分离
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<img src="thumb.jpg" data-preview data-preview-src="large.jpg" />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### 自定义标注
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<img src="demo.jpg" data-preview data-preview-label="产品展示图" />
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### 非 img 元素
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<div data-preview data-preview-src="large.jpg">
|
|
118
|
+
<img src="thumb.jpg" />
|
|
119
|
+
</div>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
------
|
|
123
|
+
|
|
124
|
+
## API
|
|
125
|
+
|
|
126
|
+
#### `DataPreview.init(selector?)`
|
|
127
|
+
|
|
128
|
+
全局初始化。扫描页面中所有匹配元素并绑定,同时启动 MutationObserver 监听后续动态插入的元素。
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
DataPreview.init() // 默认绑定 [data-preview]
|
|
132
|
+
DataPreview.init('.product-card') // 自定义选择器
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### `DataPreview.bind(el)`
|
|
136
|
+
|
|
137
|
+
手动绑定单个元素,幂等,重复调用无副作用。
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
DataPreview.bind(document.querySelector('.my-img'))
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `DataPreview.bindAll(selector)`
|
|
144
|
+
|
|
145
|
+
手动批量绑定,适合虚拟列表、分页加载等场景(此时建议关闭 MutationObserver 自动绑定)。
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
DataPreview.bindAll('.product-list img')
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### `DataPreview.unbind(el)`
|
|
152
|
+
|
|
153
|
+
解绑单个元素,移除所有事件监听。
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
DataPreview.unbind(el)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `DataPreview.destroy()`
|
|
160
|
+
|
|
161
|
+
完全销毁 runtime,清理 DOM、事件监听、MutationObserver、预加载缓存。适用于 SPA 路由切换、微前端卸载。
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
DataPreview.destroy()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
------
|
|
168
|
+
|
|
169
|
+
## Config
|
|
170
|
+
|
|
171
|
+
在 `init()` 前修改配置项:
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
DataPreview.config.imgPad = 12
|
|
175
|
+
DataPreview.config.maxWRatio = 0.85
|
|
176
|
+
DataPreview.init()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
| 配置项 | 默认值 | 说明 |
|
|
180
|
+
| ------------------- | ------ | ------------------------ |
|
|
181
|
+
| `imgPad` | `8` | 预览框内边距 px |
|
|
182
|
+
| `maxWRatio` | `0.9` | 预览框最大宽度占窗口比例 |
|
|
183
|
+
| `maxHRatio` | `0.85` | 预览框最大高度占窗口比例 |
|
|
184
|
+
| `delayHide` | `120` | 鼠标离开后隐藏延迟 ms |
|
|
185
|
+
| `longPressDuration` | `480` | 移动端长按触发时长 ms |
|
|
186
|
+
|
|
187
|
+
------
|
|
188
|
+
|
|
189
|
+
## Mobile
|
|
190
|
+
|
|
191
|
+
移动端通过 `(pointer: coarse)` 自动识别触摸设备,采用长按触发预览,点击遮罩或右上角 ✕ 关闭。无需额外配置。
|
|
192
|
+
|
|
193
|
+
------
|
|
194
|
+
|
|
195
|
+
## TypeScript
|
|
196
|
+
|
|
197
|
+
包含内置类型声明,无需单独安装 `@types`。
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import DataPreview from 'data-preview'
|
|
201
|
+
|
|
202
|
+
DataPreview.config.imgPad = 12
|
|
203
|
+
DataPreview.init()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
------
|
|
207
|
+
|
|
208
|
+
## Browser Support
|
|
209
|
+
|
|
210
|
+
Chrome、Edge、Safari、Firefox 所有现代浏览器。
|
|
211
|
+
|
|
212
|
+
------
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const ee = (() => {
|
|
2
|
+
const x = {
|
|
3
|
+
imgPad: 8,
|
|
4
|
+
maxWRatio: 0.9,
|
|
5
|
+
maxHRatio: 0.85,
|
|
6
|
+
delayHide: 120,
|
|
7
|
+
longPressDuration: 480,
|
|
8
|
+
badgeOffset: 6
|
|
9
|
+
}, y = 16, b = 12;
|
|
10
|
+
let h, i, H, d, m, p, a, M = null, w = null, L = null, E = null, _ = 0, u = null;
|
|
11
|
+
const v = /* @__PURE__ */ new Map();
|
|
12
|
+
let g = window.matchMedia("(pointer: coarse)").matches;
|
|
13
|
+
const z = window.matchMedia("(prefers-color-scheme: dark)");
|
|
14
|
+
window.matchMedia("(pointer: coarse)").addEventListener("change", (e) => {
|
|
15
|
+
g = e.matches;
|
|
16
|
+
});
|
|
17
|
+
function J() {
|
|
18
|
+
h || (h = T("div", {
|
|
19
|
+
style: "position:fixed;inset:0;z-index:99999;pointer-events:none;"
|
|
20
|
+
}), a = T("div", {
|
|
21
|
+
style: `
|
|
22
|
+
position:fixed;
|
|
23
|
+
width:24px;height:24px;
|
|
24
|
+
border-radius:6px;
|
|
25
|
+
background:rgba(0,0,0,0.45);
|
|
26
|
+
color:#fff;font-size:13px;
|
|
27
|
+
display:flex;align-items:center;justify-content:center;
|
|
28
|
+
cursor:pointer;
|
|
29
|
+
opacity:0;
|
|
30
|
+
transition:opacity 0.15s;
|
|
31
|
+
pointer-events:none;
|
|
32
|
+
z-index:100001;
|
|
33
|
+
user-select:none;
|
|
34
|
+
backdrop-filter:blur(4px);
|
|
35
|
+
`
|
|
36
|
+
}), a.textContent = "🖼️", i = T("div", {
|
|
37
|
+
style: `
|
|
38
|
+
position:fixed;
|
|
39
|
+
border-radius:12px;
|
|
40
|
+
overflow:hidden;
|
|
41
|
+
opacity:0;
|
|
42
|
+
transform:scale(0.95);
|
|
43
|
+
transition:opacity 0.15s,transform 0.15s;
|
|
44
|
+
pointer-events:none;
|
|
45
|
+
z-index:100000;
|
|
46
|
+
will-change:transform,opacity;
|
|
47
|
+
`
|
|
48
|
+
}), p = T("button", {
|
|
49
|
+
style: `
|
|
50
|
+
position:absolute;top:6px;right:6px;
|
|
51
|
+
width:26px;height:26px;
|
|
52
|
+
border-radius:50%;border:none;cursor:pointer;
|
|
53
|
+
display:flex;align-items:center;justify-content:center;
|
|
54
|
+
font-size:13px;line-height:1;z-index:1;
|
|
55
|
+
opacity:0;transition:opacity 0.15s;
|
|
56
|
+
`
|
|
57
|
+
}), p.textContent = "✕", p.setAttribute("aria-label", "关闭预览"), p.addEventListener("click", (e) => {
|
|
58
|
+
e.stopPropagation(), q();
|
|
59
|
+
}), p.addEventListener("touchend", (e) => {
|
|
60
|
+
e.stopPropagation(), q();
|
|
61
|
+
}), H = T("div", { style: "position:relative;" }), d = T("img", { style: "display:block;", alt: "preview" }), m = T("div", { style: "font-size:11px;padding:5px 10px;white-space:nowrap;" }), H.append(d, p), i.append(H, m), h.append(a, i), document.body.appendChild(h), a.addEventListener("mouseenter", () => {
|
|
62
|
+
if (clearTimeout(M), clearTimeout(w), !u) return;
|
|
63
|
+
const e = O(u), n = I(u);
|
|
64
|
+
e && j(e, n);
|
|
65
|
+
}), a.addEventListener("mouseleave", C), i.addEventListener("mouseenter", () => {
|
|
66
|
+
clearTimeout(M), clearTimeout(w), g || (p.style.opacity = "1");
|
|
67
|
+
}), i.addEventListener("mouseleave", () => {
|
|
68
|
+
g || (p.style.opacity = "0"), C();
|
|
69
|
+
}), A(), z.addEventListener("change", A));
|
|
70
|
+
}
|
|
71
|
+
function A() {
|
|
72
|
+
const e = z.matches;
|
|
73
|
+
i.style.background = e ? "#1c1c1e" : "#ffffff", i.style.border = e ? "1px solid rgba(255,255,255,.12)" : "1px solid rgba(0,0,0,.1)", i.style.boxShadow = e ? "0 12px 40px rgba(0,0,0,.55),0 2px 8px rgba(0,0,0,.3)" : "0 8px 32px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.08)", m.style.background = e ? "rgba(255,255,255,.06)" : "rgba(0,0,0,.04)", m.style.color = e ? "rgba(255,255,255,.4)" : "rgba(0,0,0,.4)", m.style.borderTop = e ? "1px solid rgba(255,255,255,.08)" : "1px solid rgba(0,0,0,.06)", p.style.background = e ? "rgba(255,255,255,.15)" : "rgba(0,0,0,.15)", p.style.color = e ? "rgba(255,255,255,.8)" : "rgba(0,0,0,.6)";
|
|
74
|
+
}
|
|
75
|
+
function D(e) {
|
|
76
|
+
const n = e.getBoundingClientRect(), t = x.badgeOffset;
|
|
77
|
+
a.style.left = n.right - 24 - t + "px", a.style.top = n.top + t + "px";
|
|
78
|
+
}
|
|
79
|
+
function R(e, n) {
|
|
80
|
+
const t = x.imgPad * 2, o = m.offsetHeight || 22, r = Math.floor(window.innerWidth * x.maxWRatio) - t, c = Math.floor(window.innerHeight * x.maxHRatio) - t - o, f = e / n;
|
|
81
|
+
let s, l;
|
|
82
|
+
return f >= 1 ? (s = Math.min(r, e), l = s / f, l > c && (l = c, s = l * f)) : (l = Math.min(c, n), s = l * f, s > r && (s = r, l = s / f)), { pw: Math.round(s), ph: Math.round(l) };
|
|
83
|
+
}
|
|
84
|
+
function K(e, n) {
|
|
85
|
+
if (!u) return;
|
|
86
|
+
const t = u.getBoundingClientRect(), o = x.imgPad, r = m.offsetHeight || 22, { pw: c, ph: f } = R(e, n), s = c + o * 2, l = f + o * 2 + r, S = window.innerWidth, G = window.innerHeight;
|
|
87
|
+
let W;
|
|
88
|
+
t.left - b >= s + y ? W = t.left - b - s : S - t.right - b >= s + y ? W = t.right + b : W = k(t.left + (t.width - s) / 2, y, S - s - y);
|
|
89
|
+
let B;
|
|
90
|
+
G - t.bottom - b >= l + y ? B = t.bottom + b : t.top - b >= l + y ? B = t.top - b - l : B = k(t.top + (t.height - l) / 2, y, G - l - y), i.style.left = W + "px", i.style.top = B + "px", i.style.width = s + "px", H.style.padding = o + "px", d.style.width = c + "px", d.style.height = f + "px";
|
|
91
|
+
}
|
|
92
|
+
function U(e, n) {
|
|
93
|
+
const t = x.imgPad, o = m.offsetHeight || 22, { pw: r, ph: c } = R(e, n), f = r + t * 2, s = c + t * 2 + o, l = window.innerWidth, S = window.innerHeight;
|
|
94
|
+
i.style.left = k((l - f) / 2, y, l - f - y) + "px", i.style.top = k((S - s) / 2, y, S - s - y) + "px", i.style.width = f + "px", H.style.padding = t + "px", d.style.width = r + "px", d.style.height = c + "px";
|
|
95
|
+
}
|
|
96
|
+
function V(e) {
|
|
97
|
+
clearTimeout(w), !(u === e && a.style.opacity === "1") && (u = e, D(e), a.style.opacity = "1", a.style.pointerEvents = "auto", h.style.pointerEvents = "auto");
|
|
98
|
+
}
|
|
99
|
+
function j(e, n) {
|
|
100
|
+
const t = ++_;
|
|
101
|
+
m.textContent = n || "";
|
|
102
|
+
function o(r, c) {
|
|
103
|
+
t === _ && (g ? U(r, c) : K(r, c), i.style.opacity = "1", i.style.transform = "scale(1)", i.style.pointerEvents = "auto", g && (p.style.opacity = "1"), m.textContent = n || r + " × " + c);
|
|
104
|
+
}
|
|
105
|
+
if (v.get(e) === "loaded" && d.src === e) {
|
|
106
|
+
o(d.naturalWidth, d.naturalHeight);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
d.src = e, d.onload = () => {
|
|
110
|
+
v.set(e, "loaded"), o(d.naturalWidth, d.naturalHeight);
|
|
111
|
+
}, d.onerror = () => {
|
|
112
|
+
v.set(e, "error");
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function C() {
|
|
116
|
+
clearTimeout(M), M = setTimeout(() => {
|
|
117
|
+
i.style.opacity = "0", i.style.transform = "scale(0.95)", i.style.pointerEvents = "none", a.style.opacity = "0", a.style.pointerEvents = "none", h.style.pointerEvents = "none", p.style.opacity = "0", u = null;
|
|
118
|
+
}, x.delayHide);
|
|
119
|
+
}
|
|
120
|
+
function q() {
|
|
121
|
+
clearTimeout(M), clearTimeout(w), ++_, i.style.opacity = "0", i.style.transform = "scale(0.95)", i.style.pointerEvents = "none", a.style.opacity = "0", a.style.pointerEvents = "none", h.style.pointerEvents = "none", p.style.opacity = "0", u = null;
|
|
122
|
+
}
|
|
123
|
+
function Q(e) {
|
|
124
|
+
if (!e || v.has(e)) return;
|
|
125
|
+
v.set(e, "loading");
|
|
126
|
+
const n = new Image();
|
|
127
|
+
n.onload = () => v.set(e, "loaded"), n.onerror = () => v.set(e, "error"), n.src = e;
|
|
128
|
+
}
|
|
129
|
+
function F() {
|
|
130
|
+
u && a.style.opacity === "1" && D(u);
|
|
131
|
+
}
|
|
132
|
+
function O(e) {
|
|
133
|
+
var n;
|
|
134
|
+
return e.dataset.previewSrc || e.src || e.currentSrc || ((n = e.querySelector("img")) == null ? void 0 : n.src) || null;
|
|
135
|
+
}
|
|
136
|
+
function I(e) {
|
|
137
|
+
return e.dataset.previewLabel || e.alt || e.title || null;
|
|
138
|
+
}
|
|
139
|
+
function P(e) {
|
|
140
|
+
if (e._dpBound) return;
|
|
141
|
+
const n = {
|
|
142
|
+
mouseenter() {
|
|
143
|
+
g || (clearTimeout(w), !(u === e && a.style.opacity === "1") && (Q(O(e)), V(e)));
|
|
144
|
+
},
|
|
145
|
+
mouseleave(t) {
|
|
146
|
+
g || e.contains(t.relatedTarget) || (w = setTimeout(() => {
|
|
147
|
+
u === e && C();
|
|
148
|
+
}, x.delayHide));
|
|
149
|
+
},
|
|
150
|
+
touchstart(t) {
|
|
151
|
+
if (!g) return;
|
|
152
|
+
const o = O(e);
|
|
153
|
+
o && (Q(o), L = setTimeout(() => {
|
|
154
|
+
u = e, j(o, I(e));
|
|
155
|
+
}, x.longPressDuration));
|
|
156
|
+
},
|
|
157
|
+
touchend() {
|
|
158
|
+
clearTimeout(L);
|
|
159
|
+
},
|
|
160
|
+
touchmove() {
|
|
161
|
+
clearTimeout(L);
|
|
162
|
+
},
|
|
163
|
+
touchcancel() {
|
|
164
|
+
clearTimeout(L);
|
|
165
|
+
},
|
|
166
|
+
contextmenu(t) {
|
|
167
|
+
g && t.preventDefault();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
Object.entries(n).forEach(([t, o]) => e.addEventListener(t, o)), e._dpBound = !0, e._dpHandlers = n;
|
|
171
|
+
}
|
|
172
|
+
function N(e) {
|
|
173
|
+
e._dpBound && (Object.entries(e._dpHandlers).forEach(
|
|
174
|
+
([n, t]) => e.removeEventListener(n, t)
|
|
175
|
+
), e._dpBound = !1, e._dpHandlers = null, e._dpMoveTimer = null);
|
|
176
|
+
}
|
|
177
|
+
function X(e) {
|
|
178
|
+
document.querySelectorAll(e).forEach((n) => P(n));
|
|
179
|
+
}
|
|
180
|
+
function Y(e = "[data-preview]") {
|
|
181
|
+
J(), document.querySelectorAll(e).forEach((n) => P(n)), window.addEventListener("scroll", F, !0), E = new MutationObserver((n) => {
|
|
182
|
+
n.forEach((t) => {
|
|
183
|
+
t.addedNodes.forEach((o) => {
|
|
184
|
+
var r, c;
|
|
185
|
+
o.nodeType === 1 && ((r = o.matches) != null && r.call(o, e) && P(o), (c = o.querySelectorAll) == null || c.call(o, e).forEach((f) => P(f)));
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}), E.observe(document.body, { childList: !0, subtree: !0 });
|
|
189
|
+
}
|
|
190
|
+
function Z() {
|
|
191
|
+
E == null || E.disconnect(), E = null, document.querySelectorAll("[data-preview]").forEach((e) => N(e)), window.removeEventListener("scroll", F, !0), z.removeEventListener("change", A), h == null || h.remove(), h = i = H = d = m = p = a = null, v.clear(), clearTimeout(M), clearTimeout(w), clearTimeout(L);
|
|
192
|
+
}
|
|
193
|
+
function T(e, n = {}) {
|
|
194
|
+
const t = document.createElement(e);
|
|
195
|
+
return Object.entries(n).forEach(([o, r]) => {
|
|
196
|
+
o === "style" ? t.style.cssText = r : t[o] = r;
|
|
197
|
+
}), t;
|
|
198
|
+
}
|
|
199
|
+
function k(e, n, t) {
|
|
200
|
+
return Math.max(n, Math.min(e, t));
|
|
201
|
+
}
|
|
202
|
+
return { init: Y, bind: P, unbind: N, bindAll: X, destroy: Z, config: x };
|
|
203
|
+
})();
|
|
204
|
+
export {
|
|
205
|
+
ee as default
|
|
206
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
(function(k,p){typeof exports=="object"&&typeof module<"u"?module.exports=p():typeof define=="function"&&define.amd?define(p):(k=typeof globalThis<"u"?globalThis:k||self,k.DataPreview=p())})(this,(function(){"use strict";return(()=>{const p={imgPad:8,maxWRatio:.9,maxHRatio:.85,delayHide:120,longPressDuration:480,badgeOffset:6},h=16,g=12;let m,i,H,d,x,u,a,P=null,w=null,E=null,M=null,z=0,f=null;const v=new Map;let b=window.matchMedia("(pointer: coarse)").matches;const A=window.matchMedia("(prefers-color-scheme: dark)");window.matchMedia("(pointer: coarse)").addEventListener("change",e=>{b=e.matches});function K(){m||(m=T("div",{style:"position:fixed;inset:0;z-index:99999;pointer-events:none;"}),a=T("div",{style:`
|
|
2
|
+
position:fixed;
|
|
3
|
+
width:24px;height:24px;
|
|
4
|
+
border-radius:6px;
|
|
5
|
+
background:rgba(0,0,0,0.45);
|
|
6
|
+
color:#fff;font-size:13px;
|
|
7
|
+
display:flex;align-items:center;justify-content:center;
|
|
8
|
+
cursor:pointer;
|
|
9
|
+
opacity:0;
|
|
10
|
+
transition:opacity 0.15s;
|
|
11
|
+
pointer-events:none;
|
|
12
|
+
z-index:100001;
|
|
13
|
+
user-select:none;
|
|
14
|
+
backdrop-filter:blur(4px);
|
|
15
|
+
`}),a.textContent="🖼️",i=T("div",{style:`
|
|
16
|
+
position:fixed;
|
|
17
|
+
border-radius:12px;
|
|
18
|
+
overflow:hidden;
|
|
19
|
+
opacity:0;
|
|
20
|
+
transform:scale(0.95);
|
|
21
|
+
transition:opacity 0.15s,transform 0.15s;
|
|
22
|
+
pointer-events:none;
|
|
23
|
+
z-index:100000;
|
|
24
|
+
will-change:transform,opacity;
|
|
25
|
+
`}),u=T("button",{style:`
|
|
26
|
+
position:absolute;top:6px;right:6px;
|
|
27
|
+
width:26px;height:26px;
|
|
28
|
+
border-radius:50%;border:none;cursor:pointer;
|
|
29
|
+
display:flex;align-items:center;justify-content:center;
|
|
30
|
+
font-size:13px;line-height:1;z-index:1;
|
|
31
|
+
opacity:0;transition:opacity 0.15s;
|
|
32
|
+
`}),u.textContent="✕",u.setAttribute("aria-label","关闭预览"),u.addEventListener("click",e=>{e.stopPropagation(),Q()}),u.addEventListener("touchend",e=>{e.stopPropagation(),Q()}),H=T("div",{style:"position:relative;"}),d=T("img",{style:"display:block;",alt:"preview"}),x=T("div",{style:"font-size:11px;padding:5px 10px;white-space:nowrap;"}),H.append(d,u),i.append(H,x),m.append(a,i),document.body.appendChild(m),a.addEventListener("mouseenter",()=>{if(clearTimeout(P),clearTimeout(w),!f)return;const e=O(f),n=N(f);e&&q(e,n)}),a.addEventListener("mouseleave",D),i.addEventListener("mouseenter",()=>{clearTimeout(P),clearTimeout(w),b||(u.style.opacity="1")}),i.addEventListener("mouseleave",()=>{b||(u.style.opacity="0"),D()}),C(),A.addEventListener("change",C))}function C(){const e=A.matches;i.style.background=e?"#1c1c1e":"#ffffff",i.style.border=e?"1px solid rgba(255,255,255,.12)":"1px solid rgba(0,0,0,.1)",i.style.boxShadow=e?"0 12px 40px rgba(0,0,0,.55),0 2px 8px rgba(0,0,0,.3)":"0 8px 32px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.08)",x.style.background=e?"rgba(255,255,255,.06)":"rgba(0,0,0,.04)",x.style.color=e?"rgba(255,255,255,.4)":"rgba(0,0,0,.4)",x.style.borderTop=e?"1px solid rgba(255,255,255,.08)":"1px solid rgba(0,0,0,.06)",u.style.background=e?"rgba(255,255,255,.15)":"rgba(0,0,0,.15)",u.style.color=e?"rgba(255,255,255,.8)":"rgba(0,0,0,.6)"}function j(e){const n=e.getBoundingClientRect(),t=p.badgeOffset;a.style.left=n.right-24-t+"px",a.style.top=n.top+t+"px"}function R(e,n){const t=p.imgPad*2,o=x.offsetHeight||22,r=Math.floor(window.innerWidth*p.maxWRatio)-t,c=Math.floor(window.innerHeight*p.maxHRatio)-t-o,y=e/n;let s,l;return y>=1?(s=Math.min(r,e),l=s/y,l>c&&(l=c,s=l*y)):(l=Math.min(c,n),s=l*y,s>r&&(s=r,l=s/y)),{pw:Math.round(s),ph:Math.round(l)}}function U(e,n){if(!f)return;const t=f.getBoundingClientRect(),o=p.imgPad,r=x.offsetHeight||22,{pw:c,ph:y}=R(e,n),s=c+o*2,l=y+o*2+r,S=window.innerWidth,J=window.innerHeight;let B;t.left-g>=s+h?B=t.left-g-s:S-t.right-g>=s+h?B=t.right+g:B=W(t.left+(t.width-s)/2,h,S-s-h);let _;J-t.bottom-g>=l+h?_=t.bottom+g:t.top-g>=l+h?_=t.top-g-l:_=W(t.top+(t.height-l)/2,h,J-l-h),i.style.left=B+"px",i.style.top=_+"px",i.style.width=s+"px",H.style.padding=o+"px",d.style.width=c+"px",d.style.height=y+"px"}function V(e,n){const t=p.imgPad,o=x.offsetHeight||22,{pw:r,ph:c}=R(e,n),y=r+t*2,s=c+t*2+o,l=window.innerWidth,S=window.innerHeight;i.style.left=W((l-y)/2,h,l-y-h)+"px",i.style.top=W((S-s)/2,h,S-s-h)+"px",i.style.width=y+"px",H.style.padding=t+"px",d.style.width=r+"px",d.style.height=c+"px"}function X(e){clearTimeout(w),!(f===e&&a.style.opacity==="1")&&(f=e,j(e),a.style.opacity="1",a.style.pointerEvents="auto",m.style.pointerEvents="auto")}function q(e,n){const t=++z;x.textContent=n||"";function o(r,c){t===z&&(b?V(r,c):U(r,c),i.style.opacity="1",i.style.transform="scale(1)",i.style.pointerEvents="auto",b&&(u.style.opacity="1"),x.textContent=n||r+" × "+c)}if(v.get(e)==="loaded"&&d.src===e){o(d.naturalWidth,d.naturalHeight);return}d.src=e,d.onload=()=>{v.set(e,"loaded"),o(d.naturalWidth,d.naturalHeight)},d.onerror=()=>{v.set(e,"error")}}function D(){clearTimeout(P),P=setTimeout(()=>{i.style.opacity="0",i.style.transform="scale(0.95)",i.style.pointerEvents="none",a.style.opacity="0",a.style.pointerEvents="none",m.style.pointerEvents="none",u.style.opacity="0",f=null},p.delayHide)}function Q(){clearTimeout(P),clearTimeout(w),++z,i.style.opacity="0",i.style.transform="scale(0.95)",i.style.pointerEvents="none",a.style.opacity="0",a.style.pointerEvents="none",m.style.pointerEvents="none",u.style.opacity="0",f=null}function F(e){if(!e||v.has(e))return;v.set(e,"loading");const n=new Image;n.onload=()=>v.set(e,"loaded"),n.onerror=()=>v.set(e,"error"),n.src=e}function I(){f&&a.style.opacity==="1"&&j(f)}function O(e){var n;return e.dataset.previewSrc||e.src||e.currentSrc||((n=e.querySelector("img"))==null?void 0:n.src)||null}function N(e){return e.dataset.previewLabel||e.alt||e.title||null}function L(e){if(e._dpBound)return;const n={mouseenter(){b||(clearTimeout(w),!(f===e&&a.style.opacity==="1")&&(F(O(e)),X(e)))},mouseleave(t){b||e.contains(t.relatedTarget)||(w=setTimeout(()=>{f===e&&D()},p.delayHide))},touchstart(t){if(!b)return;const o=O(e);o&&(F(o),E=setTimeout(()=>{f=e,q(o,N(e))},p.longPressDuration))},touchend(){clearTimeout(E)},touchmove(){clearTimeout(E)},touchcancel(){clearTimeout(E)},contextmenu(t){b&&t.preventDefault()}};Object.entries(n).forEach(([t,o])=>e.addEventListener(t,o)),e._dpBound=!0,e._dpHandlers=n}function G(e){e._dpBound&&(Object.entries(e._dpHandlers).forEach(([n,t])=>e.removeEventListener(n,t)),e._dpBound=!1,e._dpHandlers=null,e._dpMoveTimer=null)}function Y(e){document.querySelectorAll(e).forEach(n=>L(n))}function Z(e="[data-preview]"){K(),document.querySelectorAll(e).forEach(n=>L(n)),window.addEventListener("scroll",I,!0),M=new MutationObserver(n=>{n.forEach(t=>{t.addedNodes.forEach(o=>{var r,c;o.nodeType===1&&((r=o.matches)!=null&&r.call(o,e)&&L(o),(c=o.querySelectorAll)==null||c.call(o,e).forEach(y=>L(y)))})})}),M.observe(document.body,{childList:!0,subtree:!0})}function $(){M==null||M.disconnect(),M=null,document.querySelectorAll("[data-preview]").forEach(e=>G(e)),window.removeEventListener("scroll",I,!0),A.removeEventListener("change",C),m==null||m.remove(),m=i=H=d=x=u=a=null,v.clear(),clearTimeout(P),clearTimeout(w),clearTimeout(E)}function T(e,n={}){const t=document.createElement(e);return Object.entries(n).forEach(([o,r])=>{o==="style"?t.style.cssText=r:t[o]=r}),t}function W(e,n,t){return Math.max(n,Math.min(e,t))}return{init:Z,bind:L,unbind:G,bindAll:Y,destroy:$,config:p}})()}));
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "data-preview",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-native semantic preview runtime for modern web.",
|
|
5
|
+
"keywords": ["image preview", "big image", "semantic protocol", "ai-native","data preview protocol"],
|
|
6
|
+
"type": "module",
|
|
7
|
+
|
|
8
|
+
"main": "./dist/data-preview.umd.js",
|
|
9
|
+
"module": "./dist/data-preview.js",
|
|
10
|
+
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/data-preview.js",
|
|
14
|
+
"default": "./dist/data-preview.umd.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"prepublishOnly": "vite build"
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"vite": "^6.0.0"
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/doc-war/data-preview"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/doc-war/data-preview#readme",
|
|
38
|
+
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|