@hkdigital/lib-sveltekit 0.0.97 → 0.0.99
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/classes/svelte/image/ImageVariantsLoader.svelte.d.ts +9 -2
- package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +38 -31
- package/dist/components/image/EnhancedImage.svelte__ +162 -0
- package/dist/components/image/ImageBox.svelte +104 -145
- package/dist/components/image/ImageBox.svelte.d.ts +6 -49
- package/dist/components/image/ImageBox.svelte__ +242 -0
- package/dist/components/image/ImageBox.svelte___ +241 -0
- package/dist/components/image/ResponsiveImage.svelte +2 -14
- package/dist/components/image/ResponsiveImage.svelte__ +90 -0
- package/dist/components/image/index.d.ts +0 -1
- package/dist/components/image/index.js +0 -1
- package/dist/components/inputs/text-input/TextInput.svelte +2 -2
- package/dist/config/tailwind.extend.d.ts +7 -0
- package/dist/config/tailwind.extend.js +8 -0
- package/dist/constants/index.js +1 -1
- package/dist/constants/state-labels/submit-states.d.ts +4 -0
- package/dist/constants/state-labels/submit-states.js +4 -0
- package/dist/util/image/index.d.ts +16 -0
- package/dist/util/image/index.js +62 -0
- package/package.json +2 -2
- package/dist/constants/css-states/index.d.ts +0 -1
- package/dist/constants/css-states/index.js +0 -1
- /package/dist/constants/{css-states → state-labels}/input-states.d.ts +0 -0
- /package/dist/constants/{css-states → state-labels}/input-states.js +0 -0
@@ -8,9 +8,16 @@ export default class ImageVariantsLoader {
|
|
8
8
|
/**
|
9
9
|
* Set new optimal image variant or keep current
|
10
10
|
*
|
11
|
-
* @param {
|
11
|
+
* @param {object} params
|
12
|
+
* @param {number} [params.containerWidth] Container width
|
13
|
+
* @param {number} [params.containerHeight] Container height
|
14
|
+
* @param {'cover'|'contain'|'fill'} [params.fit='contain'] Fit mode
|
12
15
|
*/
|
13
|
-
updateOptimalImageMeta(containerWidth
|
16
|
+
updateOptimalImageMeta({ containerWidth, containerHeight, fit }: {
|
17
|
+
containerWidth?: number;
|
18
|
+
containerHeight?: number;
|
19
|
+
fit?: "cover" | "contain" | "fill";
|
20
|
+
}): void;
|
14
21
|
get loaded(): boolean;
|
15
22
|
get variant(): import("./typedef.js").ImageMeta;
|
16
23
|
/**
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
// import * as expect from '../../../util/expect/index.js';
|
4
4
|
|
5
|
+
import { calculateEffectiveWidth } from '../../../util/image/index.js';
|
6
|
+
|
5
7
|
import { untrack } from 'svelte';
|
6
8
|
|
7
9
|
import ImageLoader from './ImageLoader.svelte.js';
|
@@ -21,57 +23,60 @@ export default class ImageVariantsLoader {
|
|
21
23
|
|
22
24
|
#progress = $derived.by(() => {
|
23
25
|
if (this.#imageLoader) {
|
24
|
-
// const progress = this.#imageLoader.progress;
|
25
|
-
|
26
26
|
return this.#imageLoader.progress;
|
27
27
|
} else {
|
28
28
|
return { bytesLoaded: 0, size: 0, loaded: false };
|
29
29
|
}
|
30
30
|
});
|
31
31
|
|
32
|
-
#loaded = $derived(this.#progress
|
32
|
+
#loaded = $derived.by(() => this.#progress?.loaded || false);
|
33
33
|
|
34
34
|
/**
|
35
35
|
* @param {ImageMeta[]} imagesMeta
|
36
36
|
*/
|
37
37
|
constructor(imagesMeta, { devicePixelRatio = 1 } = {}) {
|
38
|
-
// expect.notEmptyArray( imagesMeta );
|
39
|
-
|
40
38
|
this.#devicePixelRatio = devicePixelRatio ?? 1;
|
41
|
-
|
42
|
-
// Sort images meta by width ascending
|
43
39
|
this.#imagesMeta = [...imagesMeta].sort((a, b) => a.width - b.width);
|
44
|
-
|
45
|
-
$effect(() => {
|
46
|
-
const variant = this.#imageVariant;
|
47
|
-
|
48
|
-
if (variant) {
|
49
|
-
// console.log('Load new variant', $state.snapshot(variant));
|
50
|
-
|
51
|
-
// TODO: abort loading if imageLoader exists
|
52
|
-
|
53
|
-
untrack(() => {
|
54
|
-
const loader = (this.#imageLoader = new ImageLoader({
|
55
|
-
url: variant.src
|
56
|
-
}));
|
57
|
-
|
58
|
-
loader.load();
|
59
|
-
});
|
60
|
-
}
|
61
|
-
});
|
62
40
|
}
|
63
41
|
|
64
42
|
/**
|
65
43
|
* Set new optimal image variant or keep current
|
66
44
|
*
|
67
|
-
* @param {
|
45
|
+
* @param {object} params
|
46
|
+
* @param {number} [params.containerWidth] Container width
|
47
|
+
* @param {number} [params.containerHeight] Container height
|
48
|
+
* @param {'cover'|'contain'|'fill'} [params.fit='contain'] Fit mode
|
68
49
|
*/
|
69
|
-
updateOptimalImageMeta(containerWidth) {
|
70
|
-
const
|
50
|
+
updateOptimalImageMeta({ containerWidth, containerHeight, fit = 'contain' }) {
|
51
|
+
const baseImage = this.#imagesMeta[0];
|
52
|
+
const imageAspectRatio = baseImage.width / baseImage.height;
|
53
|
+
|
54
|
+
const effectiveWidth = calculateEffectiveWidth({
|
55
|
+
containerWidth,
|
56
|
+
containerHeight,
|
57
|
+
imageAspectRatio,
|
58
|
+
fit
|
59
|
+
});
|
60
|
+
|
61
|
+
const newVariant = this.getOptimalImageMeta(effectiveWidth);
|
71
62
|
|
72
|
-
if (
|
73
|
-
|
63
|
+
if (
|
64
|
+
!newVariant ||
|
65
|
+
!this.#imageVariant ||
|
66
|
+
newVariant.width > this.#imageVariant.width
|
67
|
+
) {
|
74
68
|
this.#imageVariant = newVariant;
|
69
|
+
|
70
|
+
// Create and start loader here directly when variant changes
|
71
|
+
if (this.#imageLoader?.initial) {
|
72
|
+
this.#imageLoader.unload();
|
73
|
+
}
|
74
|
+
|
75
|
+
this.#imageLoader = new ImageLoader({
|
76
|
+
imageMeta: newVariant
|
77
|
+
});
|
78
|
+
|
79
|
+
this.#imageLoader.load();
|
75
80
|
}
|
76
81
|
}
|
77
82
|
|
@@ -137,7 +142,9 @@ export default class ImageVariantsLoader {
|
|
137
142
|
|
138
143
|
// Find the smallest image that's larger than our required width
|
139
144
|
|
140
|
-
const optimal = imagesMeta.find(
|
145
|
+
const optimal = imagesMeta.find(
|
146
|
+
(current) => current.width >= requiredWidth
|
147
|
+
);
|
141
148
|
|
142
149
|
// Fall back to the largest image if nothing is big enough
|
143
150
|
return optimal || imagesMeta[imagesMeta.length - 1];
|
@@ -0,0 +1,162 @@
|
|
1
|
+
<script>
|
2
|
+
import { onMount } from 'svelte';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @example
|
6
|
+
* import { EnhancedImage }
|
7
|
+
* from '$lib/components/EnhancedImage/index.js';
|
8
|
+
*
|
9
|
+
* <EnhancedImage
|
10
|
+
* classes="aspect-video max-h-svh w-full border-8 border-pink-500"
|
11
|
+
* src={NeonLightsOff}
|
12
|
+
* onload={() => {
|
13
|
+
* console.log('loaded');
|
14
|
+
* }}
|
15
|
+
* />
|
16
|
+
*/
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @type {{
|
20
|
+
* base?: string,
|
21
|
+
* bg?: string,
|
22
|
+
* classes?: string,
|
23
|
+
* overflow?: string,
|
24
|
+
* aspect?: string,
|
25
|
+
* fit?: string,
|
26
|
+
* position?: string,
|
27
|
+
* src: string | import('$lib/typedef/image.js').Picture,
|
28
|
+
* alt?: string,
|
29
|
+
* onload?: ( e: (Event|{ type: string, target: HTMLImageElement }) ) => void,
|
30
|
+
* onerror?: ( e: (Event|{ type: string, target: HTMLImageElement }) ) => void,
|
31
|
+
* loading?: string
|
32
|
+
* } & { [attr: string]: * }}
|
33
|
+
*/
|
34
|
+
let {
|
35
|
+
// Style
|
36
|
+
base,
|
37
|
+
bg,
|
38
|
+
classes,
|
39
|
+
|
40
|
+
overflow = 'overflow-clip',
|
41
|
+
aspect,
|
42
|
+
|
43
|
+
fit = 'contain',
|
44
|
+
position = 'left top',
|
45
|
+
|
46
|
+
src,
|
47
|
+
alt = '',
|
48
|
+
onload,
|
49
|
+
onerror,
|
50
|
+
loading,
|
51
|
+
|
52
|
+
// Attributes
|
53
|
+
...attrs
|
54
|
+
} = $props();
|
55
|
+
|
56
|
+
// let show = $state(false);
|
57
|
+
|
58
|
+
/** @type {HTMLDivElement|undefined} */
|
59
|
+
let imgBoxElem = $state();
|
60
|
+
|
61
|
+
/** @type {HTMLImageElement|undefined} */
|
62
|
+
let imgElem = $state();
|
63
|
+
|
64
|
+
/** @type {string | import('$lib/typedef/image.js').Picture} */
|
65
|
+
let src_;
|
66
|
+
|
67
|
+
$effect(() => {
|
68
|
+
if (src_ && src !== src_) {
|
69
|
+
throw new Error('Property [src] change is not supported');
|
70
|
+
}
|
71
|
+
});
|
72
|
+
|
73
|
+
let aspectStyle = $state('');
|
74
|
+
|
75
|
+
// > Event names
|
76
|
+
const LOAD = 'load';
|
77
|
+
const ERROR = 'error';
|
78
|
+
|
79
|
+
// > onMount
|
80
|
+
|
81
|
+
onMount(() => {
|
82
|
+
// show = true;
|
83
|
+
|
84
|
+
imgElem = imgBoxElem?.getElementsByTagName('img')[0];
|
85
|
+
|
86
|
+
if (!imgElem) {
|
87
|
+
throw new Error('Missing IMG element');
|
88
|
+
}
|
89
|
+
|
90
|
+
// > Auto set box aspect to same as image aspect if not set
|
91
|
+
|
92
|
+
if (!aspect) {
|
93
|
+
aspectStyle = `${imgElem.width / imgElem.height}`;
|
94
|
+
} else {
|
95
|
+
aspectStyle = '';
|
96
|
+
}
|
97
|
+
|
98
|
+
// > Register image onload and onerror handlers
|
99
|
+
|
100
|
+
/** @type {( e: Event ) => void} */
|
101
|
+
let onloadFn;
|
102
|
+
|
103
|
+
if (onload) {
|
104
|
+
if (imgElem?.complete) {
|
105
|
+
onload({ type: LOAD, target: imgElem });
|
106
|
+
} else {
|
107
|
+
onloadFn = onload;
|
108
|
+
imgElem?.addEventListener(LOAD, onloadFn);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
/** @type {( e: Event ) => void} */
|
113
|
+
let onerrorFn;
|
114
|
+
|
115
|
+
if (onerror) {
|
116
|
+
onerrorFn = onerror;
|
117
|
+
imgElem?.addEventListener(ERROR, onerrorFn);
|
118
|
+
}
|
119
|
+
|
120
|
+
return () => {
|
121
|
+
if (onloadFn) {
|
122
|
+
imgElem?.removeEventListener(LOAD, onloadFn);
|
123
|
+
}
|
124
|
+
|
125
|
+
if (onerrorFn) {
|
126
|
+
imgElem?.removeEventListener(ERROR, onerrorFn);
|
127
|
+
}
|
128
|
+
};
|
129
|
+
});
|
130
|
+
</script>
|
131
|
+
|
132
|
+
<div
|
133
|
+
data-hk-enhanced-image
|
134
|
+
bind:this={imgBoxElem}
|
135
|
+
class="{base} {bg} {aspect} {overflow} {classes}"
|
136
|
+
style:--fit={fit}
|
137
|
+
style:--pos={position}
|
138
|
+
style:aspect-ratio={aspectStyle}
|
139
|
+
{...attrs}
|
140
|
+
>
|
141
|
+
<enhanced:img {src} {alt} />
|
142
|
+
</div>
|
143
|
+
|
144
|
+
<style>
|
145
|
+
[data-hk-enhanced-image],
|
146
|
+
:global([data-hk-enhanced-image] picture),
|
147
|
+
:global([data-hk-enhanced-image] img) {
|
148
|
+
display: block;
|
149
|
+
width: 100%;
|
150
|
+
height: 100%;
|
151
|
+
}
|
152
|
+
:global([data-hk-enhanced-image] picture),
|
153
|
+
:global([data-hk-enhanced-image] img) {
|
154
|
+
max-width: 100%;
|
155
|
+
max-height: 100%;
|
156
|
+
}
|
157
|
+
|
158
|
+
:global([data-hk-enhanced-image] img) {
|
159
|
+
object-fit: var(--fit);
|
160
|
+
object-position: var(--pos);
|
161
|
+
}
|
162
|
+
</style>
|
@@ -1,93 +1,26 @@
|
|
1
1
|
<script>
|
2
|
-
import { onMount } from 'svelte';
|
3
|
-
|
4
2
|
import { ImageLoader } from '../../classes/svelte/image/index.js';
|
5
|
-
|
3
|
+
import { ImageVariantsLoader } from '../../classes/svelte/image/index.js';
|
6
4
|
import { toSingleImageMeta } from '../../util/image/index.js';
|
7
5
|
|
8
6
|
/**
|
9
|
-
* @
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
*
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
* fit="contain"
|
26
|
-
* position="center center"
|
27
|
-
* width="w-[200px]"
|
28
|
-
* height="h-[200px]"
|
29
|
-
* classes="border-8 border-green-500"
|
30
|
-
* />
|
31
|
-
*
|
32
|
-
* <ImageBox
|
33
|
-
* image={ArmyGreen}
|
34
|
-
* fit="contain"
|
35
|
-
* position="center center"
|
36
|
-
* width="w-[200px]"
|
37
|
-
* aspect="aspect-square"
|
38
|
-
* classes="border-8 border-green-500"
|
39
|
-
* />
|
40
|
-
*
|
41
|
-
* <ImageBox
|
42
|
-
* image={ArmyGreen}
|
43
|
-
* fit="contain"
|
44
|
-
* position="center center"
|
45
|
-
* height="h-[200px]"
|
46
|
-
* aspect="aspect-square"
|
47
|
-
* classes="border-8 border-green-500"
|
48
|
-
* />
|
49
|
-
*
|
50
|
-
* <!-- Or hack it using !important -->
|
51
|
-
*
|
52
|
-
* <ImageBox
|
53
|
-
* image={ArmyGreen}
|
54
|
-
* fit="contain"
|
55
|
-
* position="center center"
|
56
|
-
* classes="!w-[200px] !h-[200px] border-8 border-green-500"
|
57
|
-
* />
|
7
|
+
* @type {{
|
8
|
+
* base?: string,
|
9
|
+
* bg?: string,
|
10
|
+
* classes?: string,
|
11
|
+
* width?: string,
|
12
|
+
* height?: string,
|
13
|
+
* aspect?: string,
|
14
|
+
* overflow?: string,
|
15
|
+
* fit?: 'contain' | 'cover' | 'fill',
|
16
|
+
* position?: string,
|
17
|
+
* imageMeta: import('../../config/typedef.js').ImageMeta | import('../../config/typedef.js').ImageMeta[],
|
18
|
+
* imageLoader?: import('../../classes/svelte/image/index.js').ImageLoader,
|
19
|
+
* alt?: string,
|
20
|
+
* onProgress?: (progress: import('../../classes/svelte/network-loader/typedef.js').LoadingProgress) => void,
|
21
|
+
* [attr: string]: any
|
22
|
+
* }}
|
58
23
|
*/
|
59
|
-
|
60
|
-
/**
|
61
|
-
* @typedef {import('./typedef.js').ObjectFit} ObjectFit
|
62
|
-
* @typedef {import('./typedef.js').ObjectPosition} ObjectPosition
|
63
|
-
*
|
64
|
-
* @typedef {import('../../classes/svelte/network-loader/typedef.js').LoadingProgress} LoadingProgress
|
65
|
-
*
|
66
|
-
* @typedef {import('../../config/typedef.js').ImageMeta} ImageMeta
|
67
|
-
*
|
68
|
-
* @typedef {Object} Props
|
69
|
-
* @property {string} [base] - Base styling class
|
70
|
-
* @property {string} [bg] - Background styling class
|
71
|
-
* @property {string} [classes] - Additional CSS classes
|
72
|
-
* @property {string} [width] - Width of the image container
|
73
|
-
* @property {string} [height] - Height of the image container
|
74
|
-
* @property {string} [aspect] - Aspect ratio of the image container
|
75
|
-
* @property {string} [overflow] - Overflow behavior
|
76
|
-
* @property {ObjectFit} [fit] - Object-fit property
|
77
|
-
* @property {ObjectPosition} [position] - Object-position property
|
78
|
-
*
|
79
|
-
* @property {ImageMeta|ImageMeta[]} [imageMeta]
|
80
|
-
* Image metadata, TODO: array of image metadata for responsive image
|
81
|
-
*
|
82
|
-
* @property {ImageLoader} [imageLoader]
|
83
|
-
* Image loader
|
84
|
-
*
|
85
|
-
* @property {string} [alt] - Alternative text for the image
|
86
|
-
* @property {() => LoadingProgress} [onProgress] - Progress callback function
|
87
|
-
* @property {*} [attr] - Additional arbitrary attributes
|
88
|
-
*/
|
89
|
-
|
90
|
-
/** @type {Props} */
|
91
24
|
let {
|
92
25
|
// Style
|
93
26
|
base,
|
@@ -95,111 +28,101 @@
|
|
95
28
|
classes,
|
96
29
|
width,
|
97
30
|
height,
|
98
|
-
|
31
|
+
aspect,
|
99
32
|
overflow = 'overflow-clip',
|
100
33
|
|
101
|
-
|
34
|
+
// Fitting and positioning of image in its container
|
102
35
|
fit = 'contain',
|
103
36
|
position = 'left top',
|
104
37
|
|
105
|
-
|
38
|
+
// Image data
|
106
39
|
imageMeta,
|
107
|
-
|
108
40
|
imageLoader,
|
109
41
|
|
42
|
+
// Accessibility
|
110
43
|
alt = '',
|
111
44
|
|
112
|
-
//
|
45
|
+
// Events
|
46
|
+
onProgress,
|
47
|
+
|
48
|
+
// Additional attributes
|
113
49
|
...attrs
|
114
50
|
} = $props();
|
115
51
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
}
|
120
|
-
|
121
|
-
// let show = $state(false);
|
52
|
+
if (!imageMeta) {
|
53
|
+
throw new Error('Missing [imageMeta]');
|
54
|
+
}
|
122
55
|
|
123
56
|
/** @type {HTMLDivElement|undefined} */
|
124
|
-
let
|
125
|
-
|
126
|
-
/** @type {HTMLImageElement|undefined} */
|
127
|
-
let imgElem = $state();
|
57
|
+
let containerElem = $state();
|
128
58
|
|
129
|
-
let
|
130
|
-
|
131
|
-
|
59
|
+
let imageMeta_ = $state();
|
60
|
+
let variantsLoader = $state();
|
61
|
+
let variantObjectUrl = $state(null);
|
62
|
+
let objectUrl = $state(null);
|
132
63
|
|
64
|
+
// For single image meta
|
133
65
|
let metaWidth = $state(0);
|
134
66
|
let metaHeight = $state(0);
|
135
67
|
|
136
|
-
|
68
|
+
/** @type {ImageLoader|undefined} */
|
69
|
+
let imageLoader_ = $state();
|
137
70
|
|
138
71
|
$effect(() => {
|
139
|
-
|
140
|
-
{
|
141
|
-
|
72
|
+
// Setup variants loader for responsive images
|
73
|
+
if (Array.isArray(imageMeta) && !imageLoader && !variantsLoader) {
|
74
|
+
variantsLoader = new ImageVariantsLoader(imageMeta, {
|
75
|
+
devicePixelRatio: window.devicePixelRatio
|
76
|
+
});
|
77
|
+
}
|
78
|
+
// Handle single image meta
|
79
|
+
else if (imageMeta && !variantsLoader) {
|
80
|
+
imageMeta_ = toSingleImageMeta(imageMeta);
|
142
81
|
}
|
143
82
|
});
|
144
83
|
|
84
|
+
// Handle progress reporting
|
145
85
|
$effect(() => {
|
146
|
-
|
147
|
-
// Set meta width and height
|
148
|
-
//
|
149
|
-
if (imageMeta_) {
|
150
|
-
if (imageMeta_.width) {
|
151
|
-
metaWidth = imageMeta_.width;
|
152
|
-
}
|
86
|
+
if (!onProgress) return;
|
153
87
|
|
154
|
-
|
155
|
-
|
156
|
-
|
88
|
+
// Report progress from variants loader
|
89
|
+
if (variantsLoader) {
|
90
|
+
onProgress(variantsLoader.progress);
|
91
|
+
}
|
92
|
+
// Report progress from single image loader
|
93
|
+
else if (imageLoader_) {
|
94
|
+
onProgress(imageLoader_.progress);
|
157
95
|
}
|
158
96
|
});
|
159
97
|
|
160
|
-
|
161
|
-
|
98
|
+
$effect(() => {
|
99
|
+
if (imageMeta_) {
|
100
|
+
metaWidth = imageMeta_.width ?? 0;
|
101
|
+
metaHeight = imageMeta_.height ?? 0;
|
102
|
+
}
|
103
|
+
});
|
162
104
|
|
163
|
-
$effect(
|
164
|
-
|
165
|
-
// User supplied imageLoader instead of imageMeta
|
166
|
-
//
|
167
|
-
if( !imageMeta && imageLoader && !imageLoader_ )
|
168
|
-
{
|
105
|
+
$effect(() => {
|
106
|
+
if (!imageMeta && imageLoader && !imageLoader_) {
|
169
107
|
imageLoader_ = imageLoader;
|
170
108
|
imageMeta_ = imageLoader.imageMeta;
|
171
109
|
}
|
172
|
-
}
|
173
|
-
|
174
|
-
/** @type {string|null} */
|
175
|
-
let objectUrl = $state(null);
|
110
|
+
});
|
176
111
|
|
177
112
|
$effect(() => {
|
178
|
-
//
|
179
|
-
// Create image loader
|
180
|
-
//
|
181
113
|
if (imageMeta_ && !imageLoader_) {
|
182
114
|
imageLoader_ = new ImageLoader({ imageMeta: imageMeta_ });
|
183
115
|
}
|
184
116
|
});
|
185
117
|
|
186
118
|
$effect(() => {
|
187
|
-
//
|
188
|
-
// Start loading if imageLoader_ is in state 'initial'
|
189
|
-
//
|
190
|
-
// TODO: implement lazy flag
|
191
|
-
//
|
192
119
|
if (imageLoader_?.initial) {
|
193
120
|
imageLoader_.load();
|
194
121
|
}
|
195
122
|
});
|
196
123
|
|
197
124
|
$effect(() => {
|
198
|
-
|
199
|
-
// Get objectUrl when the image has finished loading
|
200
|
-
//
|
201
|
-
if (imageLoader_.loaded) {
|
202
|
-
// @ts-ignore
|
125
|
+
if (imageLoader_?.loaded) {
|
203
126
|
objectUrl = imageLoader_.getObjectURL();
|
204
127
|
}
|
205
128
|
|
@@ -211,26 +134,62 @@
|
|
211
134
|
};
|
212
135
|
});
|
213
136
|
|
137
|
+
$effect(() => {
|
138
|
+
if (!containerElem || !variantsLoader) return;
|
139
|
+
|
140
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
141
|
+
for (const entry of entries) {
|
142
|
+
const { width, height } = entry.contentRect;
|
143
|
+
variantsLoader.updateOptimalImageMeta({
|
144
|
+
containerWidth: width,
|
145
|
+
containerHeight: height,
|
146
|
+
fit
|
147
|
+
});
|
148
|
+
}
|
149
|
+
});
|
150
|
+
|
151
|
+
resizeObserver.observe(containerElem);
|
152
|
+
return () => resizeObserver.disconnect();
|
153
|
+
});
|
154
|
+
|
155
|
+
$effect(() => {
|
156
|
+
if (variantsLoader?.loaded) {
|
157
|
+
variantObjectUrl = variantsLoader.getObjectURL();
|
158
|
+
}
|
159
|
+
|
160
|
+
return () => {
|
161
|
+
if (variantObjectUrl) {
|
162
|
+
URL.revokeObjectURL(variantObjectUrl);
|
163
|
+
variantObjectUrl = null;
|
164
|
+
}
|
165
|
+
};
|
166
|
+
});
|
214
167
|
</script>
|
215
168
|
|
216
169
|
<div
|
217
170
|
data-image="box"
|
218
|
-
bind:this={
|
171
|
+
bind:this={containerElem}
|
219
172
|
class="{base} {bg} {aspect} {overflow} {width} {height} {classes}"
|
220
173
|
style:--fit={fit}
|
221
174
|
style:--pos={position}
|
222
|
-
style:aspect-ratio={aspectStyle}
|
223
175
|
style:width={width || (height && aspect) ? undefined : '100%'}
|
224
176
|
style:height={height || (width && aspect) ? undefined : '100%'}
|
225
177
|
{...attrs}
|
226
178
|
>
|
227
|
-
{#if
|
179
|
+
{#if variantsLoader?.loaded && variantObjectUrl}
|
180
|
+
<img
|
181
|
+
src={variantObjectUrl}
|
182
|
+
{alt}
|
183
|
+
width={variantsLoader.variant.width}
|
184
|
+
height={variantsLoader.variant.height}
|
185
|
+
/>
|
186
|
+
{:else if objectUrl && metaWidth && metaHeight}
|
228
187
|
<img src={objectUrl} {alt} width={metaWidth} height={metaHeight} />
|
229
188
|
{/if}
|
230
189
|
</div>
|
231
190
|
|
232
191
|
<style>
|
233
|
-
[data-image=
|
192
|
+
[data-image='box'] {
|
234
193
|
max-width: 100%;
|
235
194
|
max-height: 100%;
|
236
195
|
}
|
@@ -1,60 +1,17 @@
|
|
1
1
|
export default ImageBox;
|
2
2
|
declare const ImageBox: import("svelte").Component<{
|
3
|
-
|
4
|
-
* - Base styling class
|
5
|
-
*/
|
3
|
+
[attr: string]: any;
|
6
4
|
base?: string;
|
7
|
-
/**
|
8
|
-
* - Background styling class
|
9
|
-
*/
|
10
5
|
bg?: string;
|
11
|
-
/**
|
12
|
-
* - Additional CSS classes
|
13
|
-
*/
|
14
6
|
classes?: string;
|
15
|
-
/**
|
16
|
-
* - Width of the image container
|
17
|
-
*/
|
18
7
|
width?: string;
|
19
|
-
/**
|
20
|
-
* - Height of the image container
|
21
|
-
*/
|
22
8
|
height?: string;
|
23
|
-
/**
|
24
|
-
* - Aspect ratio of the image container
|
25
|
-
*/
|
26
9
|
aspect?: string;
|
27
|
-
/**
|
28
|
-
* - Overflow behavior
|
29
|
-
*/
|
30
10
|
overflow?: string;
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
/**
|
36
|
-
* - Object-position property
|
37
|
-
*/
|
38
|
-
position?: import("./typedef.js").ObjectPosition;
|
39
|
-
/**
|
40
|
-
* Image metadata, TODO: array of image metadata for responsive image
|
41
|
-
*/
|
42
|
-
imageMeta?: import("../../config/typedef.js").ImageMeta | import("../../config/typedef.js").ImageMeta[];
|
43
|
-
/**
|
44
|
-
* Image loader
|
45
|
-
*/
|
46
|
-
imageLoader?: ImageLoader;
|
47
|
-
/**
|
48
|
-
* - Alternative text for the image
|
49
|
-
*/
|
11
|
+
fit?: "contain" | "cover" | "fill";
|
12
|
+
position?: string;
|
13
|
+
imageMeta: import("../../config/typedef.js").ImageMeta | import("../../config/typedef.js").ImageMeta[];
|
14
|
+
imageLoader?: import("../../classes/svelte/image/index.js").ImageLoader;
|
50
15
|
alt?: string;
|
51
|
-
|
52
|
-
* - Progress callback function
|
53
|
-
*/
|
54
|
-
onProgress?: () => import("../../classes/svelte/network-loader/typedef.js").LoadingProgress;
|
55
|
-
/**
|
56
|
-
* - Additional arbitrary attributes
|
57
|
-
*/
|
58
|
-
attr?: any;
|
16
|
+
onProgress?: (progress: import("../../classes/svelte/network-loader/typedef.js").LoadingProgress) => void;
|
59
17
|
}, {}, "">;
|
60
|
-
import { ImageLoader } from '../../classes/svelte/image/index.js';
|