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 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
+ }