@hkdigital/lib-sveltekit 0.0.98 → 0.1.0

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.
@@ -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>
@@ -1,5 +1,4 @@
1
1
  export { default as ImageBox } from "./ImageBox.svelte";
2
- export { default as ResponsiveImage } from "./ResponsiveImage.svelte";
3
2
  declare const _default: {};
4
3
  export default _default;
5
4
  export type ImageMeta = import("../../config/typedef.js").ImageMeta;
@@ -1,6 +1,5 @@
1
1
  /** @typedef {import('../../config/typedef.js').ImageMeta} ImageMeta */
2
2
 
3
3
  export { default as ImageBox } from './ImageBox.svelte';
4
- export { default as ResponsiveImage } from './ResponsiveImage.svelte';
5
4
 
6
5
  export default {};
@@ -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/css-states/index.js';
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/css-states
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>
@@ -47,3 +47,10 @@ export const borderWidth: {
47
47
  '2p': string;
48
48
  '4p': string;
49
49
  };
50
+ export const strokeWidth: {
51
+ '1p': string;
52
+ '2p': string;
53
+ '4p': string;
54
+ '6p': string;
55
+ '8p': string;
56
+ };
@@ -54,3 +54,11 @@ export const borderWidth = {
54
54
  '2p': '0.125rem',
55
55
  '4p': '0.250rem'
56
56
  };
57
+
58
+ export const strokeWidth = {
59
+ '1p': '0.0625rem',
60
+ '2p': '0.125rem',
61
+ '4p': '0.250rem',
62
+ '6p': '0.375rem',
63
+ '8p': '0.5rem'
64
+ };
@@ -1,3 +1,3 @@
1
1
  export * as regexp from './regexp/index.js';
2
2
  export * as time from './time.js';
3
- //export * as cssStates from './css-states/index.js';
3
+ //export * as stateLabels from './state-labels/index.js';
@@ -0,0 +1,4 @@
1
+ export const IDLE: "idle";
2
+ export const SUBMITTING: "submitting";
3
+ export const SUBMIT_OK: "submit-ok";
4
+ export const SUBMIT_FAILED: "submit-failed";
@@ -0,0 +1,4 @@
1
+ export const IDLE = 'idle';
2
+ export const SUBMITTING = 'submitting';
3
+ export const SUBMIT_OK = 'submit-ok';
4
+ export const SUBMIT_FAILED = 'submit-failed';
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+
2
+ @define-mixin all_vars {
3
+
4
+ /* --tab-bar-height: 72px;
5
+ --tab-bar-selector-width: 132px;
6
+ --tab-bar-selector-height: var(--tab-bar-height, 72px); */
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-sveltekit",
3
- "version": "0.0.98",
3
+ "version": "0.1.0",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"
@@ -20,19 +20,19 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "dev": "vite dev",
23
- "build": "cross-env vite build && npm run package",
23
+ "build": "vite build && npm run package",
24
24
  "preview": "vite preview",
25
- "package": "cross-env svelte-kit sync && svelte-package && publint",
26
- "prepublishOnly": "cross-env npm run package",
27
- "publish:npm": "cross-env npm version patch && npm publish --access public && git push",
28
- "check": "cross-env svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
29
- "check:watch": "cross-env svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
25
+ "package": "svelte-kit sync && svelte-package && publint",
26
+ "prepublishOnly": "npm run package",
27
+ "publish:npm": "npm version patch && npm publish --access public && git push",
28
+ "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
29
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
30
30
  "format": "prettier --write .",
31
- "lint": "cross-env prettier --check . && eslint .",
31
+ "lint": "prettier --check . && eslint .",
32
32
  "test:unit": "vitest",
33
- "test": "cross-env npm run test:unit -- --run && npm run test:e2e",
33
+ "test": "npm run test:unit -- --run && npm run test:e2e",
34
34
  "test:e2e": "playwright test",
35
- "upgrade:all": "cross-env ncu -u && pnpm install"
35
+ "upgrade:all": "ncu -u && pnpm install"
36
36
  },
37
37
  "files": [
38
38
  "dist",
@@ -73,7 +73,6 @@
73
73
  "@tailwindcss/typography": "^0.5.16",
74
74
  "@types/eslint": "^9.6.1",
75
75
  "autoprefixer": "^10.4.20",
76
- "cross-env": "^7.0.3",
77
76
  "eslint": "^9.18.0",
78
77
  "eslint-config-prettier": "^10.0.1",
79
78
  "eslint-plugin-svelte": "^2.46.1",
@@ -1 +0,0 @@
1
- export * from "./input-states.js";
@@ -1 +0,0 @@
1
- export * from './input-states.js';