@hkdigital/lib-sveltekit 0.0.91 → 0.0.93
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/ImageLoader.svelte.d.ts +10 -0
- package/dist/classes/svelte/image/ImageLoader.svelte.js +20 -18
- package/dist/classes/svelte/image/ImageScene.svelte.d.ts +78 -0
- package/dist/classes/svelte/image/ImageScene.svelte.js +256 -0
- package/dist/classes/svelte/network-loader/NetworkLoader.svelte.d.ts +2 -2
- package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +2 -4
- package/dist/components/image/ImageBox.svelte +41 -28
- package/dist/components/image/ImageBox.svelte.d.ts +7 -2
- package/dist/components/image/ResponsiveImage.svelte +1 -1
- package/dist/components/image/ResponsiveImage.svelte.d.ts +1 -1
- package/dist/components/image/index.d.ts +2 -1
- package/dist/components/image/index.js +2 -1
- package/dist/config/imagetools.d.ts +0 -6
- package/dist/util/image/index.d.ts +9 -0
- package/dist/util/image/index.js +24 -0
- package/package.json +8 -8
- package/dist/components/image/EnhancedImage.svelte__ +0 -162
@@ -4,6 +4,16 @@
|
|
4
4
|
* - The loading process can be monitored
|
5
5
|
*/
|
6
6
|
export default class ImageLoader extends NetworkLoader {
|
7
|
+
/**
|
8
|
+
* @param {object} _
|
9
|
+
* @param {ImageMeta|ImageMeta[]} _.imageMeta
|
10
|
+
*/
|
11
|
+
constructor({ imageMeta }: {
|
12
|
+
imageMeta: ImageMeta | ImageMeta[];
|
13
|
+
});
|
14
|
+
get imageMeta(): import("./typedef.js").ImageMeta;
|
7
15
|
get url(): string;
|
16
|
+
#private;
|
8
17
|
}
|
18
|
+
export type ImageMeta = import("./typedef.js").ImageMeta;
|
9
19
|
import { NetworkLoader } from '../network-loader/index.js';
|
@@ -1,3 +1,7 @@
|
|
1
|
+
/** @typedef {import('./typedef.js').ImageMeta} ImageMeta */
|
2
|
+
|
3
|
+
import { toSingleImageMeta } from '../../../util/image/index.js';
|
4
|
+
|
1
5
|
import {
|
2
6
|
NetworkLoader
|
3
7
|
// ERROR_NOT_LOADED,
|
@@ -10,31 +14,29 @@ import {
|
|
10
14
|
* - The loading process can be monitored
|
11
15
|
*/
|
12
16
|
export default class ImageLoader extends NetworkLoader {
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
/** @type {ImageMeta} */
|
18
|
+
#imageMeta;
|
19
|
+
|
20
|
+
/**
|
21
|
+
* @param {object} _
|
22
|
+
* @param {ImageMeta|ImageMeta[]} _.imageMeta
|
23
|
+
*/
|
24
|
+
constructor({ imageMeta }) {
|
25
|
+
imageMeta = toSingleImageMeta(imageMeta);
|
16
26
|
|
17
|
-
|
18
|
-
// super({ url });
|
27
|
+
super({ url: imageMeta.src });
|
19
28
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
this.#imageMeta = imageMeta;
|
30
|
+
}
|
31
|
+
|
32
|
+
get imageMeta() {
|
33
|
+
return this.#imageMeta;
|
34
|
+
}
|
24
35
|
|
25
36
|
get url() {
|
26
37
|
return this._url;
|
27
38
|
}
|
28
39
|
|
29
|
-
//
|
30
|
-
// /**
|
31
|
-
// * Construct ImageLoader
|
32
|
-
// *
|
33
|
-
// * @param {object} _
|
34
|
-
// * @param {string} _.url
|
35
|
-
// */
|
36
|
-
// constructor( { url } ) {}
|
37
|
-
//
|
38
40
|
// /**
|
39
41
|
// * Get object URL that can be used as src parameter of an HTML image
|
40
42
|
// *
|
@@ -0,0 +1,78 @@
|
|
1
|
+
/**
|
2
|
+
* @typedef {object} SourceConfig
|
3
|
+
* // property ...
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* @typedef {object} ImageSource
|
7
|
+
* @property {string} label
|
8
|
+
* @property {ImageLoader} imageLoader
|
9
|
+
* @property {ImageMeta} [imageMeta]
|
10
|
+
*/
|
11
|
+
export default class ImageScene {
|
12
|
+
state: string;
|
13
|
+
loaded: boolean;
|
14
|
+
destroy(): void;
|
15
|
+
/**
|
16
|
+
* Add image source
|
17
|
+
* - Uses an ImageLoader instance to load image data from network
|
18
|
+
*
|
19
|
+
* @param {object} _
|
20
|
+
* @param {string} _.label
|
21
|
+
* @param {ImageMeta|ImageMeta[]} _.imageMeta
|
22
|
+
*/
|
23
|
+
defineImage({ label, imageMeta }: {
|
24
|
+
label: string;
|
25
|
+
imageMeta: ImageMeta | ImageMeta[];
|
26
|
+
}): void;
|
27
|
+
/**
|
28
|
+
* Start loading all image sources
|
29
|
+
*/
|
30
|
+
load(): void;
|
31
|
+
/**
|
32
|
+
* Get image scene loading progress
|
33
|
+
*/
|
34
|
+
get progress(): {
|
35
|
+
totalBytesLoaded: number;
|
36
|
+
totalSize: number;
|
37
|
+
sourcesLoaded: number;
|
38
|
+
numberOfSources: number;
|
39
|
+
};
|
40
|
+
/**
|
41
|
+
* Get an image loader
|
42
|
+
*
|
43
|
+
* @param {string} label
|
44
|
+
*
|
45
|
+
* @returns {ImageLoader}
|
46
|
+
*/
|
47
|
+
getImageLoader(label: string): ImageLoader;
|
48
|
+
/**
|
49
|
+
* Get object URL that can be used as src parameter of an HTML image
|
50
|
+
*
|
51
|
+
* @param {string} label
|
52
|
+
*
|
53
|
+
* @returns {ImageMeta}
|
54
|
+
*/
|
55
|
+
getImageMeta(label: string): ImageMeta;
|
56
|
+
/**
|
57
|
+
* Get object URL that can be used as src parameter of an HTML image
|
58
|
+
*
|
59
|
+
* @param {string} label
|
60
|
+
*
|
61
|
+
* @note the objectURL should be revoked when no longer used
|
62
|
+
*
|
63
|
+
* @returns {string}
|
64
|
+
*/
|
65
|
+
getObjectURL(label: string): string;
|
66
|
+
#private;
|
67
|
+
}
|
68
|
+
export type ImageMeta = import("./typedef.js").ImageMeta;
|
69
|
+
/**
|
70
|
+
* // property ...
|
71
|
+
*/
|
72
|
+
export type SourceConfig = object;
|
73
|
+
export type ImageSource = {
|
74
|
+
label: string;
|
75
|
+
imageLoader: ImageLoader;
|
76
|
+
imageMeta?: ImageMeta;
|
77
|
+
};
|
78
|
+
import ImageLoader from './ImageLoader.svelte.js';
|
@@ -0,0 +1,256 @@
|
|
1
|
+
/** @typedef {import('./typedef.js').ImageMeta} ImageMeta */
|
2
|
+
|
3
|
+
import * as expect from '../../../util/expect/index.js';
|
4
|
+
|
5
|
+
import {
|
6
|
+
LoadingStateMachine,
|
7
|
+
STATE_INITIAL,
|
8
|
+
STATE_LOADING,
|
9
|
+
STATE_UNLOADING,
|
10
|
+
STATE_LOADED,
|
11
|
+
STATE_CANCELLED,
|
12
|
+
STATE_ERROR,
|
13
|
+
LOAD,
|
14
|
+
// CANCEL,
|
15
|
+
ERROR,
|
16
|
+
LOADED,
|
17
|
+
UNLOAD,
|
18
|
+
INITIAL
|
19
|
+
} from '../loading-state-machine/index.js';
|
20
|
+
|
21
|
+
import ImageLoader from './ImageLoader.svelte.js';
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @typedef {object} SourceConfig
|
25
|
+
* // property ...
|
26
|
+
*/
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @typedef {object} ImageSource
|
30
|
+
* @property {string} label
|
31
|
+
* @property {ImageLoader} imageLoader
|
32
|
+
* @property {ImageMeta} [imageMeta]
|
33
|
+
*/
|
34
|
+
|
35
|
+
export default class ImageScene {
|
36
|
+
#state = new LoadingStateMachine();
|
37
|
+
|
38
|
+
// @note this exported state is set by $effect's
|
39
|
+
state = $state(STATE_INITIAL);
|
40
|
+
|
41
|
+
// @note this exported state is set by $effect's
|
42
|
+
loaded = $derived.by(() => {
|
43
|
+
return this.state === STATE_LOADED;
|
44
|
+
});
|
45
|
+
|
46
|
+
/** @type {ImageSource[]} */
|
47
|
+
#imageSources = $state([]);
|
48
|
+
|
49
|
+
#progress = $derived.by(() => {
|
50
|
+
// console.log('update progress');
|
51
|
+
|
52
|
+
let totalSize = 0;
|
53
|
+
let totalBytesLoaded = 0;
|
54
|
+
let sourcesLoaded = 0;
|
55
|
+
|
56
|
+
const sources = this.#imageSources;
|
57
|
+
const numberOfSources = sources.length;
|
58
|
+
|
59
|
+
for (let j = 0; j < numberOfSources; j++) {
|
60
|
+
const source = sources[j];
|
61
|
+
const { imageLoader } = source;
|
62
|
+
|
63
|
+
const { bytesLoaded, size, loaded } = imageLoader.progress;
|
64
|
+
|
65
|
+
totalSize += size;
|
66
|
+
totalBytesLoaded += bytesLoaded;
|
67
|
+
|
68
|
+
if (loaded) {
|
69
|
+
sourcesLoaded++;
|
70
|
+
}
|
71
|
+
} // end for
|
72
|
+
|
73
|
+
return {
|
74
|
+
totalBytesLoaded,
|
75
|
+
totalSize,
|
76
|
+
sourcesLoaded,
|
77
|
+
numberOfSources
|
78
|
+
};
|
79
|
+
});
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Construct AudioScene
|
83
|
+
*
|
84
|
+
* @param {object} _
|
85
|
+
* @param {AudioContext} _.audioContext
|
86
|
+
*/
|
87
|
+
constructor() {
|
88
|
+
const state = this.#state;
|
89
|
+
|
90
|
+
$effect(() => {
|
91
|
+
if (state.current === STATE_LOADING) {
|
92
|
+
// console.log(
|
93
|
+
// 'progress',
|
94
|
+
// JSON.stringify($state.snapshot(this.#progress))
|
95
|
+
// );
|
96
|
+
|
97
|
+
const { sourcesLoaded, numberOfSources } = this.#progress;
|
98
|
+
|
99
|
+
if (sourcesLoaded === numberOfSources) {
|
100
|
+
// console.log(`All [${numberOfSources}] sources loaded`);
|
101
|
+
this.#state.send(LOADED);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
});
|
105
|
+
|
106
|
+
$effect(() => {
|
107
|
+
switch (state.current) {
|
108
|
+
case STATE_LOADING:
|
109
|
+
{
|
110
|
+
// console.log('ImageScene:loading');
|
111
|
+
this.#startLoading();
|
112
|
+
}
|
113
|
+
break;
|
114
|
+
|
115
|
+
case STATE_UNLOADING:
|
116
|
+
{
|
117
|
+
// console.log('ImageScene:unloading');
|
118
|
+
// this.#startUnLoading();
|
119
|
+
}
|
120
|
+
break;
|
121
|
+
|
122
|
+
case STATE_LOADED:
|
123
|
+
{
|
124
|
+
// console.log('ImageScene:loaded');
|
125
|
+
// TODO
|
126
|
+
// this.#abortLoading = null;
|
127
|
+
}
|
128
|
+
break;
|
129
|
+
|
130
|
+
case STATE_CANCELLED:
|
131
|
+
{
|
132
|
+
// console.log('ImageScene:cancelled');
|
133
|
+
// TODO
|
134
|
+
}
|
135
|
+
break;
|
136
|
+
|
137
|
+
case STATE_ERROR:
|
138
|
+
{
|
139
|
+
console.log('ImageScene:error', state.error);
|
140
|
+
}
|
141
|
+
break;
|
142
|
+
} // end switch
|
143
|
+
|
144
|
+
this.state = state.current;
|
145
|
+
});
|
146
|
+
}
|
147
|
+
|
148
|
+
destroy() {
|
149
|
+
// TODO: disconnect all image sources?
|
150
|
+
// TODO: Unload ImageLoaders?
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Add image source
|
155
|
+
* - Uses an ImageLoader instance to load image data from network
|
156
|
+
*
|
157
|
+
* @param {object} _
|
158
|
+
* @param {string} _.label
|
159
|
+
* @param {ImageMeta|ImageMeta[]} _.imageMeta
|
160
|
+
*/
|
161
|
+
defineImage({ label, imageMeta }) {
|
162
|
+
expect.notEmptyString(label);
|
163
|
+
|
164
|
+
// expect.notEmptyString(url);
|
165
|
+
|
166
|
+
const imageLoader = new ImageLoader({ imageMeta });
|
167
|
+
|
168
|
+
this.#imageSources.push({ label, imageLoader, imageMeta });
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Start loading all image sources
|
173
|
+
*/
|
174
|
+
load() {
|
175
|
+
this.#state.send(LOAD);
|
176
|
+
|
177
|
+
// FIXME: in unit test when moved to startloading it hangs!
|
178
|
+
|
179
|
+
for (const { imageLoader } of this.#imageSources) {
|
180
|
+
imageLoader.load();
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
async #startLoading() {
|
185
|
+
// console.log('#startLoading');
|
186
|
+
// FIXME: in unit test when moved to startloading it hangs!
|
187
|
+
// for (const { audioLoader } of this.#memorySources) {
|
188
|
+
// audioLoader.load();
|
189
|
+
// }
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* Get Image source
|
194
|
+
*
|
195
|
+
* @param {string} label
|
196
|
+
*
|
197
|
+
* @returns {ImageSource}
|
198
|
+
*/
|
199
|
+
#getImageSource(label) {
|
200
|
+
for (const source of this.#imageSources) {
|
201
|
+
if (label === source.label) {
|
202
|
+
return source;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
throw new Error(`Source [${label}] has not been defined`);
|
207
|
+
}
|
208
|
+
|
209
|
+
/**
|
210
|
+
* Get image scene loading progress
|
211
|
+
*/
|
212
|
+
get progress() {
|
213
|
+
return this.#progress;
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Get an image loader
|
218
|
+
*
|
219
|
+
* @param {string} label
|
220
|
+
*
|
221
|
+
* @returns {ImageLoader}
|
222
|
+
*/
|
223
|
+
getImageLoader(label) {
|
224
|
+
const source = this.#getImageSource(label);
|
225
|
+
|
226
|
+
return source.imageLoader;
|
227
|
+
}
|
228
|
+
|
229
|
+
/**
|
230
|
+
* Get object URL that can be used as src parameter of an HTML image
|
231
|
+
*
|
232
|
+
* @param {string} label
|
233
|
+
*
|
234
|
+
* @returns {ImageMeta}
|
235
|
+
*/
|
236
|
+
getImageMeta(label) {
|
237
|
+
const source = this.#getImageSource(label);
|
238
|
+
|
239
|
+
return source.imageMeta;
|
240
|
+
}
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Get object URL that can be used as src parameter of an HTML image
|
244
|
+
*
|
245
|
+
* @param {string} label
|
246
|
+
*
|
247
|
+
* @note the objectURL should be revoked when no longer used
|
248
|
+
*
|
249
|
+
* @returns {string}
|
250
|
+
*/
|
251
|
+
getObjectURL(label) {
|
252
|
+
const source = this.#getImageSource(label);
|
253
|
+
|
254
|
+
return source.imageLoader.getObjectURL();
|
255
|
+
}
|
256
|
+
}
|
@@ -83,9 +83,9 @@ export default class NetworkLoader {
|
|
83
83
|
*
|
84
84
|
* @note the objectURL should be revoked when no longer used
|
85
85
|
*
|
86
|
-
* @returns {string
|
86
|
+
* @returns {string}
|
87
87
|
*/
|
88
|
-
getObjectURL(): string
|
88
|
+
getObjectURL(): string;
|
89
89
|
#private;
|
90
90
|
}
|
91
91
|
import { LoadingStateMachine } from '../loading-state-machine/index.js';
|
@@ -227,7 +227,7 @@ export default class NetworkLoader {
|
|
227
227
|
*
|
228
228
|
* @note the objectURL should be revoked when no longer used
|
229
229
|
*
|
230
|
-
* @returns {string
|
230
|
+
* @returns {string}
|
231
231
|
*/
|
232
232
|
getObjectURL() {
|
233
233
|
//
|
@@ -247,9 +247,7 @@ export default class NetworkLoader {
|
|
247
247
|
// };
|
248
248
|
// });
|
249
249
|
|
250
|
-
|
251
|
-
|
252
|
-
return blob ? URL.createObjectURL(blob) : null;
|
250
|
+
return URL.createObjectURL(this.getBlob());
|
253
251
|
}
|
254
252
|
|
255
253
|
/**
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
import { ImageLoader } from '../../classes/svelte/image/index.js';
|
5
5
|
|
6
|
+
import { toSingleImageMeta } from '../../util/image/index.js';
|
7
|
+
|
6
8
|
/**
|
7
9
|
* @example
|
8
10
|
* import { ImageBox } from '/path/to/ImageBox/index.js';
|
@@ -73,7 +75,13 @@
|
|
73
75
|
* @property {string} [overflow] - Overflow behavior
|
74
76
|
* @property {ObjectFit} [fit] - Object-fit property
|
75
77
|
* @property {ObjectPosition} [position] - Object-position property
|
76
|
-
*
|
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
|
+
*
|
77
85
|
* @property {string} [alt] - Alternative text for the image
|
78
86
|
* @property {() => LoadingProgress} [onProgress] - Progress callback function
|
79
87
|
* @property {*} [attr] - Additional arbitrary attributes
|
@@ -94,8 +102,10 @@
|
|
94
102
|
fit = 'contain',
|
95
103
|
position = 'left top',
|
96
104
|
|
97
|
-
// Image meta
|
98
|
-
|
105
|
+
// Image meta data
|
106
|
+
imageMeta,
|
107
|
+
|
108
|
+
imageLoader,
|
99
109
|
|
100
110
|
alt = '',
|
101
111
|
|
@@ -118,16 +128,12 @@
|
|
118
128
|
let metaWidth = $state(0);
|
119
129
|
let metaHeight = $state(0);
|
120
130
|
|
121
|
-
let
|
131
|
+
let imageMeta_ = $state();
|
122
132
|
|
123
133
|
$effect(() => {
|
124
|
-
if
|
125
|
-
|
126
|
-
|
127
|
-
// expect image.src
|
128
|
-
// expect image.width
|
129
|
-
// expect image.height
|
130
|
-
singleImage = image;
|
134
|
+
if( imageMeta )
|
135
|
+
{
|
136
|
+
imageMeta_ = toSingleImageMeta( imageMeta );
|
131
137
|
}
|
132
138
|
});
|
133
139
|
|
@@ -135,19 +141,30 @@
|
|
135
141
|
//
|
136
142
|
// Set meta width and height
|
137
143
|
//
|
138
|
-
if (
|
139
|
-
if (
|
140
|
-
metaWidth =
|
144
|
+
if (imageMeta_) {
|
145
|
+
if (imageMeta_.width) {
|
146
|
+
metaWidth = imageMeta_.width;
|
141
147
|
}
|
142
148
|
|
143
|
-
if (
|
144
|
-
metaHeight =
|
149
|
+
if (imageMeta_.height) {
|
150
|
+
metaHeight = imageMeta_.height;
|
145
151
|
}
|
146
152
|
}
|
147
153
|
});
|
148
154
|
|
149
155
|
/** @type {ImageLoader|undefined} */
|
150
|
-
let
|
156
|
+
let imageLoader_ = $state();
|
157
|
+
|
158
|
+
$effect( () => {
|
159
|
+
//
|
160
|
+
// User supplied imageLoader instead of imageMeta
|
161
|
+
//
|
162
|
+
if( !imageMeta && imageLoader && !imageLoader_ )
|
163
|
+
{
|
164
|
+
imageLoader_ = imageLoader;
|
165
|
+
imageMeta_ = imageLoader.imageMeta;
|
166
|
+
}
|
167
|
+
} );
|
151
168
|
|
152
169
|
/** @type {string|null} */
|
153
170
|
let objectUrl = $state(null);
|
@@ -156,20 +173,19 @@
|
|
156
173
|
//
|
157
174
|
// Create image loader
|
158
175
|
//
|
159
|
-
if (
|
160
|
-
|
161
|
-
imageLoader = new ImageLoader({ url });
|
176
|
+
if (imageMeta_ && !imageLoader_) {
|
177
|
+
imageLoader_ = new ImageLoader({ imageMeta: imageMeta_ });
|
162
178
|
}
|
163
179
|
});
|
164
180
|
|
165
181
|
$effect(() => {
|
166
182
|
//
|
167
|
-
// Start loading if
|
183
|
+
// Start loading if imageLoader_ is in state 'initial'
|
168
184
|
//
|
169
185
|
// TODO: implement lazy flag
|
170
186
|
//
|
171
|
-
if (
|
172
|
-
|
187
|
+
if (imageLoader_?.initial) {
|
188
|
+
imageLoader_.load();
|
173
189
|
}
|
174
190
|
});
|
175
191
|
|
@@ -177,9 +193,9 @@
|
|
177
193
|
//
|
178
194
|
// Get objectUrl when the image has finished loading
|
179
195
|
//
|
180
|
-
if (
|
196
|
+
if (imageLoader_.loaded) {
|
181
197
|
// @ts-ignore
|
182
|
-
objectUrl =
|
198
|
+
objectUrl = imageLoader_.getObjectURL();
|
183
199
|
}
|
184
200
|
|
185
201
|
return () => {
|
@@ -190,9 +206,6 @@
|
|
190
206
|
};
|
191
207
|
});
|
192
208
|
|
193
|
-
$effect(() => {
|
194
|
-
console.log('classes', classes);
|
195
|
-
});
|
196
209
|
</script>
|
197
210
|
|
198
211
|
<div
|
@@ -37,9 +37,13 @@ declare const ImageBox: import("svelte").Component<{
|
|
37
37
|
*/
|
38
38
|
position?: import("./typedef.js").ObjectPosition;
|
39
39
|
/**
|
40
|
-
*
|
40
|
+
* Image metadata, TODO: array of image metadata for responsive image
|
41
41
|
*/
|
42
|
-
|
42
|
+
imageMeta?: import("../../config/typedef.js").ImageMeta | import("../../config/typedef.js").ImageMeta[];
|
43
|
+
/**
|
44
|
+
* Image loader
|
45
|
+
*/
|
46
|
+
imageLoader?: ImageLoader;
|
43
47
|
/**
|
44
48
|
* - Alternative text for the image
|
45
49
|
*/
|
@@ -53,3 +57,4 @@ declare const ImageBox: import("svelte").Component<{
|
|
53
57
|
*/
|
54
58
|
attr?: any;
|
55
59
|
}, {}, "">;
|
60
|
+
import { ImageLoader } from '../../classes/svelte/image/index.js';
|
@@ -1,4 +1,5 @@
|
|
1
|
+
export { default as ImageBox } from "./ImageBox.svelte";
|
1
2
|
export { default as ResponsiveImage } from "./ResponsiveImage.svelte";
|
2
3
|
declare const _default: {};
|
3
4
|
export default _default;
|
4
|
-
export type ImageMeta =
|
5
|
+
export type ImageMeta = import("../../config/typedef.js").ImageMeta;
|
@@ -1,5 +1,6 @@
|
|
1
|
-
/** @typedef {import('../../
|
1
|
+
/** @typedef {import('../../config/typedef.js').ImageMeta} ImageMeta */
|
2
2
|
|
3
|
+
export { default as ImageBox } from './ImageBox.svelte';
|
3
4
|
export { default as ResponsiveImage } from './ResponsiveImage.svelte';
|
4
5
|
|
5
6
|
export default {};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/**
|
2
|
+
* Returns the unchanged image meta object or the last item of
|
3
|
+
* an array of ImageMeta objects. This is assumed to be a list
|
4
|
+
* of sorted responsive image formats, so it should be the
|
5
|
+
* largest image.
|
6
|
+
*
|
7
|
+
* @param {ImageMeta|ImageMeta[]} imageMeta
|
8
|
+
*/
|
9
|
+
export function toSingleImageMeta(imageMeta: ImageMeta | ImageMeta[]): import("../../config/typedef").ImageMeta;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/**
|
2
|
+
* Returns the unchanged image meta object or the last item of
|
3
|
+
* an array of ImageMeta objects. This is assumed to be a list
|
4
|
+
* of sorted responsive image formats, so it should be the
|
5
|
+
* largest image.
|
6
|
+
*
|
7
|
+
* @param {ImageMeta|ImageMeta[]} imageMeta
|
8
|
+
*/
|
9
|
+
export function toSingleImageMeta(imageMeta) {
|
10
|
+
if (Array.isArray(imageMeta)) {
|
11
|
+
if (!imageMeta.length) {
|
12
|
+
throw new Error('List of ImageMeta objects is empty');
|
13
|
+
}
|
14
|
+
imageMeta = imageMeta[imageMeta.length - 1];
|
15
|
+
}
|
16
|
+
|
17
|
+
if (typeof imageMeta === 'object') {
|
18
|
+
return imageMeta;
|
19
|
+
} else if (!imageMeta) {
|
20
|
+
throw new Error('Missing [imageMeta]');
|
21
|
+
}
|
22
|
+
|
23
|
+
throw new Error('Invalid value for parameter [imageMeta]');
|
24
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@hkdigital/lib-sveltekit",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.93",
|
4
4
|
"author": "Jens Kleinhout, HKdigital (https://hkdigital.nl)",
|
5
5
|
"license": "ISC",
|
6
6
|
"repository": {
|
@@ -48,6 +48,7 @@
|
|
48
48
|
"./*": "./dist/*"
|
49
49
|
},
|
50
50
|
"peerDependencies": {
|
51
|
+
"@sveltejs/kit": "^2.15.2",
|
51
52
|
"svelte": "^5.0.0"
|
52
53
|
},
|
53
54
|
"devDependencies": {
|
@@ -58,18 +59,18 @@
|
|
58
59
|
"@types/eslint": "^9.6.1",
|
59
60
|
"autoprefixer": "^10.4.20",
|
60
61
|
"cross-env": "^7.0.3",
|
61
|
-
"eslint": "^9.
|
62
|
-
"eslint-config-prettier": "^
|
62
|
+
"eslint": "^9.18.0",
|
63
|
+
"eslint-config-prettier": "^10.0.1",
|
63
64
|
"eslint-plugin-svelte": "^2.46.1",
|
64
65
|
"globals": "^15.14.0",
|
65
66
|
"jsdom": "^26.0.0",
|
66
67
|
"prettier": "^3.4.2",
|
67
|
-
"prettier-plugin-svelte": "^3.3.
|
68
|
+
"prettier-plugin-svelte": "^3.3.3",
|
68
69
|
"prettier-plugin-tailwindcss": "^0.6.9",
|
69
|
-
"publint": "^0.3.
|
70
|
+
"publint": "^0.3.2",
|
70
71
|
"standardized-audio-context-mock": "^9.7.15",
|
71
|
-
"svelte": "^5.17.
|
72
|
-
"svelte-check": "^4.1.
|
72
|
+
"svelte": "^5.17.5",
|
73
|
+
"svelte-check": "^4.1.4",
|
73
74
|
"tailwindcss": "^3.4.17",
|
74
75
|
"typescript": "^5.7.3",
|
75
76
|
"vite": "^6.0.7",
|
@@ -77,7 +78,6 @@
|
|
77
78
|
"vitest": "^2.1.8"
|
78
79
|
},
|
79
80
|
"dependencies": {
|
80
|
-
"@sveltejs/kit": "^2.15.2",
|
81
81
|
"runed": "^0.23.0",
|
82
82
|
"valibot": "^0.42.1",
|
83
83
|
"zod": "^3.24.1"
|
@@ -1,162 +0,0 @@
|
|
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>
|