@hkdigital/lib-sveltekit 0.0.92 → 0.0.94
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/README.md +1 -1
- package/dist/classes/svelte/image/ImageScene.svelte.js +1 -4
- package/dist/components/image/ImageBox.svelte +5 -0
- 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/components/widgets/compare-left-right/CompareLeftRight.svelte +175 -0
- package/dist/components/widgets/compare-left-right/CompareLeftRight.svelte.d.ts +8 -0
- package/dist/components/widgets/compare-left-right/index.d.ts +1 -0
- package/dist/components/widgets/compare-left-right/index.js +1 -0
- package/dist/config/imagetools.d.ts +0 -6
- package/package.json +17 -5
- package/dist/components/image/EnhancedImage.svelte__ +0 -162
package/README.md
CHANGED
@@ -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,175 @@
|
|
1
|
+
<!-- CompareLeftRight.svelte -->
|
2
|
+
<script>
|
3
|
+
/** @type {HTMLElement | null} */
|
4
|
+
let container = $state(null);
|
5
|
+
let isMouseDown = $state(false);
|
6
|
+
let position = $state(50);
|
7
|
+
|
8
|
+
/** @type {{
|
9
|
+
leftContent?: import('svelte').Snippet,
|
10
|
+
rightContent?: import('svelte').Snippet,
|
11
|
+
classes?: string,
|
12
|
+
dividerColor?: string,
|
13
|
+
handleColor?: string
|
14
|
+
} & Record<string, any>} */
|
15
|
+
let {
|
16
|
+
leftContent,
|
17
|
+
rightContent,
|
18
|
+
classes = '',
|
19
|
+
dividerColor = 'bg-surface-500',
|
20
|
+
handleColor = 'bg-surface-700',
|
21
|
+
...attrs
|
22
|
+
} = $props();
|
23
|
+
|
24
|
+
const leftStyle = $derived(`clip-path: inset(0 ${100 - position}% 0 0)`);
|
25
|
+
const rightStyle = $derived(`clip-path: inset(0 0 0 ${position}%)`);
|
26
|
+
const dividerStyle = $derived(`left: ${position}%`);
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Updates the position with keyboard navigation
|
30
|
+
* @param {number} newPosition - The new position to set
|
31
|
+
*/
|
32
|
+
function updatePosition(newPosition) {
|
33
|
+
position = Math.max(0, Math.min(100, newPosition));
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Handles the mouse down event on the divider
|
38
|
+
* @param {MouseEvent} event - The mouse event
|
39
|
+
*/
|
40
|
+
function handleMouseDown(event) {
|
41
|
+
event.preventDefault();
|
42
|
+
isMouseDown = true;
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Updates the divider position based on mouse movement
|
47
|
+
* @param {MouseEvent} event - The mouse event
|
48
|
+
*/
|
49
|
+
function handleMouseMove(event) {
|
50
|
+
if (!isMouseDown || !container) return;
|
51
|
+
|
52
|
+
const rect = container.getBoundingClientRect();
|
53
|
+
const x = Math.min(Math.max(0, event.clientX - rect.left), rect.width);
|
54
|
+
updatePosition((x / rect.width) * 100);
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Handles keyboard navigation for the slider
|
59
|
+
* @param {KeyboardEvent} event - The keyboard event
|
60
|
+
*/
|
61
|
+
function handleKeyDown(event) {
|
62
|
+
const step = event.shiftKey ? 10 : 1;
|
63
|
+
|
64
|
+
switch (event.key) {
|
65
|
+
case 'ArrowLeft':
|
66
|
+
case 'ArrowDown':
|
67
|
+
event.preventDefault();
|
68
|
+
updatePosition(position - step);
|
69
|
+
break;
|
70
|
+
case 'ArrowRight':
|
71
|
+
case 'ArrowUp':
|
72
|
+
event.preventDefault();
|
73
|
+
updatePosition(position + step);
|
74
|
+
break;
|
75
|
+
case 'Home':
|
76
|
+
event.preventDefault();
|
77
|
+
updatePosition(0);
|
78
|
+
break;
|
79
|
+
case 'End':
|
80
|
+
event.preventDefault();
|
81
|
+
updatePosition(100);
|
82
|
+
break;
|
83
|
+
case 'PageDown':
|
84
|
+
event.preventDefault();
|
85
|
+
updatePosition(position - 10);
|
86
|
+
break;
|
87
|
+
case 'PageUp':
|
88
|
+
event.preventDefault();
|
89
|
+
updatePosition(position + 10);
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Resets the mouse down state
|
96
|
+
*/
|
97
|
+
function handleMouseUp() {
|
98
|
+
isMouseDown = false;
|
99
|
+
}
|
100
|
+
|
101
|
+
// Effect to handle document-level mouse events
|
102
|
+
$effect(() => {
|
103
|
+
if (isMouseDown) {
|
104
|
+
document.addEventListener('mousemove', handleMouseMove);
|
105
|
+
document.addEventListener('mouseup', handleMouseUp);
|
106
|
+
}
|
107
|
+
|
108
|
+
return () => {
|
109
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
110
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
111
|
+
};
|
112
|
+
});
|
113
|
+
</script>
|
114
|
+
|
115
|
+
<div
|
116
|
+
bind:this={container}
|
117
|
+
class="relative flex h-full w-full overflow-hidden {classes}"
|
118
|
+
role="group"
|
119
|
+
aria-label="Content comparison"
|
120
|
+
{...attrs}
|
121
|
+
>
|
122
|
+
<!-- Content container - both pieces of content are positioned absolutely -->
|
123
|
+
<div class="relative h-full w-full">
|
124
|
+
<!-- Left content -->
|
125
|
+
<div
|
126
|
+
class="absolute h-full w-full"
|
127
|
+
style={leftStyle}
|
128
|
+
>
|
129
|
+
{@render leftContent()}
|
130
|
+
</div>
|
131
|
+
|
132
|
+
<!-- Right content -->
|
133
|
+
<div
|
134
|
+
class="absolute h-full w-full"
|
135
|
+
style={rightStyle}
|
136
|
+
>
|
137
|
+
{@render rightContent()}
|
138
|
+
</div>
|
139
|
+
</div>
|
140
|
+
|
141
|
+
<!-- Slider control -->
|
142
|
+
<div
|
143
|
+
class="absolute inset-y-0 z-10 flex w-1 cursor-col-resize items-center justify-center {dividerColor}"
|
144
|
+
style={dividerStyle}
|
145
|
+
>
|
146
|
+
<!-- Vertical separator line -->
|
147
|
+
<div class="absolute inset-y-0 w-0.5 bg-current opacity-50"></div>
|
148
|
+
|
149
|
+
<button
|
150
|
+
class="flex h-10 w-10 items-center justify-center rounded-full shadow-lg hover:scale-110 transition-transform focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 {handleColor}"
|
151
|
+
on:mousedown={handleMouseDown}
|
152
|
+
on:keydown={handleKeyDown}
|
153
|
+
role="slider"
|
154
|
+
aria-orientation="vertical"
|
155
|
+
aria-valuenow={position}
|
156
|
+
aria-valuemin={0}
|
157
|
+
aria-valuemax={100}
|
158
|
+
aria-label="Adjust divider position"
|
159
|
+
>
|
160
|
+
<svg
|
161
|
+
class="h-6 w-6 stroke-surface-50"
|
162
|
+
viewBox="0 0 24 24"
|
163
|
+
fill="none"
|
164
|
+
stroke="currentColor"
|
165
|
+
stroke-width="2"
|
166
|
+
stroke-linecap="round"
|
167
|
+
stroke-linejoin="round"
|
168
|
+
>
|
169
|
+
<path d="M7 16l-4-4 4-4" />
|
170
|
+
<path d="M17 8l4 4-4 4" />
|
171
|
+
<path d="M3 12h18" />
|
172
|
+
</svg>
|
173
|
+
</button>
|
174
|
+
</div>
|
175
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
export default CompareLeftRight;
|
2
|
+
declare const CompareLeftRight: import("svelte").Component<{
|
3
|
+
leftContent?: import("svelte").Snippet;
|
4
|
+
rightContent?: import("svelte").Snippet;
|
5
|
+
classes?: string;
|
6
|
+
dividerColor?: string;
|
7
|
+
handleColor?: string;
|
8
|
+
} & Record<string, any>, {}, "">;
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as CompareLeftRight } from "./CompareLeftRight.svelte";
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as CompareLeftRight } from "./CompareLeftRight.svelte";
|
package/package.json
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"name": "@hkdigital/lib-sveltekit",
|
3
|
-
"version": "0.0.
|
4
|
-
"author":
|
3
|
+
"version": "0.0.94",
|
4
|
+
"author": {
|
5
|
+
"name": "HKdigital",
|
6
|
+
"url": "https://hkdigital.nl"
|
7
|
+
},
|
5
8
|
"license": "ISC",
|
6
9
|
"repository": {
|
7
10
|
"type": "git",
|
@@ -45,14 +48,23 @@
|
|
45
48
|
"types": "./dist/index.d.ts",
|
46
49
|
"type": "module",
|
47
50
|
"exports": {
|
51
|
+
".": {
|
52
|
+
"types": "./dist/index.d.ts",
|
53
|
+
"svelte": "./dist/index.js",
|
54
|
+
"default": "./dist/index.js"
|
55
|
+
},
|
48
56
|
"./*": "./dist/*"
|
49
57
|
},
|
50
58
|
"peerDependencies": {
|
51
59
|
"@sveltejs/kit": "^2.15.2",
|
52
|
-
"svelte": "^5.0.0"
|
60
|
+
"svelte": "^5.0.0",
|
61
|
+
"runed": "^0.23.0",
|
62
|
+
"valibot": "^0.42.1"
|
53
63
|
},
|
54
64
|
"devDependencies": {
|
55
65
|
"@playwright/test": "^1.49.1",
|
66
|
+
"@skeletonlabs/skeleton": "3.0.0-next.2",
|
67
|
+
"@skeletonlabs/skeleton-svelte": "1.0.0-next.4",
|
56
68
|
"@sveltejs/adapter-auto": "^3.3.1",
|
57
69
|
"@sveltejs/package": "^2.3.7",
|
58
70
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
@@ -64,6 +76,8 @@
|
|
64
76
|
"eslint-plugin-svelte": "^2.46.1",
|
65
77
|
"globals": "^15.14.0",
|
66
78
|
"jsdom": "^26.0.0",
|
79
|
+
"postcss": "^8.5.1",
|
80
|
+
"postcss-mixins": "^11.0.3",
|
67
81
|
"prettier": "^3.4.2",
|
68
82
|
"prettier-plugin-svelte": "^3.3.3",
|
69
83
|
"prettier-plugin-tailwindcss": "^0.6.9",
|
@@ -78,8 +92,6 @@
|
|
78
92
|
"vitest": "^2.1.8"
|
79
93
|
},
|
80
94
|
"dependencies": {
|
81
|
-
"runed": "^0.23.0",
|
82
|
-
"valibot": "^0.42.1",
|
83
95
|
"zod": "^3.24.1"
|
84
96
|
}
|
85
97
|
}
|
@@ -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>
|