@picked/svelte 0.0.1-dev.1764096269522 → 0.0.1-dev.1764287278696
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/dist/components/LazyImage.svelte +196 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLImgAttributes } from 'svelte/elements'
|
|
3
|
+
import { onMount } from 'svelte'
|
|
4
|
+
|
|
5
|
+
type CommonProps = Omit<HTMLImgAttributes, 'src' | 'loading' | 'width' | 'height'> & {
|
|
6
|
+
src: string
|
|
7
|
+
loading?: 'eager' | 'lazy'
|
|
8
|
+
alt: string
|
|
9
|
+
sizes?: string
|
|
10
|
+
objectFit?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type FillProps = {
|
|
14
|
+
fill: true
|
|
15
|
+
width?: never
|
|
16
|
+
height?: never
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type FixedSizeProps = {
|
|
20
|
+
fill?: false
|
|
21
|
+
width: number
|
|
22
|
+
height: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Props = (CommonProps & FillProps) | (CommonProps & FixedSizeProps)
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
src,
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
loading = 'lazy',
|
|
32
|
+
alt,
|
|
33
|
+
sizes = '100vw',
|
|
34
|
+
objectFit = 'cover',
|
|
35
|
+
fill = false,
|
|
36
|
+
...restProps
|
|
37
|
+
}: Props = $props()
|
|
38
|
+
|
|
39
|
+
let imgElement = $state<HTMLImageElement | undefined>(undefined)
|
|
40
|
+
let placeholderElement = $state<HTMLDivElement | undefined>(undefined)
|
|
41
|
+
let isLoaded = $state(false)
|
|
42
|
+
let isInView = $state(false)
|
|
43
|
+
let observer: IntersectionObserver | undefined
|
|
44
|
+
|
|
45
|
+
const aspectRatio = $derived.by(() =>
|
|
46
|
+
!fill && typeof width === 'number' && typeof height === 'number' && height !== 0
|
|
47
|
+
? width / height
|
|
48
|
+
: undefined,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const SUPPORTED_SIZES = [
|
|
52
|
+
16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840,
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
function findClosestSize(targetSize: number): number {
|
|
56
|
+
const roundedTargetSize = Math.round(targetSize)
|
|
57
|
+
return SUPPORTED_SIZES.reduce(
|
|
58
|
+
(prev, curr) =>
|
|
59
|
+
Math.abs(curr - roundedTargetSize) < Math.abs(prev - roundedTargetSize) ? curr : prev,
|
|
60
|
+
SUPPORTED_SIZES[0],
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isConvincelyDomain(url: URL): boolean {
|
|
65
|
+
try {
|
|
66
|
+
return url.hostname.endsWith('.convincely.io')
|
|
67
|
+
} catch {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isShopifyDomain(url: URL): boolean {
|
|
73
|
+
try {
|
|
74
|
+
return url.hostname === 'cdn.shopify.com'
|
|
75
|
+
} catch {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getOptimizedImageUrl(imageSrc: string, targetWidth: number): string {
|
|
81
|
+
try {
|
|
82
|
+
const url = new URL(imageSrc, import.meta.url)
|
|
83
|
+
if (isConvincelyDomain(url)) {
|
|
84
|
+
if (
|
|
85
|
+
import.meta.env.VERCEL_DEPLOYMENT_ID &&
|
|
86
|
+
import.meta.env.VERCEL_SKEW_PROTECTION_ENABLED === '1'
|
|
87
|
+
) {
|
|
88
|
+
url.searchParams.set('dpl', import.meta.env.VERCEL_DEPLOYMENT_ID)
|
|
89
|
+
}
|
|
90
|
+
const encodedUrl = encodeURIComponent(url.toString())
|
|
91
|
+
const optimizedSize = findClosestSize(targetWidth)
|
|
92
|
+
return `https://img.convincely.io/_next/image?url=${encodedUrl}&w=${optimizedSize}&q=75`
|
|
93
|
+
}
|
|
94
|
+
if (isShopifyDomain(url)) {
|
|
95
|
+
const optimizedSize = findClosestSize(targetWidth)
|
|
96
|
+
url.searchParams.set('width', optimizedSize.toString())
|
|
97
|
+
return url.toString()
|
|
98
|
+
}
|
|
99
|
+
return getImgUrl(imageSrc)
|
|
100
|
+
} catch {
|
|
101
|
+
return getImgUrl(imageSrc)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getImgUrl(imageSrc: string): string {
|
|
106
|
+
if (imageSrc.startsWith('data:')) {
|
|
107
|
+
return imageSrc
|
|
108
|
+
}
|
|
109
|
+
const baseUrl = new URL(import.meta.url).origin
|
|
110
|
+
return imageSrc.startsWith('http') ? imageSrc : `${baseUrl}${imageSrc}`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleLoad() {
|
|
114
|
+
isLoaded = true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onMount(() => {
|
|
118
|
+
if (loading === 'lazy') {
|
|
119
|
+
const elementToObserve = placeholderElement || imgElement
|
|
120
|
+
if (elementToObserve) {
|
|
121
|
+
observer = new IntersectionObserver(
|
|
122
|
+
(entries) => {
|
|
123
|
+
entries.forEach((entry) => {
|
|
124
|
+
if (entry.isIntersecting) {
|
|
125
|
+
isInView = true
|
|
126
|
+
observer?.unobserve(entry.target)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
rootMargin: '50px',
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
observer.observe(elementToObserve)
|
|
135
|
+
return () => {
|
|
136
|
+
observer?.disconnect()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} else if (loading === 'eager') {
|
|
140
|
+
isInView = true
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const shouldLoadImage = $derived(loading === 'eager' || isInView)
|
|
145
|
+
|
|
146
|
+
const widthCandidates = $derived.by(() => {
|
|
147
|
+
if (typeof width === 'number' && Number.isFinite(width)) {
|
|
148
|
+
const filtered = SUPPORTED_SIZES.filter((w) => w <= width)
|
|
149
|
+
return filtered.length ? filtered : [width]
|
|
150
|
+
}
|
|
151
|
+
return SUPPORTED_SIZES
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const srcSet = $derived.by(() =>
|
|
155
|
+
widthCandidates.map((w) => `${getOptimizedImageUrl(src, w)} ${w}w`).join(', '),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
const fallbackSrc = $derived.by(() =>
|
|
159
|
+
getOptimizedImageUrl(src, widthCandidates[0]),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const fadeClasses = $derived.by(() => {
|
|
163
|
+
if (loading === 'eager') return ''
|
|
164
|
+
return `transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`
|
|
165
|
+
})
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
{#if shouldLoadImage}
|
|
169
|
+
<img
|
|
170
|
+
{...restProps}
|
|
171
|
+
bind:this={imgElement}
|
|
172
|
+
src={fallbackSrc}
|
|
173
|
+
srcset={srcSet}
|
|
174
|
+
{sizes}
|
|
175
|
+
width={fill ? undefined : width}
|
|
176
|
+
height={fill ? undefined : height}
|
|
177
|
+
{alt}
|
|
178
|
+
style:aspect-ratio={aspectRatio}
|
|
179
|
+
style:object-fit={objectFit}
|
|
180
|
+
style:position={fill ? 'absolute' : undefined}
|
|
181
|
+
style:inset={fill ? 0 : undefined}
|
|
182
|
+
{loading}
|
|
183
|
+
onload={handleLoad}
|
|
184
|
+
class="{fadeClasses} {restProps.class || ''}"
|
|
185
|
+
data-testid="lazy-image-element"
|
|
186
|
+
/>
|
|
187
|
+
{:else}
|
|
188
|
+
<div
|
|
189
|
+
bind:this={placeholderElement}
|
|
190
|
+
style:aspect-ratio={aspectRatio}
|
|
191
|
+
style:position={fill ? 'absolute' : undefined}
|
|
192
|
+
style:inset={fill ? 0 : undefined}
|
|
193
|
+
aria-label={alt}
|
|
194
|
+
data-testid="lazy-image-placeholder"
|
|
195
|
+
></div>
|
|
196
|
+
{/if}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as LazyImage } from './LazyImage.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{default as f}from"./LazyImage.svelte";export{f as LazyImage};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{adaptSvelteComponent as
|
|
1
|
+
import{adaptSvelteComponent as o,isValidAdaptedComponent as t}from"./component.js";import{default as d}from"./components/LazyImage.svelte";export{d as LazyImage,o as adaptSvelteComponent,t as isValidAdaptedComponent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.1764287278696",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
],
|
|
72
72
|
"module": "./dist/index.js",
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@picked/client": "0.0.1-dev.
|
|
74
|
+
"@picked/client": "0.0.1-dev.1764287278696",
|
|
75
75
|
"effector": "^23.3.0",
|
|
76
76
|
"svelte": "^5.34.3"
|
|
77
77
|
},
|