@hkdigital/lib-sveltekit 0.0.98 → 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/components/image/EnhancedImage.svelte__ +162 -0
- package/dist/components/image/ImageBox.svelte__ +242 -0
- package/dist/components/image/ImageBox.svelte___ +241 -0
- package/dist/components/image/index.d.ts +0 -1
- package/dist/components/image/index.js +0 -1
- package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -0
- package/dist/components/inputs/text-input/TextInput.svelte +2 -2
- package/dist/components/inputs/text-input/TextInput.svelte___ +83 -0
- 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/css/tw-prose.postcss__ +259 -0
- package/dist/themes/hkdev/global/text.postcss__ +35 -0
- package/dist/themes/hkdev/global/vars.postcss__ +7 -0
- package/package.json +1 -1
- 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
@@ -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>
|
@@ -0,0 +1,242 @@
|
|
1
|
+
<script>
|
2
|
+
import { onMount } from 'svelte';
|
3
|
+
|
4
|
+
import { ImageLoader } from '$lib/classes/svelte/image/index.js';
|
5
|
+
|
6
|
+
import { toSingleImageMeta } from '$lib/util/image/index.js';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @example
|
10
|
+
* import { ImageBox } from '/path/to/ImageBox/index.js';
|
11
|
+
*
|
12
|
+
* // @note 'as=metadata' is set by the preset
|
13
|
+
* import NeonLightsOff from '$lib/img/NeonLightsOff.jpg?preset=gradient';
|
14
|
+
*
|
15
|
+
* <!-- Example that fits in an outer-box -->
|
16
|
+
*
|
17
|
+
* <div class="outer-box">
|
18
|
+
* <ImageBox image={ArmyGreen} fit="contain" position="center center" />
|
19
|
+
* </div>
|
20
|
+
*
|
21
|
+
* <!-- Examples that has have width, height or aspect set -->
|
22
|
+
*
|
23
|
+
* <ImageBox
|
24
|
+
* image={ArmyGreen}
|
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
|
+
* />
|
58
|
+
*/
|
59
|
+
|
60
|
+
/**
|
61
|
+
* @typedef {import('./typedef.js').ObjectFit} ObjectFit
|
62
|
+
* @typedef {import('./typedef.js').ObjectPosition} ObjectPosition
|
63
|
+
*
|
64
|
+
* @typedef {import('$lib/classes/svelte/network-loader/typedef.js').LoadingProgress} LoadingProgress
|
65
|
+
*
|
66
|
+
* @typedef {import('$lib/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
|
+
let {
|
92
|
+
// Style
|
93
|
+
base,
|
94
|
+
bg,
|
95
|
+
classes,
|
96
|
+
width,
|
97
|
+
height,
|
98
|
+
aspect,
|
99
|
+
overflow = 'overflow-clip',
|
100
|
+
|
101
|
+
// Fitting and positioning of image in its container
|
102
|
+
fit = 'contain',
|
103
|
+
position = 'left top',
|
104
|
+
|
105
|
+
// Single image or responsive image meta data
|
106
|
+
imageMeta,
|
107
|
+
|
108
|
+
imageLoader,
|
109
|
+
|
110
|
+
alt = '',
|
111
|
+
|
112
|
+
// Attributes
|
113
|
+
...attrs
|
114
|
+
} = $props();
|
115
|
+
|
116
|
+
if (!imageMeta) {
|
117
|
+
throw new Error('Missing [imageMeta]');
|
118
|
+
}
|
119
|
+
|
120
|
+
// let show = $state(false);
|
121
|
+
|
122
|
+
/** @type {HTMLImageElement|undefined} */
|
123
|
+
let imgElem = $state();
|
124
|
+
|
125
|
+
let aspectStyle = $state('');
|
126
|
+
|
127
|
+
// > Loading
|
128
|
+
|
129
|
+
let metaWidth = $state(0);
|
130
|
+
let metaHeight = $state(0);
|
131
|
+
|
132
|
+
let imageMeta_ = $state();
|
133
|
+
|
134
|
+
$effect(() => {
|
135
|
+
//
|
136
|
+
// Is imageMeta is a list of responsive options: pick one
|
137
|
+
//
|
138
|
+
if (imageMeta) {
|
139
|
+
imageMeta_ = toSingleImageMeta(imageMeta);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
|
143
|
+
$effect(() => {
|
144
|
+
//
|
145
|
+
// Set meta width and height
|
146
|
+
//
|
147
|
+
if (imageMeta_) {
|
148
|
+
if (imageMeta_.width) {
|
149
|
+
metaWidth = imageMeta_.width;
|
150
|
+
}
|
151
|
+
|
152
|
+
if (imageMeta_.height) {
|
153
|
+
metaHeight = imageMeta_.height;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
});
|
157
|
+
|
158
|
+
/** @type {ImageLoader|undefined} */
|
159
|
+
let imageLoader_ = $state();
|
160
|
+
|
161
|
+
$effect(() => {
|
162
|
+
//
|
163
|
+
// User supplied imageLoader instead of imageMeta
|
164
|
+
//
|
165
|
+
if (!imageMeta && imageLoader && !imageLoader_) {
|
166
|
+
imageLoader_ = imageLoader;
|
167
|
+
imageMeta_ = imageLoader.imageMeta;
|
168
|
+
}
|
169
|
+
});
|
170
|
+
|
171
|
+
/** @type {string|null} */
|
172
|
+
let objectUrl = $state(null);
|
173
|
+
|
174
|
+
$effect(() => {
|
175
|
+
//
|
176
|
+
// Create image loader
|
177
|
+
//
|
178
|
+
if (imageMeta_ && !imageLoader_) {
|
179
|
+
imageLoader_ = new ImageLoader({ imageMeta: imageMeta_ });
|
180
|
+
}
|
181
|
+
});
|
182
|
+
|
183
|
+
$effect(() => {
|
184
|
+
//
|
185
|
+
// Start loading if imageLoader_ is in state 'initial'
|
186
|
+
//
|
187
|
+
// TODO: implement lazy flag
|
188
|
+
//
|
189
|
+
if (imageLoader_?.initial) {
|
190
|
+
imageLoader_.load();
|
191
|
+
}
|
192
|
+
});
|
193
|
+
|
194
|
+
$effect(() => {
|
195
|
+
//
|
196
|
+
// Get objectUrl when the image has finished loading
|
197
|
+
//
|
198
|
+
if (imageLoader_.loaded) {
|
199
|
+
// @ts-ignore
|
200
|
+
objectUrl = imageLoader_.getObjectURL();
|
201
|
+
}
|
202
|
+
|
203
|
+
return () => {
|
204
|
+
if (objectUrl) {
|
205
|
+
URL.revokeObjectURL(objectUrl);
|
206
|
+
objectUrl = null;
|
207
|
+
}
|
208
|
+
};
|
209
|
+
});
|
210
|
+
</script>
|
211
|
+
|
212
|
+
<div
|
213
|
+
data-image="box"
|
214
|
+
class="{base} {bg} {aspect} {overflow} {width} {height} {classes}"
|
215
|
+
style:--fit={fit}
|
216
|
+
style:--pos={position}
|
217
|
+
style:aspect-ratio={aspectStyle}
|
218
|
+
style:width={width || (height && aspect) ? undefined : '100%'}
|
219
|
+
style:height={height || (width && aspect) ? undefined : '100%'}
|
220
|
+
{...attrs}
|
221
|
+
>
|
222
|
+
{#if objectUrl && metaWidth && metaHeight}
|
223
|
+
<img src={objectUrl} {alt} width={metaWidth} height={metaHeight} />
|
224
|
+
{/if}
|
225
|
+
</div>
|
226
|
+
|
227
|
+
<style>
|
228
|
+
[data-image='box'] {
|
229
|
+
max-width: 100%;
|
230
|
+
max-height: 100%;
|
231
|
+
}
|
232
|
+
|
233
|
+
img {
|
234
|
+
display: block;
|
235
|
+
width: 100%;
|
236
|
+
height: 100%;
|
237
|
+
max-width: 100%;
|
238
|
+
max-height: 100%;
|
239
|
+
object-fit: var(--fit);
|
240
|
+
object-position: var(--pos);
|
241
|
+
}
|
242
|
+
</style>
|
@@ -0,0 +1,241 @@
|
|
1
|
+
<script>
|
2
|
+
import { onMount } from 'svelte';
|
3
|
+
import { ImageLoader } from '$lib/classes/svelte/image/index.js';
|
4
|
+
import { ImageVariantsLoader } from '$lib/classes/svelte/image/index.js';
|
5
|
+
import { toSingleImageMeta } from '$lib/util/image/index.js';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @type {{
|
9
|
+
* base?: string,
|
10
|
+
* bg?: string,
|
11
|
+
* classes?: string,
|
12
|
+
* width?: string,
|
13
|
+
* height?: string,
|
14
|
+
* aspect?: string,
|
15
|
+
* overflow?: string,
|
16
|
+
* fit?: 'contain' | 'cover' | 'fill',
|
17
|
+
* position?: string,
|
18
|
+
* imageMeta: import('$lib/config/typedef.js').ImageMeta | import('$lib/config/typedef.js').ImageMeta[],
|
19
|
+
* imageLoader?: import('$lib/classes/svelte/image/index.js').ImageLoader,
|
20
|
+
* alt?: string,
|
21
|
+
* onProgress?: () => import('$lib/classes/svelte/network-loader/typedef.js').LoadingProgress,
|
22
|
+
* debug?: boolean,
|
23
|
+
* [attr: string]: any
|
24
|
+
* }}
|
25
|
+
*/
|
26
|
+
let {
|
27
|
+
// Style
|
28
|
+
base,
|
29
|
+
bg,
|
30
|
+
classes,
|
31
|
+
width,
|
32
|
+
height,
|
33
|
+
aspect,
|
34
|
+
overflow = 'overflow-clip',
|
35
|
+
|
36
|
+
// Fitting and positioning of image in its container
|
37
|
+
fit = 'contain',
|
38
|
+
position = 'left top',
|
39
|
+
|
40
|
+
// Single image or responsive image meta data
|
41
|
+
imageMeta,
|
42
|
+
imageLoader,
|
43
|
+
|
44
|
+
// Accessibility
|
45
|
+
alt = '',
|
46
|
+
|
47
|
+
// Development
|
48
|
+
debug = false,
|
49
|
+
|
50
|
+
// Additional attributes
|
51
|
+
...attrs
|
52
|
+
} = $props();
|
53
|
+
|
54
|
+
if (!imageMeta) {
|
55
|
+
throw new Error('Missing [imageMeta]');
|
56
|
+
}
|
57
|
+
|
58
|
+
/** @type {HTMLDivElement|undefined} */
|
59
|
+
let containerElem = $state();
|
60
|
+
|
61
|
+
/** @type {HTMLImageElement|undefined} */
|
62
|
+
let imgElem = $state();
|
63
|
+
|
64
|
+
let aspectStyle = $state('');
|
65
|
+
|
66
|
+
// > Loading
|
67
|
+
let metaWidth = $state(0);
|
68
|
+
let metaHeight = $state(0);
|
69
|
+
|
70
|
+
let imageMeta_ = $state();
|
71
|
+
let variantsLoader = $state();
|
72
|
+
let variantObjectUrl = $state(null);
|
73
|
+
let objectUrl = $state(null);
|
74
|
+
|
75
|
+
$effect(() => {
|
76
|
+
//
|
77
|
+
// Is imageMeta is a list of responsive options:
|
78
|
+
// => setup variants loader
|
79
|
+
//
|
80
|
+
if (Array.isArray(imageMeta) && !imageLoader && !variantsLoader) {
|
81
|
+
// $inspect(imageMeta, 'Creating variantsLoader');
|
82
|
+
|
83
|
+
variantsLoader = new ImageVariantsLoader(imageMeta, {
|
84
|
+
devicePixelRatio: window.devicePixelRatio
|
85
|
+
});
|
86
|
+
}
|
87
|
+
//
|
88
|
+
// Single image meta (only if not using variants)
|
89
|
+
//
|
90
|
+
else if (imageMeta && !variantsLoader) {
|
91
|
+
// Only run if there is no variantsLoader
|
92
|
+
|
93
|
+
//$inspect(imageMeta, 'Using single imageMeta');
|
94
|
+
|
95
|
+
imageMeta_ = toSingleImageMeta(imageMeta);
|
96
|
+
}
|
97
|
+
});
|
98
|
+
|
99
|
+
$effect(() => {
|
100
|
+
//
|
101
|
+
// Set meta width and height for single image
|
102
|
+
//
|
103
|
+
if (imageMeta_) {
|
104
|
+
if (imageMeta_.width) {
|
105
|
+
metaWidth = imageMeta_.width;
|
106
|
+
}
|
107
|
+
|
108
|
+
if (imageMeta_.height) {
|
109
|
+
metaHeight = imageMeta_.height;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
/** @type {ImageLoader|undefined} */
|
115
|
+
let imageLoader_ = $state();
|
116
|
+
|
117
|
+
$effect(() => {
|
118
|
+
//
|
119
|
+
// User supplied imageLoader instead of imageMeta
|
120
|
+
//
|
121
|
+
if (!imageMeta && imageLoader && !imageLoader_) {
|
122
|
+
imageLoader_ = imageLoader;
|
123
|
+
imageMeta_ = imageLoader.imageMeta;
|
124
|
+
}
|
125
|
+
});
|
126
|
+
|
127
|
+
$effect(() => {
|
128
|
+
//
|
129
|
+
// Create image loader for single image
|
130
|
+
//
|
131
|
+
if (imageMeta_ && !imageLoader_) {
|
132
|
+
imageLoader_ = new ImageLoader({ imageMeta: imageMeta_ });
|
133
|
+
}
|
134
|
+
});
|
135
|
+
|
136
|
+
$effect(() => {
|
137
|
+
//
|
138
|
+
// Start loading if imageLoader_ is in state 'initial'
|
139
|
+
//
|
140
|
+
if (imageLoader_?.initial) {
|
141
|
+
imageLoader_.load();
|
142
|
+
}
|
143
|
+
});
|
144
|
+
|
145
|
+
$effect(() => {
|
146
|
+
//
|
147
|
+
// Get objectUrl when the single image has finished loading
|
148
|
+
//
|
149
|
+
if (imageLoader_?.loaded) {
|
150
|
+
objectUrl = imageLoader_.getObjectURL();
|
151
|
+
}
|
152
|
+
|
153
|
+
return () => {
|
154
|
+
if (objectUrl) {
|
155
|
+
URL.revokeObjectURL(objectUrl);
|
156
|
+
objectUrl = null;
|
157
|
+
}
|
158
|
+
};
|
159
|
+
});
|
160
|
+
|
161
|
+
$effect(() => {
|
162
|
+
//
|
163
|
+
// Setup resize observer for variants loading
|
164
|
+
//
|
165
|
+
if (!containerElem || !variantsLoader) return;
|
166
|
+
|
167
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
168
|
+
for (const entry of entries) {
|
169
|
+
const { width, height } = entry.contentRect;
|
170
|
+
|
171
|
+
variantsLoader.updateOptimalImageMeta({
|
172
|
+
containerWidth: width,
|
173
|
+
containerHeight: height,
|
174
|
+
fit
|
175
|
+
});
|
176
|
+
}
|
177
|
+
});
|
178
|
+
|
179
|
+
resizeObserver.observe(containerElem);
|
180
|
+
|
181
|
+
return () => {
|
182
|
+
resizeObserver.disconnect();
|
183
|
+
};
|
184
|
+
});
|
185
|
+
|
186
|
+
$effect(() => {
|
187
|
+
//
|
188
|
+
// Get variantObjectUrl when a variant has finished loading
|
189
|
+
//
|
190
|
+
if (variantsLoader?.loaded) {
|
191
|
+
variantObjectUrl = variantsLoader.getObjectURL();
|
192
|
+
}
|
193
|
+
|
194
|
+
return () => {
|
195
|
+
if (variantObjectUrl) {
|
196
|
+
URL.revokeObjectURL(variantObjectUrl);
|
197
|
+
variantObjectUrl = null;
|
198
|
+
}
|
199
|
+
};
|
200
|
+
});
|
201
|
+
</script>
|
202
|
+
|
203
|
+
<div
|
204
|
+
data-image="box"
|
205
|
+
bind:this={containerElem}
|
206
|
+
class="{base} {bg} {aspect} {overflow} {width} {height} {classes}"
|
207
|
+
style:--fit={fit}
|
208
|
+
style:--pos={position}
|
209
|
+
style:aspect-ratio={aspectStyle}
|
210
|
+
style:width={width || (height && aspect) ? undefined : '100%'}
|
211
|
+
style:height={height || (width && aspect) ? undefined : '100%'}
|
212
|
+
{...attrs}
|
213
|
+
>
|
214
|
+
{#if variantsLoader?.loaded && variantObjectUrl}
|
215
|
+
<img
|
216
|
+
src={variantObjectUrl}
|
217
|
+
{alt}
|
218
|
+
width={variantsLoader.variant.width}
|
219
|
+
height={variantsLoader.variant.height}
|
220
|
+
/>
|
221
|
+
{:else if objectUrl && metaWidth && metaHeight}
|
222
|
+
<img src={objectUrl} {alt} width={metaWidth} height={metaHeight} />
|
223
|
+
{/if}
|
224
|
+
</div>
|
225
|
+
|
226
|
+
<style>
|
227
|
+
[data-image='box'] {
|
228
|
+
max-width: 100%;
|
229
|
+
max-height: 100%;
|
230
|
+
}
|
231
|
+
|
232
|
+
img {
|
233
|
+
display: block;
|
234
|
+
width: 100%;
|
235
|
+
height: 100%;
|
236
|
+
max-width: 100%;
|
237
|
+
max-height: 100%;
|
238
|
+
object-fit: var(--fit);
|
239
|
+
object-position: var(--pos);
|
240
|
+
}
|
241
|
+
</style>
|
@@ -0,0 +1,102 @@
|
|
1
|
+
<script>
|
2
|
+
/**
|
3
|
+
* @type {{
|
4
|
+
* base?: string,
|
5
|
+
* error?: string,
|
6
|
+
* classes?: string,
|
7
|
+
* fieldClasses?: string,
|
8
|
+
* fieldError?: string,
|
9
|
+
* legendBase?: string,
|
10
|
+
* legendClasses?: string,
|
11
|
+
* legendError?: string,
|
12
|
+
* value?: string,
|
13
|
+
* type?: 'text' | 'url' | 'email' | 'number',
|
14
|
+
* pattern?: string,
|
15
|
+
* required?: boolean,
|
16
|
+
* title?: string,
|
17
|
+
* valid?: boolean,
|
18
|
+
* pristine?: boolean,
|
19
|
+
* validate?: (value: string) => string | undefined,
|
20
|
+
* } & { [attr: string]: * }}
|
21
|
+
*/
|
22
|
+
let {
|
23
|
+
base = '',
|
24
|
+
error = '',
|
25
|
+
classes = '',
|
26
|
+
|
27
|
+
fieldClasses,
|
28
|
+
fieldError,
|
29
|
+
|
30
|
+
legendBase = 'ml-16p px-8p',
|
31
|
+
legendClasses,
|
32
|
+
legendError,
|
33
|
+
|
34
|
+
value = $bindable(''),
|
35
|
+
type = 'text',
|
36
|
+
pattern,
|
37
|
+
required = false,
|
38
|
+
|
39
|
+
title = '',
|
40
|
+
|
41
|
+
valid = $bindable(true),
|
42
|
+
pristine = $bindable(true),
|
43
|
+
|
44
|
+
validate,
|
45
|
+
|
46
|
+
...attrs
|
47
|
+
} = $props();
|
48
|
+
|
49
|
+
let inputRef = $state();
|
50
|
+
let validationMessage = $state('');
|
51
|
+
let initialValue = $state('');
|
52
|
+
|
53
|
+
$effect(() => {
|
54
|
+
if (!inputRef) return;
|
55
|
+
initialValue = value;
|
56
|
+
validateInput(inputRef, value);
|
57
|
+
});
|
58
|
+
|
59
|
+
function validateInput(input, currentValue) {
|
60
|
+
input.setCustomValidity('');
|
61
|
+
const isBuiltInValid = input.checkValidity();
|
62
|
+
|
63
|
+
if (isBuiltInValid && validate) {
|
64
|
+
const customError = validate(currentValue);
|
65
|
+
input.setCustomValidity(customError || '');
|
66
|
+
}
|
67
|
+
|
68
|
+
pristine = currentValue === initialValue;
|
69
|
+
valid = input.validity.valid;
|
70
|
+
validationMessage = input.validationMessage;
|
71
|
+
}
|
72
|
+
|
73
|
+
function handleInput(event) {
|
74
|
+
validateInput(event.target, event.target.value);
|
75
|
+
}
|
76
|
+
</script>
|
77
|
+
|
78
|
+
<fieldset
|
79
|
+
data-inputs="text-input"
|
80
|
+
class="flex w-full items-center rounded {fieldClasses}"
|
81
|
+
>
|
82
|
+
<legend class="{legendBase} {legendClasses}">{title}</legend>
|
83
|
+
|
84
|
+
<input
|
85
|
+
bind:this={inputRef}
|
86
|
+
{type}
|
87
|
+
{pattern}
|
88
|
+
{required}
|
89
|
+
{value}
|
90
|
+
class="w-full border-none bg-transparent {base} {classes}"
|
91
|
+
aria-invalid={!valid}
|
92
|
+
aria-errormessage={!valid ? 'validation-message' : undefined}
|
93
|
+
oninput={handleInput}
|
94
|
+
{...attrs}
|
95
|
+
/>
|
96
|
+
|
97
|
+
{#if !valid}
|
98
|
+
<small id="validation-message" class="text-error" role="alert">
|
99
|
+
{validationMessage}
|
100
|
+
</small>
|
101
|
+
{/if}
|
102
|
+
</fieldset>
|
@@ -15,7 +15,7 @@
|
|
15
15
|
INVALID,
|
16
16
|
REQUIRED,
|
17
17
|
DISABLED
|
18
|
-
} from '../../../constants/
|
18
|
+
} from '../../../constants/state-labels/input-states.js';
|
19
19
|
|
20
20
|
/**
|
21
21
|
* @type {{
|
@@ -136,7 +136,7 @@
|
|
136
136
|
//
|
137
137
|
// Return CSS classes that indicate the component's state
|
138
138
|
//
|
139
|
-
// @see $lib/constants/
|
139
|
+
// @see $lib/constants/state-labels
|
140
140
|
//
|
141
141
|
const outArr = [];
|
142
142
|
|
@@ -0,0 +1,83 @@
|
|
1
|
+
<script>
|
2
|
+
/**
|
3
|
+
* @type {{
|
4
|
+
* classes?: string,
|
5
|
+
* fieldClasses?: string,
|
6
|
+
* legendClasses?: string,
|
7
|
+
* legendTitle?: string,
|
8
|
+
* error?: boolean,
|
9
|
+
* type?: string,
|
10
|
+
* placeholder: string,
|
11
|
+
* required: boolean,
|
12
|
+
* snippetWarning?: import('svelte').Snippet,
|
13
|
+
* } & { [attr: string]: * }}
|
14
|
+
*/
|
15
|
+
let {
|
16
|
+
// Style
|
17
|
+
classes,
|
18
|
+
fieldClasses,
|
19
|
+
legendClasses,
|
20
|
+
|
21
|
+
// Functionality
|
22
|
+
name,
|
23
|
+
disabled,
|
24
|
+
required,
|
25
|
+
|
26
|
+
// initialValue
|
27
|
+
// value
|
28
|
+
// readonly
|
29
|
+
// pattern
|
30
|
+
// minlength
|
31
|
+
// maxlength
|
32
|
+
|
33
|
+
// Text placeholders
|
34
|
+
legendTitle,
|
35
|
+
placeholder,
|
36
|
+
|
37
|
+
type,
|
38
|
+
snippetWarning,
|
39
|
+
|
40
|
+
// Attributes
|
41
|
+
...attrs
|
42
|
+
} = $props();
|
43
|
+
</script>
|
44
|
+
|
45
|
+
{#snippet defaultWarning()}
|
46
|
+
<svg
|
47
|
+
width="17"
|
48
|
+
height="16"
|
49
|
+
viewBox="0 0 17 16"
|
50
|
+
fill="none"
|
51
|
+
xmlns="http://www.w3.org/2000/svg"
|
52
|
+
>
|
53
|
+
<path
|
54
|
+
fill-rule="evenodd"
|
55
|
+
clip-rule="evenodd"
|
56
|
+
d="M6.36747 1.28014C7.3152 -0.426712 9.68492 -0.426712 10.6318 1.28014L16.6669 12.1596C17.6138 13.8664 16.429 16 14.5343 16H2.46497C0.570331 16 -0.613713 13.8664 0.333194 12.1596L6.36665 1.28014H6.36747ZM8.50006 5.75805C8.66328 5.75805 8.81981 5.82549 8.93522 5.94553C9.05063 6.06556 9.11547 6.22837 9.11547 6.39812V9.59846C9.11547 9.76822 9.05063 9.93102 8.93522 10.0511C8.81981 10.1711 8.66328 10.2385 8.50006 10.2385C8.33684 10.2385 8.18031 10.1711 8.0649 10.0511C7.94949 9.93102 7.88465 9.76822 7.88465 9.59846V6.39812C7.88465 6.22837 7.94949 6.06556 8.0649 5.94553C8.18031 5.82549 8.33684 5.75805 8.50006 5.75805ZM8.50006 12.7988C8.66328 12.7988 8.81981 12.7314 8.93522 12.6113C9.05063 12.4913 9.11547 12.3285 9.11547 12.1587C9.11547 11.989 9.05063 11.8262 8.93522 11.7061C8.81981 11.5861 8.66328 11.5187 8.50006 11.5187C8.33684 11.5187 8.18031 11.5861 8.0649 11.7061C7.94949 11.8262 7.88465 11.989 7.88465 12.1587C7.88465 12.3285 7.94949 12.4913 8.0649 12.6113C8.18031 12.7314 8.33684 12.7988 8.50006 12.7988Z"
|
57
|
+
fill="#F8705E"
|
58
|
+
/>
|
59
|
+
</svg>
|
60
|
+
{/snippet}
|
61
|
+
|
62
|
+
<fieldset
|
63
|
+
data-input="text-input"
|
64
|
+
class="flex w-full items-center rounded {fieldClasses}"
|
65
|
+
>
|
66
|
+
<legend class="px-2 {legendClasses}" class:error>{legendTitle}</legend>
|
67
|
+
<input
|
68
|
+
class="w-full border-none bg-transparent {classes}"
|
69
|
+
{type}
|
70
|
+
{placeholder}
|
71
|
+
{name}
|
72
|
+
{required}
|
73
|
+
{...attrs}
|
74
|
+
/>
|
75
|
+
{#if error}
|
76
|
+
{#if snippetWarning}
|
77
|
+
{@render snippetWarning()}
|
78
|
+
{:else}
|
79
|
+
{@render defaultWarning()}
|
80
|
+
{/if}
|
81
|
+
<!-- <img src={warningSymbol} class="mb-2 mr-8" alt="Warning" /> -->
|
82
|
+
{/if}
|
83
|
+
</fieldset>
|
package/dist/constants/index.js
CHANGED
@@ -0,0 +1,259 @@
|
|
1
|
+
/* Base prose styles */
|
2
|
+
.prose {
|
3
|
+
font-size: 1rem;
|
4
|
+
line-height: 1.75;
|
5
|
+
max-width: 65ch;
|
6
|
+
}
|
7
|
+
|
8
|
+
.prose > * + * {
|
9
|
+
margin-top: 1.25em;
|
10
|
+
}
|
11
|
+
|
12
|
+
/* Headings */
|
13
|
+
.prose h1 {
|
14
|
+
font-size: 2.25em;
|
15
|
+
line-height: 1.1111111;
|
16
|
+
margin-top: 0;
|
17
|
+
margin-bottom: 0.8888889em;
|
18
|
+
font-weight: 800;
|
19
|
+
}
|
20
|
+
|
21
|
+
.prose h2 {
|
22
|
+
font-size: 1.5em;
|
23
|
+
line-height: 1.3333333;
|
24
|
+
margin-top: 2em;
|
25
|
+
margin-bottom: 1em;
|
26
|
+
font-weight: 700;
|
27
|
+
}
|
28
|
+
|
29
|
+
.prose h3 {
|
30
|
+
font-size: 1.25em;
|
31
|
+
line-height: 1.6;
|
32
|
+
margin-top: 1.6em;
|
33
|
+
margin-bottom: 0.6em;
|
34
|
+
font-weight: 600;
|
35
|
+
}
|
36
|
+
|
37
|
+
.prose h4 {
|
38
|
+
font-size: 1.125em;
|
39
|
+
line-height: 1.5;
|
40
|
+
margin-top: 1.5em;
|
41
|
+
margin-bottom: 0.5em;
|
42
|
+
font-weight: 600;
|
43
|
+
}
|
44
|
+
|
45
|
+
/* Paragraphs */
|
46
|
+
.prose p {
|
47
|
+
margin-top: 1.25em;
|
48
|
+
margin-bottom: 1.25em;
|
49
|
+
}
|
50
|
+
|
51
|
+
/* Lists */
|
52
|
+
.prose ul,
|
53
|
+
.prose ol {
|
54
|
+
padding-left: 1.625em;
|
55
|
+
margin-top: 1.25em;
|
56
|
+
margin-bottom: 1.25em;
|
57
|
+
}
|
58
|
+
|
59
|
+
.prose li {
|
60
|
+
margin-top: 0.5em;
|
61
|
+
margin-bottom: 0.5em;
|
62
|
+
}
|
63
|
+
|
64
|
+
.prose > ul > li p {
|
65
|
+
margin-top: 0.75em;
|
66
|
+
margin-bottom: 0.75em;
|
67
|
+
}
|
68
|
+
|
69
|
+
.prose > ul > li > *:first-child {
|
70
|
+
margin-top: 1.25em;
|
71
|
+
}
|
72
|
+
|
73
|
+
.prose > ul > li > *:last-child {
|
74
|
+
margin-bottom: 1.25em;
|
75
|
+
}
|
76
|
+
|
77
|
+
/* Nested lists */
|
78
|
+
.prose ul ul,
|
79
|
+
.prose ul ol,
|
80
|
+
.prose ol ul,
|
81
|
+
.prose ol ol {
|
82
|
+
margin-top: 0.75em;
|
83
|
+
margin-bottom: 0.75em;
|
84
|
+
}
|
85
|
+
|
86
|
+
/* Links */
|
87
|
+
.prose a {
|
88
|
+
color: #111827;
|
89
|
+
text-decoration: underline;
|
90
|
+
font-weight: 500;
|
91
|
+
}
|
92
|
+
|
93
|
+
.prose a:hover {
|
94
|
+
text-decoration-thickness: 2px;
|
95
|
+
}
|
96
|
+
|
97
|
+
/* Code blocks */
|
98
|
+
.prose code {
|
99
|
+
color: #111827;
|
100
|
+
font-weight: 600;
|
101
|
+
font-size: 0.875em;
|
102
|
+
}
|
103
|
+
|
104
|
+
.prose pre {
|
105
|
+
color: #e5e7eb;
|
106
|
+
background-color: #1f2937;
|
107
|
+
overflow-x: auto;
|
108
|
+
font-size: 0.875em;
|
109
|
+
line-height: 1.7142857;
|
110
|
+
margin-top: 1.7142857em;
|
111
|
+
margin-bottom: 1.7142857em;
|
112
|
+
border-radius: 0.375rem;
|
113
|
+
padding: 0.8571429em 1.1428571em;
|
114
|
+
}
|
115
|
+
|
116
|
+
.prose pre code {
|
117
|
+
background-color: transparent;
|
118
|
+
border-radius: 0;
|
119
|
+
padding: 0;
|
120
|
+
font-weight: 400;
|
121
|
+
color: inherit;
|
122
|
+
font-size: inherit;
|
123
|
+
font-family: inherit;
|
124
|
+
line-height: inherit;
|
125
|
+
}
|
126
|
+
|
127
|
+
/* Blockquotes */
|
128
|
+
.prose blockquote {
|
129
|
+
font-weight: 500;
|
130
|
+
font-style: italic;
|
131
|
+
color: #111827;
|
132
|
+
border-left-width: 0.25rem;
|
133
|
+
border-left-color: #e5e7eb;
|
134
|
+
margin-top: 1.6em;
|
135
|
+
margin-bottom: 1.6em;
|
136
|
+
padding-left: 1em;
|
137
|
+
}
|
138
|
+
|
139
|
+
/* Tables */
|
140
|
+
.prose table {
|
141
|
+
width: 100%;
|
142
|
+
table-layout: auto;
|
143
|
+
text-align: left;
|
144
|
+
margin-top: 2em;
|
145
|
+
margin-bottom: 2em;
|
146
|
+
font-size: 0.875em;
|
147
|
+
line-height: 1.7142857;
|
148
|
+
}
|
149
|
+
|
150
|
+
.prose thead {
|
151
|
+
font-weight: 600;
|
152
|
+
border-bottom-width: 1px;
|
153
|
+
border-bottom-color: #d1d5db;
|
154
|
+
}
|
155
|
+
|
156
|
+
.prose thead th {
|
157
|
+
vertical-align: bottom;
|
158
|
+
padding-right: 0.5714286em;
|
159
|
+
padding-bottom: 0.5714286em;
|
160
|
+
padding-left: 0.5714286em;
|
161
|
+
}
|
162
|
+
|
163
|
+
.prose tbody tr {
|
164
|
+
border-bottom-width: 1px;
|
165
|
+
border-bottom-color: #e5e7eb;
|
166
|
+
}
|
167
|
+
|
168
|
+
.prose tbody td {
|
169
|
+
vertical-align: top;
|
170
|
+
padding: 0.5714286em;
|
171
|
+
}
|
172
|
+
|
173
|
+
/* Size variations */
|
174
|
+
.prose-sm {
|
175
|
+
font-size: 0.875rem;
|
176
|
+
line-height: 1.7142857;
|
177
|
+
}
|
178
|
+
|
179
|
+
.prose-lg {
|
180
|
+
font-size: 1.125rem;
|
181
|
+
line-height: 1.7777778;
|
182
|
+
}
|
183
|
+
|
184
|
+
.prose-xl {
|
185
|
+
font-size: 1.25rem;
|
186
|
+
line-height: 1.8;
|
187
|
+
}
|
188
|
+
|
189
|
+
/* Dark mode */
|
190
|
+
.prose-invert {
|
191
|
+
color: #d1d5db;
|
192
|
+
}
|
193
|
+
|
194
|
+
.prose-invert a {
|
195
|
+
color: #fff;
|
196
|
+
}
|
197
|
+
|
198
|
+
.prose-invert strong {
|
199
|
+
color: #fff;
|
200
|
+
}
|
201
|
+
|
202
|
+
.prose-invert code {
|
203
|
+
color: #fff;
|
204
|
+
}
|
205
|
+
|
206
|
+
.prose-invert thead {
|
207
|
+
border-bottom-color: #4b5563;
|
208
|
+
}
|
209
|
+
|
210
|
+
.prose-invert tbody tr {
|
211
|
+
border-bottom-color: #374151;
|
212
|
+
}
|
213
|
+
|
214
|
+
.prose-invert blockquote {
|
215
|
+
color: #9ca3af;
|
216
|
+
border-left-color: #4b5563;
|
217
|
+
}
|
218
|
+
|
219
|
+
/* Images */
|
220
|
+
.prose img {
|
221
|
+
margin-top: 2em;
|
222
|
+
margin-bottom: 2em;
|
223
|
+
}
|
224
|
+
|
225
|
+
.prose figure > * {
|
226
|
+
margin-top: 0;
|
227
|
+
margin-bottom: 0;
|
228
|
+
}
|
229
|
+
|
230
|
+
.prose figure figcaption {
|
231
|
+
color: #6b7280;
|
232
|
+
font-size: 0.875em;
|
233
|
+
line-height: 1.4285714;
|
234
|
+
margin-top: 0.8571429em;
|
235
|
+
}
|
236
|
+
|
237
|
+
/* Custom elements */
|
238
|
+
.prose hr {
|
239
|
+
border-color: #e5e7eb;
|
240
|
+
border-top-width: 1px;
|
241
|
+
margin-top: 3em;
|
242
|
+
margin-bottom: 3em;
|
243
|
+
}
|
244
|
+
|
245
|
+
.prose strong {
|
246
|
+
font-weight: 600;
|
247
|
+
color: #111827;
|
248
|
+
}
|
249
|
+
|
250
|
+
.prose em {
|
251
|
+
font-style: italic;
|
252
|
+
}
|
253
|
+
|
254
|
+
/* Focus styles */
|
255
|
+
.prose a:focus {
|
256
|
+
outline: 2px solid transparent;
|
257
|
+
outline-offset: 2px;
|
258
|
+
text-decoration-thickness: 2px;
|
259
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
/**
|
2
|
+
* Tailwind defaults
|
3
|
+
*
|
4
|
+
* text-sm 14px;
|
5
|
+
* text-base 16px;
|
6
|
+
* text-lg 18px;
|
7
|
+
* text-xl 20px;
|
8
|
+
* text-2xl 24px;
|
9
|
+
* text-3xl 30px;
|
10
|
+
* text-4xl 36px;
|
11
|
+
* text-5xl 48px;
|
12
|
+
* text-6xl 60px;
|
13
|
+
* text-7xl 72px;
|
14
|
+
* text-8xl 96px;
|
15
|
+
* text-9xl 128px;
|
16
|
+
*
|
17
|
+
* @see https://tailwindcss.com/docs/font-size
|
18
|
+
*/
|
19
|
+
|
20
|
+
@define-mixin all_text {
|
21
|
+
|
22
|
+
a {
|
23
|
+
@apply text-sm font-bold underline;
|
24
|
+
}
|
25
|
+
|
26
|
+
p {
|
27
|
+
@apply text-xl;
|
28
|
+
}
|
29
|
+
|
30
|
+
h1 {
|
31
|
+
&.text-presenter-title {
|
32
|
+
@apply text-5xl;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
package/package.json
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
export * from "./input-states.js";
|
@@ -1 +0,0 @@
|
|
1
|
-
export * from './input-states.js';
|
File without changes
|
File without changes
|