lite-image-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 +24 -0
- package/README.md +286 -0
- package/dist/main.cjs +722 -0
- package/dist/main.d.ts +116 -0
- package/dist/main.js +719 -0
- package/package.json +63 -0
- package/rolldown.config.js +18 -0
- package/src/main.ts +11 -0
- package/src/preview.ts +834 -0
- package/src/types.ts +98 -0
- package/src/util.ts +102 -0
- package/tsconfig.app.json +47 -0
- package/tsconfig.json +11 -0
- package/tsconfig.node.json +24 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A 2D point in viewport/client coordinates.
|
|
3
|
+
*/
|
|
4
|
+
export type Point = {
|
|
5
|
+
x: number
|
|
6
|
+
y: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A simple transform state used by the default transform-based adapter.
|
|
11
|
+
*/
|
|
12
|
+
export type TransformState = {
|
|
13
|
+
scale: number
|
|
14
|
+
x: number
|
|
15
|
+
y: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An SVG viewBox rectangle.
|
|
20
|
+
*/
|
|
21
|
+
export type ViewBox = {
|
|
22
|
+
x: number
|
|
23
|
+
y: number
|
|
24
|
+
w: number
|
|
25
|
+
h: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A function that closes an opened preview dialog.
|
|
30
|
+
*/
|
|
31
|
+
export type PreviewCloseHandle = () => void
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The minimum controller interface used by the preview container.
|
|
35
|
+
* Custom adapters may extend this behavior, but these methods are required.
|
|
36
|
+
*/
|
|
37
|
+
export interface PreviewController {
|
|
38
|
+
/**
|
|
39
|
+
* Zoom in or out using a wheel event.
|
|
40
|
+
* Implementations should call preventDefault() when appropriate.
|
|
41
|
+
*/
|
|
42
|
+
zoomWithWheel: (e: WheelEvent) => void
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Release listeners and internal state created by the adapter.
|
|
46
|
+
*/
|
|
47
|
+
destroy: () => void
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Restore the element's inline styles or attributes back to the pre-preview state.
|
|
51
|
+
*/
|
|
52
|
+
resetStyle: () => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The public adapter interface used by createPreview.
|
|
57
|
+
*
|
|
58
|
+
* The preview container handles dialog lifecycle and gesture routing,
|
|
59
|
+
* while the adapter owns the actual rendering backend.
|
|
60
|
+
* For example:
|
|
61
|
+
* - image previews can use CSS transform
|
|
62
|
+
* - SVG previews can use viewBox
|
|
63
|
+
*/
|
|
64
|
+
export interface PreviewAdapter extends PreviewController {
|
|
65
|
+
/**
|
|
66
|
+
* Reset the visual state to the initial "fit to stage" layout.
|
|
67
|
+
*/
|
|
68
|
+
fitToStage: (stage: HTMLElement) => void
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Move the content by a delta measured in client pixels.
|
|
72
|
+
*/
|
|
73
|
+
panBy: (dx: number, dy: number) => void
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Zoom around a specific client coordinate.
|
|
77
|
+
*
|
|
78
|
+
* @param clientX - Client X coordinate.
|
|
79
|
+
* @param clientY - Client Y coordinate.
|
|
80
|
+
* @param factor - Multiplicative zoom factor.
|
|
81
|
+
*/
|
|
82
|
+
zoomAt: (clientX: number, clientY: number, factor: number) => void
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Start a pinch gesture using two points in client coordinates.
|
|
86
|
+
*/
|
|
87
|
+
beginPinch: (points: [Point, Point]) => void
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update an active pinch gesture using two points in client coordinates.
|
|
91
|
+
*/
|
|
92
|
+
updatePinch: (points: [Point, Point]) => void
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Factory function used by createPreview to initialize a rendering adapter.
|
|
97
|
+
*/
|
|
98
|
+
export type PreviewAdapterFactory = (stage: HTMLElement) => PreviewAdapter
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Point, ViewBox } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clamp a number into the given range.
|
|
5
|
+
*/
|
|
6
|
+
export function clamp(n: number, min: number, max: number) {
|
|
7
|
+
return Math.min(max, Math.max(min, n))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Return whether a value is a finite number.
|
|
12
|
+
*/
|
|
13
|
+
export function isFiniteNumber(n: number) {
|
|
14
|
+
return Number.isFinite(n)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Compute the Euclidean distance between two points.
|
|
19
|
+
*/
|
|
20
|
+
export function dist(a: Point, b: Point) {
|
|
21
|
+
return Math.hypot(a.x - b.x, a.y - b.y)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Compute the midpoint between two points.
|
|
26
|
+
*/
|
|
27
|
+
export function mid(a: Point, b: Point) {
|
|
28
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read a safe client rect. Width and height are never returned as zero.
|
|
33
|
+
*/
|
|
34
|
+
export function safeRect(el: Element) {
|
|
35
|
+
const rect = el.getBoundingClientRect()
|
|
36
|
+
return {
|
|
37
|
+
left: rect.left,
|
|
38
|
+
top: rect.top,
|
|
39
|
+
width: rect.width || 1,
|
|
40
|
+
height: rect.height || 1,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse a numeric SVG/CSS length value.
|
|
46
|
+
* Returns null when the value is missing or invalid.
|
|
47
|
+
*/
|
|
48
|
+
export function parseLength(value: string | null): number | null {
|
|
49
|
+
if (!value) return null
|
|
50
|
+
const n = Number.parseFloat(value)
|
|
51
|
+
return Number.isFinite(n) && n > 0 ? n : null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Measure the base SVG size used to initialize the preview viewBox.
|
|
56
|
+
*/
|
|
57
|
+
export function measureSvgBaseViewBox(svg: SVGSVGElement): ViewBox {
|
|
58
|
+
const vb = svg.viewBox.baseVal
|
|
59
|
+
if (vb && vb.width > 0 && vb.height > 0) {
|
|
60
|
+
return { x: vb.x, y: vb.y, w: vb.width, h: vb.height }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const attrW = parseLength(svg.getAttribute('width'))
|
|
64
|
+
const attrH = parseLength(svg.getAttribute('height'))
|
|
65
|
+
if (attrW && attrH) {
|
|
66
|
+
return { x: 0, y: 0, w: attrW, h: attrH }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const bbox = svg.getBBox()
|
|
71
|
+
if (bbox.width > 0 && bbox.height > 0) {
|
|
72
|
+
return { x: bbox.x, y: bbox.y, w: bbox.width, h: bbox.height }
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Some SVGs may fail getBBox() before they are fully laid out.
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { x: 0, y: 0, w: 100, h: 100 }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Wait until an HTML image is fully loaded.
|
|
83
|
+
*/
|
|
84
|
+
export function waitForImageLoad(img: HTMLImageElement): Promise<void> {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
if (img.complete) {
|
|
87
|
+
if (img.naturalWidth > 0) {
|
|
88
|
+
resolve()
|
|
89
|
+
} else {
|
|
90
|
+
reject(new Error(`Image failed to load: ${img.currentSrc || img.src}`))
|
|
91
|
+
}
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
img.addEventListener('load', () => resolve(), { once: true })
|
|
96
|
+
img.addEventListener(
|
|
97
|
+
'error',
|
|
98
|
+
() => reject(new Error(`Image failed to load: ${img.currentSrc || img.src}`)),
|
|
99
|
+
{ once: true }
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
// File Layout
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
|
|
8
|
+
// Environment Settings
|
|
9
|
+
// See also https://aka.ms/tsconfig/module
|
|
10
|
+
"module": "ESNext",
|
|
11
|
+
"target": "es2022",
|
|
12
|
+
"lib": ["ES2022", "DOM"],
|
|
13
|
+
"types": [],
|
|
14
|
+
// For nodejs:
|
|
15
|
+
// "lib": ["esnext"],
|
|
16
|
+
// "types": ["node"],
|
|
17
|
+
// and npm install -D @types/node
|
|
18
|
+
|
|
19
|
+
// Other Outputs
|
|
20
|
+
"sourceMap": true,
|
|
21
|
+
"declaration": true,
|
|
22
|
+
"declarationMap": true,
|
|
23
|
+
|
|
24
|
+
// Stricter Typechecking Options
|
|
25
|
+
"noUncheckedIndexedAccess": true,
|
|
26
|
+
"exactOptionalPropertyTypes": true,
|
|
27
|
+
|
|
28
|
+
// Style Options
|
|
29
|
+
// "noImplicitReturns": true,
|
|
30
|
+
// "noImplicitOverride": true,
|
|
31
|
+
"noUnusedLocals": true,
|
|
32
|
+
// "noUnusedParameters": true,
|
|
33
|
+
"noFallthroughCasesInSwitch": true,
|
|
34
|
+
// "noPropertyAccessFromIndexSignature": true,
|
|
35
|
+
|
|
36
|
+
// Recommended Options
|
|
37
|
+
"strict": true,
|
|
38
|
+
"verbatimModuleSyntax": true,
|
|
39
|
+
"isolatedModules": true,
|
|
40
|
+
"noUncheckedSideEffectImports": true,
|
|
41
|
+
"moduleDetection": "force",
|
|
42
|
+
"skipLibCheck": true,
|
|
43
|
+
"moduleResolution": "bundler",
|
|
44
|
+
},
|
|
45
|
+
"include": ["src/**/*"],
|
|
46
|
+
"exclude": ["node_modules", "dist"]
|
|
47
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@tsconfig/node24/tsconfig.json",
|
|
3
|
+
"include": [
|
|
4
|
+
"rolldown.config.*",
|
|
5
|
+
"vite.config.*",
|
|
6
|
+
"vitest.config.*",
|
|
7
|
+
"cypress.config.*",
|
|
8
|
+
"nightwatch.conf.*",
|
|
9
|
+
"playwright.config.*",
|
|
10
|
+
"eslint.config.*"
|
|
11
|
+
],
|
|
12
|
+
"compilerOptions": {
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
15
|
+
|
|
16
|
+
"allowJs": true,
|
|
17
|
+
"checkJs": true,
|
|
18
|
+
"strict": false,
|
|
19
|
+
|
|
20
|
+
"module": "ESNext",
|
|
21
|
+
"moduleResolution": "Bundler",
|
|
22
|
+
"types": ["node"]
|
|
23
|
+
}
|
|
24
|
+
}
|