@rezcom/rez-components 0.0.2

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 ADDED
@@ -0,0 +1,58 @@
1
+ # Svelte library
2
+
3
+ Everything you need to build a Svelte library, powered by [`sv`](https://npmjs.com/package/sv).
4
+
5
+ Read more about creating a library [in the docs](https://svelte.dev/docs/kit/packaging).
6
+
7
+ ## Creating a project
8
+
9
+ If you're seeing this, you've probably already done this step. Congrats!
10
+
11
+ ```sh
12
+ # create a new project in the current directory
13
+ npx sv create
14
+
15
+ # create a new project in my-app
16
+ npx sv create my-app
17
+ ```
18
+
19
+ ## Developing
20
+
21
+ Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
22
+
23
+ ```sh
24
+ npm run dev
25
+
26
+ # or start the server and open the app in a new browser tab
27
+ npm run dev -- --open
28
+ ```
29
+
30
+ Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
31
+
32
+ ## Building
33
+
34
+ To build your library:
35
+
36
+ ```sh
37
+ npm pack
38
+ ```
39
+
40
+ To create a production version of your showcase app:
41
+
42
+ ```sh
43
+ npm run build
44
+ ```
45
+
46
+ You can preview the production build with `npm run preview`.
47
+
48
+ > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
49
+
50
+ ## Publishing
51
+
52
+ Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
53
+
54
+ To publish your library to [npm](https://www.npmjs.com):
55
+
56
+ ```sh
57
+ npm publish
58
+ ```
@@ -0,0 +1,5 @@
1
+ import Button from './ui/Button.svelte';
2
+ import Carousel from './ui/Carousel.svelte';
3
+ import Dropdown from './ui/Dropdown.svelte';
4
+ import Modal from './ui/Modal.svelte';
5
+ export { Button, Carousel, Dropdown, Modal };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import Button from './ui/Button.svelte';
2
+ import Carousel from './ui/Carousel.svelte';
3
+ import Dropdown from './ui/Dropdown.svelte';
4
+ import Modal from './ui/Modal.svelte';
5
+ // Reexport your entry components here
6
+ export { Button, Carousel, Dropdown, Modal };
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface ButtonProps {
5
+ text?: null | string;
6
+ active?: boolean; // Condition as to whether or not it's clickable
7
+
8
+ // TailwindCSS
9
+ color?: string;
10
+ textColor?: string;
11
+ inactiveColor?: string;
12
+ inactiveTextColor?: string;
13
+ ringColor?: string;
14
+ class?: string;
15
+
16
+ // Add ring and shine effects?
17
+ ring?: boolean;
18
+ shine?: boolean;
19
+
20
+ // href
21
+ href?: null | string;
22
+ target?: null | string;
23
+
24
+ // Interactivity - Use if the button should have behavior rather than redirecting to a link
25
+ onclick?: (() => void) | null;
26
+
27
+ // Child props
28
+ children?: null | Snippet;
29
+ }
30
+
31
+ let {
32
+ text = null,
33
+ active = true,
34
+
35
+ color = 'bg-indigo-700 hover:bg-indigo-800 active:bg-blue-900',
36
+ textColor = 'text-white ',
37
+ inactiveColor = 'bg-gray-700',
38
+ inactiveTextColor = 'text-white',
39
+
40
+ ringColor = 'hover:ring-indigo-800 focus-visible:ring-indigo-800',
41
+ class: otherClasses = '',
42
+
43
+ ring = true,
44
+ shine = true,
45
+
46
+ target = null,
47
+ href = null,
48
+
49
+ onclick = null,
50
+ children = null
51
+ }: ButtonProps = $props();
52
+
53
+ const baseClass =
54
+ 'group relative inline-flex overflow-hidden items-center justify-center rounded-md px-2 py-2 text-center uppercase tracking-wide focus:outline-hidden focus:ring-3 focus:ring-offset-white transition duration-200 ease-in-out'.concat(
55
+ active ? ' hover:cursor-pointer' : ''
56
+ );
57
+
58
+ const classes = [
59
+ otherClasses,
60
+ baseClass,
61
+ active ? color : inactiveColor,
62
+ active ? textColor : inactiveTextColor,
63
+ ring ? `hover:ring-2 hover:ring-offset-2 ${ringColor}` : ''
64
+ ].join(' ');
65
+ </script>
66
+
67
+ {#snippet inside()}
68
+ {#if shine}
69
+ <span
70
+ class="absolute left-0 -mt-12 h-32 w-1/2 translate-x-[250%] rotate-12 bg-white/20 transition-all duration-250 ease-out group-hover:translate-x-[2%]"
71
+ ></span>
72
+ {/if}
73
+ {@render children?.()}
74
+ {#if text}
75
+ {text}
76
+ {/if}
77
+ {/snippet}
78
+
79
+ {#if active}
80
+ {#if href}
81
+ <a {href} class={classes} {target}>
82
+ {@render inside()}
83
+ </a>
84
+ {:else if onclick}
85
+ <button {onclick} class={classes}>
86
+ {@render inside()}
87
+ </button>
88
+ {/if}
89
+ {:else}
90
+ <div class={classes}>
91
+ {@render inside()}
92
+ </div>
93
+ {/if}
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface ButtonProps {
3
+ text?: null | string;
4
+ active?: boolean;
5
+ color?: string;
6
+ textColor?: string;
7
+ inactiveColor?: string;
8
+ inactiveTextColor?: string;
9
+ ringColor?: string;
10
+ class?: string;
11
+ ring?: boolean;
12
+ shine?: boolean;
13
+ href?: null | string;
14
+ target?: null | string;
15
+ onclick?: (() => void) | null;
16
+ children?: null | Snippet;
17
+ }
18
+ declare const Button: import("svelte").Component<ButtonProps, {}, "">;
19
+ type Button = ReturnType<typeof Button>;
20
+ export default Button;
@@ -0,0 +1,311 @@
1
+ <script lang="ts">
2
+ import type { ImageMetadata } from 'astro';
3
+ import { Carousel } from 'flowbite';
4
+ import { onDestroy, onMount } from 'svelte';
5
+ import { measureImageHeights, preloadImages } from '../util/util.js';
6
+
7
+ let carousel: Carousel | null = $state(null);
8
+
9
+ let maxHeight = $state(0);
10
+ let aspectRatio = $state(1);
11
+
12
+ interface CarouselProps {
13
+ // List of image metadata's OR strings
14
+ images: (ImageMetadata | string)[];
15
+
16
+ // Carousel ID to make it unique
17
+ carouselID?: number;
18
+
19
+ // Whether or not the user can click to move the carousel pages.
20
+ clickablePages?: boolean;
21
+
22
+ // Duration for auto-slide, if any
23
+ // 0 Indicates none
24
+ duration?: number;
25
+
26
+ // How fast should the page switch? (as a TailwindCSS class)
27
+ animationSpeed?: string;
28
+
29
+ // Pause on mouse hover
30
+ pauseOnHover?: boolean;
31
+
32
+ // Override the height with a TailwindCSS class
33
+ heightOverride?: string;
34
+
35
+ // Default Page index
36
+ defaultPage?: number;
37
+
38
+ // Show Slider Indicators
39
+ showSliderIndicators?: boolean;
40
+
41
+ // Image Options (see interface below)
42
+ imageOptions?: ImageOptions;
43
+
44
+ // Carousel Bg Color (as a TailwindCSS class)
45
+ bgColor?: string;
46
+
47
+ // Object property (as a TailwindCSS class such as "object-cover")
48
+ object?: string;
49
+
50
+ // Other classes TailwindCSS
51
+ class?: string;
52
+ }
53
+
54
+ interface ImageOptions {
55
+ // Should the images be rounded?
56
+ rounded?: boolean;
57
+
58
+ // Image's height as a TailwindCSS class
59
+ height?: string;
60
+
61
+ // Move the image slightly up within the wrapping div? (Stupid Flowbite bug)
62
+ translateUp?: boolean;
63
+ }
64
+
65
+ const {
66
+ images = [],
67
+ carouselID = 0,
68
+ clickablePages = true,
69
+ defaultPage = 0,
70
+ showSliderIndicators = true,
71
+ animationSpeed = 'duration-700',
72
+ object = 'object-cover',
73
+ heightOverride,
74
+ imageOptions = {
75
+ rounded: true,
76
+ height: 'h-full',
77
+ translateUp: true
78
+ },
79
+ bgColor = '',
80
+ duration = 0,
81
+ pauseOnHover = true,
82
+ class: otherClasses = ''
83
+ }: CarouselProps = $props();
84
+
85
+ let isMouseHovering = $state(false);
86
+ let interval: ReturnType<typeof setInterval> | undefined = undefined;
87
+ let preloadedImages: HTMLImageElement[] = $state([]);
88
+
89
+ function nextPage() {
90
+ if (carousel) {
91
+ carousel.next();
92
+ } else {
93
+ console.error(
94
+ 'The carousel has not been initialized correctly in the mount and therefore the nextPage() function failed.'
95
+ );
96
+ }
97
+ }
98
+
99
+ function previousPage() {
100
+ if (carousel) {
101
+ carousel.prev();
102
+ } else {
103
+ console.error(
104
+ 'The carousel has not been initialized correctly in the mount and therefore the previousPage() function failed.'
105
+ );
106
+ }
107
+ }
108
+
109
+ function startAutoPlay(carousel: Carousel) {
110
+ if (duration) {
111
+ interval = setInterval(() => {
112
+ if (!isMouseHovering) {
113
+ carousel.next();
114
+ }
115
+ }, duration);
116
+ }
117
+ }
118
+
119
+ function stopAutoPlay() {
120
+ clearInterval(interval);
121
+ }
122
+
123
+ onMount(() => {
124
+ async function setup() {
125
+ // Preload images
126
+ preloadedImages = preloadImages(images);
127
+
128
+ // Measure and set max height after images load
129
+ const { height, ratio } = await measureImageHeights(preloadedImages);
130
+ maxHeight = height;
131
+ aspectRatio = ratio;
132
+
133
+ // Indicate carousel-item-ids
134
+ const items = preloadedImages
135
+ .map((item, i) => {
136
+ const el = document.getElementById(`carousel-item-${carouselID}-${i}`);
137
+
138
+ if (el) {
139
+ return { position: i, el };
140
+ }
141
+ console.warn(`Element with ID carousel-item-${carouselID}-${i} not found.`);
142
+ return null;
143
+ })
144
+ .filter((item): item is { position: number; el: HTMLElement } => item !== null);
145
+
146
+ // Indicate the options which outline the indicator ids
147
+ const options = {
148
+ defaultPosition: defaultPage,
149
+ indicators: {
150
+ activeClasses: 'bg-white',
151
+ items: preloadedImages
152
+ .map((item, i) => {
153
+ const el = document.getElementById(`carousel-indicator-${carouselID}-${i}`);
154
+ if (el) {
155
+ return { position: i, el };
156
+ }
157
+ console.warn(`Element with ID carousel-indicator-${carouselID}-${i} not found.`);
158
+ return null;
159
+ })
160
+ .filter((item): item is { position: number; el: HTMLElement } => item !== null)
161
+ }
162
+ };
163
+
164
+ // Handle resizes
165
+ const resizeHandler = async () => {
166
+ const { height, ratio } = await measureImageHeights(preloadedImages);
167
+ maxHeight = height;
168
+ aspectRatio = ratio;
169
+ };
170
+
171
+ window.addEventListener('resize', resizeHandler);
172
+
173
+ // Initialize the carousel
174
+ carousel = new Carousel(carouselElement, items, options);
175
+
176
+ startAutoPlay(carousel);
177
+ }
178
+
179
+ const carouselElement = document.getElementById(`default-carousel-${carouselID}`);
180
+
181
+ setup();
182
+ });
183
+
184
+ onDestroy(() => {
185
+ stopAutoPlay();
186
+ });
187
+ </script>
188
+
189
+ <menu
190
+ id="default-carousel-{carouselID}"
191
+ class={`${otherClasses} relative top-0 ${bgColor} overflow-hidden ${heightOverride ? heightOverride : ''}`}
192
+ style={!heightOverride && maxHeight ? `aspect-ratio: ${aspectRatio}` : ''}
193
+ onmouseenter={() => {
194
+ if (pauseOnHover) {
195
+ isMouseHovering = true;
196
+ }
197
+ }}
198
+ onmouseleave={() => {
199
+ isMouseHovering = false;
200
+ }}
201
+ >
202
+ <!-- Carousel wrapper -->
203
+ <div
204
+ class={`relative h-full w-full overflow-hidden ${bgColor} ${imageOptions.rounded ? 'rounded-md' : ''}`}
205
+ >
206
+ <!-- Items -->
207
+ {#if preloadedImages}
208
+ {#each preloadedImages as srcObject, i}
209
+ <div
210
+ class="{animationSpeed} flex h-full w-full items-center justify-center ease-in-out"
211
+ id="carousel-item-{carouselID}-{i}"
212
+ >
213
+ <img
214
+ alt=""
215
+ src={typeof srcObject === 'object' && srcObject !== null && 'src' in srcObject
216
+ ? srcObject.src
217
+ : srcObject}
218
+ class={`h-full w-full ${imageOptions.rounded ? 'rounded-md' : ''} ${object ?? 'object-contain'}`}
219
+ style={heightOverride ? '' : `aspect-ratio: ${aspectRatio};`}
220
+ />
221
+ </div>
222
+ {/each}
223
+ {:else}
224
+ <span class="font-bold italic">Loading Images Carousel...</span>
225
+ {/if}
226
+ </div>
227
+ <!-- Slider indicators -->
228
+ {#if showSliderIndicators}
229
+ <div
230
+ class="absolute bottom-5 left-1/2 z-30 flex -translate-x-1/2 space-x-3 rtl:space-x-reverse"
231
+ >
232
+ {#each images as item, index}
233
+ <button
234
+ id="carousel-indicator-{carouselID}-{index}"
235
+ type="button"
236
+ class="h-3 w-3 rounded-full"
237
+ aria-current="true"
238
+ aria-label="Slide {index}"
239
+ data-carousel-slide-to={index}
240
+ ></button>
241
+ {/each}
242
+ </div>
243
+ {/if}
244
+ <!-- Slider controls -->
245
+ {#if clickablePages}
246
+ <button
247
+ type="button"
248
+ class="group absolute start-0 top-0 z-30 flex h-full cursor-pointer items-center justify-center px-4 focus:outline-hidden"
249
+ onclick={previousPage}
250
+ >
251
+ <div
252
+ class={'absolute inset-0 bg-gradient-to-r from-black/50 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100'.concat(
253
+ ' ',
254
+ imageOptions.rounded ? 'rounded-l-md' : ''
255
+ )}
256
+ ></div>
257
+ <span
258
+ class="relative inline-flex h-10 w-10 items-center justify-center rounded-full bg-white/30 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus:ring-4 group-focus:ring-white group-focus:outline-hidden"
259
+ >
260
+ <svg
261
+ class="h-4 w-4 text-white"
262
+ aria-hidden="true"
263
+ xmlns="http://www.w3.org/2000/svg"
264
+ fill="none"
265
+ viewBox="0 0 6 10"
266
+ >
267
+ <path
268
+ stroke="currentColor"
269
+ stroke-linecap="round"
270
+ stroke-linejoin="round"
271
+ stroke-width="2"
272
+ d="M5 1 1 5l4 4"
273
+ />
274
+ </svg>
275
+ <span class="sr-only">Previous</span>
276
+ </span>
277
+ </button>
278
+ <button
279
+ type="button"
280
+ class="group absolute end-0 top-0 z-30 flex h-full cursor-pointer items-center justify-center px-4 focus:outline-hidden"
281
+ onclick={nextPage}
282
+ >
283
+ <div
284
+ class={'absolute inset-0 bg-gradient-to-l from-black/50 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100'.concat(
285
+ ' ',
286
+ imageOptions.rounded ? 'rounded-r-md' : ''
287
+ )}
288
+ ></div>
289
+ <span
290
+ class="relative inline-flex h-10 w-10 items-center justify-center rounded-full bg-white/30 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus:ring-4 group-focus:ring-white group-focus:outline-hidden"
291
+ >
292
+ <svg
293
+ class="h-4 w-4 text-white"
294
+ aria-hidden="true"
295
+ xmlns="http://www.w3.org/2000/svg"
296
+ fill="none"
297
+ viewBox="0 0 6 10"
298
+ >
299
+ <path
300
+ stroke="currentColor"
301
+ stroke-linecap="round"
302
+ stroke-linejoin="round"
303
+ stroke-width="2"
304
+ d="m1 9 4-4-4-4"
305
+ />
306
+ </svg>
307
+ <span class="sr-only">Next</span>
308
+ </span>
309
+ </button>
310
+ {/if}
311
+ </menu>
@@ -0,0 +1,25 @@
1
+ import type { ImageMetadata } from 'astro';
2
+ import { Carousel } from 'flowbite';
3
+ interface ImageOptions {
4
+ rounded?: boolean;
5
+ height?: string;
6
+ translateUp?: boolean;
7
+ }
8
+ interface CarouselProps {
9
+ images: (ImageMetadata | string)[];
10
+ carouselID?: number;
11
+ clickablePages?: boolean;
12
+ duration?: number;
13
+ animationSpeed?: string;
14
+ pauseOnHover?: boolean;
15
+ heightOverride?: string;
16
+ defaultPage?: number;
17
+ showSliderIndicators?: boolean;
18
+ imageOptions?: ImageOptions;
19
+ bgColor?: string;
20
+ object?: string;
21
+ class?: string;
22
+ }
23
+ declare const Carousel: import("svelte").Component<CarouselProps, {}, "">;
24
+ type Carousel = ReturnType<typeof Carousel>;
25
+ export default Carousel;
@@ -0,0 +1,126 @@
1
+ <script lang="ts">
2
+ import type { DropdownItem } from '../util/type.js';
3
+ import { onMount } from 'svelte';
4
+ import { fly } from 'svelte/transition';
5
+
6
+ interface DropdownProps {
7
+ // Options
8
+ // Label before options chosen
9
+ defaultLabel?: string;
10
+ // First item is loaded by default?
11
+ firstItemDefault?: boolean;
12
+
13
+ // Function that fires when the user selects something
14
+ onselect: (selection: string) => void;
15
+
16
+ // List of selections
17
+ items: DropdownItem[];
18
+
19
+ // TailwindCSS
20
+
21
+ itemColor?: string; // Styling for individual items
22
+ textColor?: string;
23
+ color?: string;
24
+ hoverColor?: string;
25
+ ringColor?: string;
26
+
27
+ minWidth?: string;
28
+ textSize?: string;
29
+ class?: string;
30
+ }
31
+
32
+ // Props
33
+ let {
34
+ defaultLabel = '',
35
+ firstItemDefault = true,
36
+ onselect,
37
+ items,
38
+ itemColor = 'hover:bg-indigo-900',
39
+
40
+ textColor = 'text-white',
41
+ color = 'bg-indigo-700',
42
+ hoverColor = 'bg-indigo-800',
43
+ ringColor = 'hover:ring-indigo-800 focus-visible:ring-indigo-800',
44
+ minWidth = 'min-w-full',
45
+ textSize = 'text-md',
46
+ class: otherClasses = ''
47
+ }: DropdownProps = $props();
48
+
49
+ let dropdownOpen = $state(false);
50
+ let currItem: DropdownItem | null = $state(null);
51
+
52
+ onMount(() => {
53
+ // Initially set the current option to the first one
54
+ if (firstItemDefault) {
55
+ currItem = items[0];
56
+ onselect(currItem.value);
57
+ }
58
+ });
59
+ </script>
60
+
61
+ <div class={`relative ${otherClasses}`}>
62
+ <form
63
+ onmouseleave={() => {
64
+ dropdownOpen = false;
65
+ }}
66
+ >
67
+ <button
68
+ onmouseenter={() => {
69
+ dropdownOpen = true;
70
+ }}
71
+ class={`flex ${textSize} ${color} ${textColor} ${ringColor} ${minWidth} items-center rounded-t-lg px-5 py-2.5 text-center transition duration-200 `.concat(
72
+ dropdownOpen ? `rounded-br-lg ${hoverColor}` : 'rounded-b-lg'
73
+ )}
74
+ type="button"
75
+ >
76
+ {#if items && currItem !== null}
77
+ {currItem.label}
78
+ {:else}
79
+ {defaultLabel}
80
+ {/if}
81
+
82
+ <!-- Down Arrow -->
83
+ <div class="ml-auto">
84
+ <svg
85
+ class="ms-3 h-2.5 w-2.5"
86
+ aria-hidden="true"
87
+ xmlns="http://www.w3.org/2000/svg"
88
+ fill="none"
89
+ viewBox="0 0 10 6"
90
+ >
91
+ <path
92
+ stroke="currentColor"
93
+ stroke-linecap="round"
94
+ stroke-linejoin="round"
95
+ stroke-width="2"
96
+ d="m1 1 4 4 4-4"
97
+ />
98
+ </svg>
99
+ </div>
100
+ </button>
101
+ <!-- Dropdown menu -->
102
+ {#if dropdownOpen}
103
+ <div
104
+ transition:fly={{ duration: 150, y: -10 }}
105
+ class={`${hoverColor} ${minWidth} absolute z-10 rounded-tr-lg rounded-b-lg shadow-2xl`}
106
+ >
107
+ <ul class={`py-2 ${textSize} ${textColor}`}>
108
+ {#each items as dropdownOption}
109
+ <li>
110
+ <button
111
+ onclick={() => {
112
+ onselect(dropdownOption.value);
113
+ currItem = dropdownOption;
114
+ dropdownOpen = false;
115
+ }}
116
+ class={`${itemColor} w-full cursor-pointer px-4 py-2 transition duration-200 ease-in-out`}
117
+ >
118
+ {dropdownOption.label}
119
+ </button>
120
+ </li>
121
+ {/each}
122
+ </ul>
123
+ </div>
124
+ {/if}
125
+ </form>
126
+ </div>
@@ -0,0 +1,18 @@
1
+ import type { DropdownItem } from '../util/type.js';
2
+ interface DropdownProps {
3
+ defaultLabel?: string;
4
+ firstItemDefault?: boolean;
5
+ onselect: (selection: string) => void;
6
+ items: DropdownItem[];
7
+ itemColor?: string;
8
+ textColor?: string;
9
+ color?: string;
10
+ hoverColor?: string;
11
+ ringColor?: string;
12
+ minWidth?: string;
13
+ textSize?: string;
14
+ class?: string;
15
+ }
16
+ declare const Dropdown: import("svelte").Component<DropdownProps, {}, "">;
17
+ type Dropdown = ReturnType<typeof Dropdown>;
18
+ export default Dropdown;
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface HeroProps {
5
+ // Background of the hero image
6
+ background: Snippet;
7
+
8
+ // Content
9
+ children: Snippet;
10
+
11
+ // Height of the child div
12
+ childHeight?: string;
13
+ }
14
+
15
+ const { background, children, childHeight = 'h-full' }: HeroProps = $props();
16
+ </script>
17
+
18
+ <main class="relative h-max overflow-hidden">
19
+ <div class="absolute inset-0 top-0 -z-50">
20
+ {@render background()}
21
+ </div>
22
+ <div
23
+ class={`${childHeight} relative z-0 flex flex-col items-center justify-center p-4 text-center`}
24
+ >
25
+ {@render children()}
26
+ </div>
27
+ </main>
@@ -0,0 +1,9 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface HeroProps {
3
+ background: Snippet;
4
+ children: Snippet;
5
+ childHeight?: string;
6
+ }
7
+ declare const Hero: import("svelte").Component<HeroProps, {}, "">;
8
+ type Hero = ReturnType<typeof Hero>;
9
+ export default Hero;
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface ModalProps {
5
+ // Function that fires when the modal is closed
6
+ onclose: () => void;
7
+ // Whether or not to show the modal
8
+ showModal: boolean;
9
+
10
+ styling?: string; // Styles in TailwindCSS
11
+ blurStyle?: string;
12
+
13
+ // Clicking Closes?
14
+ clickingBackdropCloses?: boolean;
15
+ clickingModalCloses?: boolean;
16
+
17
+ children: Snippet;
18
+ }
19
+
20
+ const {
21
+ onclose,
22
+ showModal = false,
23
+ styling = 'bg-gradient-to-b from-slate-700/20 to-gray-700/20 border border-slate-500/60',
24
+ blurStyle = 'backdrop-blur-md',
25
+ clickingBackdropCloses = true,
26
+ clickingModalCloses = false,
27
+ children
28
+ }: ModalProps = $props();
29
+ </script>
30
+
31
+ {#if showModal}
32
+ <!-- Backdrop -->
33
+ <button
34
+ class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 hover:cursor-default"
35
+ onclick={() => {
36
+ if (clickingBackdropCloses) {
37
+ onclose();
38
+ }
39
+ }}
40
+ >
41
+ <!-- Modal container -->
42
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
43
+ <div
44
+ role="alertdialog"
45
+ tabindex={-1}
46
+ class={`${styling} ${blurStyle} max-h-[90%] max-w-[90%] overflow-x-hidden overflow-y-scroll rounded-xl p-6 shadow-lg`}
47
+ onclick={(e) => {
48
+ if (clickingModalCloses) {
49
+ onclose();
50
+ } else {
51
+ e.stopPropagation();
52
+ }
53
+ }}
54
+ style="-ms-overflow-style: none; scrollbar-width: none;"
55
+ >
56
+ {@render children()}
57
+ </div>
58
+ </button>
59
+ {/if}
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface ModalProps {
3
+ onclose: () => void;
4
+ showModal: boolean;
5
+ styling?: string;
6
+ blurStyle?: string;
7
+ clickingBackdropCloses?: boolean;
8
+ clickingModalCloses?: boolean;
9
+ children: Snippet;
10
+ }
11
+ declare const Modal: import("svelte").Component<ModalProps, {}, "">;
12
+ type Modal = ReturnType<typeof Modal>;
13
+ export default Modal;
@@ -0,0 +1,4 @@
1
+ export interface DropdownItem {
2
+ label: string;
3
+ value: string;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { ImageMetadata } from 'astro';
2
+ export declare function getImageSource(image: HTMLImageElement | ImageMetadata | string): string;
3
+ export declare function measureImageHeights(imageElements: HTMLImageElement[], imageStyle?: string): Promise<{
4
+ height: number;
5
+ ratio: number;
6
+ }>;
7
+ export declare function preloadImages(images: (ImageMetadata | string)[], imageClass?: string): HTMLImageElement[];
@@ -0,0 +1,47 @@
1
+ // Given an ImageMetadata or a string representing an image, returns the source
2
+ export function getImageSource(image) {
3
+ return typeof image === 'object' && 'src' in image ? image.src : image;
4
+ }
5
+ // Measure the height and aspect ratio of each image as it would be rendered
6
+ export async function measureImageHeights(imageElements, imageStyle) {
7
+ const hiddenContainer = document.createElement('div');
8
+ hiddenContainer.style.visibility = 'hidden';
9
+ hiddenContainer.style.position = 'absolute';
10
+ hiddenContainer.style.top = '-9999px';
11
+ hiddenContainer.style.left = '-9999px';
12
+ hiddenContainer.style.width = '1000px'; // simulate real layout width
13
+ document.body.appendChild(hiddenContainer);
14
+ const imageObjects = imageElements.map((img) => {
15
+ const image = new Image();
16
+ image.src = getImageSource(img);
17
+ image.className = imageStyle ?? '';
18
+ hiddenContainer.appendChild(image);
19
+ return image;
20
+ });
21
+ // Wait until all images load and can be measured
22
+ await Promise.all(imageObjects.map((img) => new Promise((resolve) => {
23
+ img.onload = () => resolve();
24
+ })));
25
+ let maxHeight = 0;
26
+ let bestRatio = 1;
27
+ for (const img of imageObjects) {
28
+ const height = img.offsetHeight;
29
+ const width = img.offsetWidth;
30
+ const ratio = width / height;
31
+ if (height > maxHeight) {
32
+ maxHeight = height;
33
+ bestRatio = ratio;
34
+ }
35
+ }
36
+ document.body.removeChild(hiddenContainer);
37
+ return { height: maxHeight, ratio: bestRatio };
38
+ }
39
+ // Returns a list of image objects given a list of ImageMetadatas / image sources
40
+ export function preloadImages(images, imageClass) {
41
+ return images.map((img) => {
42
+ const image = new Image();
43
+ image.src = getImageSource(img);
44
+ image.className = imageClass ?? '';
45
+ return image;
46
+ });
47
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@rezcom/rez-components",
3
+ "version": "0.0.2",
4
+ "scripts": {
5
+ "dev": "vite dev",
6
+ "build": "vite build && npm run prepack",
7
+ "preview": "vite preview",
8
+ "prepare": "svelte-kit sync || echo ''",
9
+ "prepack": "svelte-kit sync && svelte-package && publint",
10
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
+ "format": "prettier --write .",
13
+ "lint": "prettier --check . && eslint ."
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "!dist/**/*.test.*",
18
+ "!dist/**/*.spec.*"
19
+ ],
20
+ "sideEffects": [
21
+ "**/*.css"
22
+ ],
23
+ "svelte": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "svelte": "./dist/index.js"
30
+ }
31
+ },
32
+ "peerDependencies": {
33
+ "svelte": "^5.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@eslint/compat": "^1.3.2",
37
+ "@eslint/js": "^9.36.0",
38
+ "@sveltejs/adapter-auto": "^6.1.0",
39
+ "@sveltejs/kit": "^2.42.2",
40
+ "@sveltejs/package": "^2.5.2",
41
+ "@sveltejs/vite-plugin-svelte": "^6.2.0",
42
+ "@tailwindcss/forms": "^0.5.10",
43
+ "@tailwindcss/typography": "^0.5.18",
44
+ "@tailwindcss/vite": "^4.1.13",
45
+ "@types/node": "^24.5.2",
46
+ "astro": "^5.13.9",
47
+ "eslint": "^9.36.0",
48
+ "eslint-config-prettier": "^10.1.8",
49
+ "eslint-plugin-svelte": "^3.12.3",
50
+ "flowbite": "^3.1.2",
51
+ "globals": "^16.4.0",
52
+ "prettier": "^3.6.2",
53
+ "prettier-plugin-svelte": "^3.4.0",
54
+ "prettier-plugin-tailwindcss": "^0.6.14",
55
+ "publint": "^0.3.13",
56
+ "svelte": "^5.39.2",
57
+ "svelte-check": "^4.3.1",
58
+ "tailwindcss": "^4.1.13",
59
+ "typescript": "^5.9.2",
60
+ "typescript-eslint": "^8.44.0",
61
+ "vite": "^7.1.6"
62
+ },
63
+ "keywords": [
64
+ "svelte"
65
+ ],
66
+ "pnpm": {
67
+ "onlyBuiltDependencies": [
68
+ "@tailwindcss/oxide",
69
+ "esbuild",
70
+ "sharp"
71
+ ]
72
+ }
73
+ }