zone5 0.0.0 → 1.0.1

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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +258 -0
  3. package/dist/cli/index.js +33 -0
  4. package/dist/cli/index2.js +238 -0
  5. package/dist/cli/index3.js +53 -0
  6. package/dist/cli/templates/.zone5.toml +4 -0
  7. package/dist/cli/templates/app.css +3 -0
  8. package/dist/cli/templates/layout.svelte +15 -0
  9. package/dist/cli/templates/layout.ts +1 -0
  10. package/dist/components/Zone5.svelte +153 -0
  11. package/dist/components/Zone5.svelte.d.ts +12 -0
  12. package/dist/components/Zone5Img.svelte +103 -0
  13. package/dist/components/Zone5Img.svelte.d.ts +10 -0
  14. package/dist/components/Zone5Lightbox.svelte +131 -0
  15. package/dist/components/Zone5Lightbox.svelte.d.ts +12 -0
  16. package/dist/components/Zone5Provider.svelte +68 -0
  17. package/dist/components/Zone5Provider.svelte.d.ts +9 -0
  18. package/dist/components/atoms/Button.svelte +40 -0
  19. package/dist/components/atoms/Button.svelte.d.ts +13 -0
  20. package/dist/components/atoms/CloseButton.svelte +18 -0
  21. package/dist/components/atoms/CloseButton.svelte.d.ts +9 -0
  22. package/dist/components/atoms/NextButton.svelte +19 -0
  23. package/dist/components/atoms/NextButton.svelte.d.ts +10 -0
  24. package/dist/components/atoms/PrevButton.svelte +19 -0
  25. package/dist/components/atoms/PrevButton.svelte.d.ts +10 -0
  26. package/dist/components/atoms/index.d.ts +4 -0
  27. package/dist/components/atoms/index.js +5 -0
  28. package/dist/components/constants.d.ts +17 -0
  29. package/dist/components/constants.js +17 -0
  30. package/dist/components/index.d.ts +6 -0
  31. package/dist/components/index.js +7 -0
  32. package/dist/components/portal.d.ts +4 -0
  33. package/dist/components/portal.js +26 -0
  34. package/dist/components/types.d.ts +7 -0
  35. package/dist/components/types.js +1 -0
  36. package/dist/config.d.ts +51 -0
  37. package/dist/config.js +56 -0
  38. package/dist/index.d.ts +6 -0
  39. package/dist/index.js +4 -0
  40. package/dist/module.d.ts +19 -0
  41. package/dist/processor/blurhash.d.ts +7 -0
  42. package/dist/processor/blurhash.js +37 -0
  43. package/dist/processor/color.d.ts +5 -0
  44. package/dist/processor/color.js +32 -0
  45. package/dist/processor/config.d.ts +12 -0
  46. package/dist/processor/config.js +9 -0
  47. package/dist/processor/exif/converters.d.ts +7 -0
  48. package/dist/processor/exif/converters.js +38 -0
  49. package/dist/processor/exif/defaults.d.ts +17 -0
  50. package/dist/processor/exif/defaults.js +17 -0
  51. package/dist/processor/exif/exif.d.ts +34 -0
  52. package/dist/processor/exif/exif.js +43 -0
  53. package/dist/processor/exif/index.d.ts +1 -0
  54. package/dist/processor/exif/index.js +1 -0
  55. package/dist/processor/exif/types.d.ts +4 -0
  56. package/dist/processor/exif/types.js +1 -0
  57. package/dist/processor/file.d.ts +3 -0
  58. package/dist/processor/file.js +28 -0
  59. package/dist/processor/index.d.ts +27 -0
  60. package/dist/processor/index.js +70 -0
  61. package/dist/processor/variants.d.ts +13 -0
  62. package/dist/processor/variants.js +83 -0
  63. package/dist/remark.d.ts +5 -0
  64. package/dist/remark.js +268 -0
  65. package/dist/stores/index.d.ts +1 -0
  66. package/dist/stores/index.js +1 -0
  67. package/dist/stores/registry.svelte.d.ts +22 -0
  68. package/dist/stores/registry.svelte.js +100 -0
  69. package/dist/test-setup.d.ts +9 -0
  70. package/dist/test-setup.js +25 -0
  71. package/dist/vite.d.ts +2 -0
  72. package/dist/vite.js +158 -0
  73. package/package.json +144 -5
@@ -0,0 +1,153 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+
4
+ import Img from './Zone5Img.svelte';
5
+ import { useImageRegistry } from './Zone5Provider.svelte';
6
+ import {
7
+ DEFAULT_COLUMN_BREAKPOINTS,
8
+ SINGLE_IMAGE_HEIGHT_CLASS,
9
+ WALL_IMAGE_HEIGHT_CLASS,
10
+ } from './constants';
11
+ import type { ImageData } from './types';
12
+
13
+ interface Props {
14
+ columnBreakpoints?: { [key: number]: number };
15
+ images: ImageData[];
16
+ mode?: 'wall' | 'waterfall';
17
+ nocaption?: boolean;
18
+ }
19
+
20
+ let {
21
+ columnBreakpoints = DEFAULT_COLUMN_BREAKPOINTS,
22
+ images,
23
+ mode = 'wall',
24
+ nocaption = false,
25
+ }: Props = $props();
26
+
27
+ const imageStore = useImageRegistry();
28
+ const componentId: symbol = Symbol('Zone5Component');
29
+
30
+ // Register images with the global image registry
31
+ $effect(() => {
32
+ if (imageStore) {
33
+ imageStore.register(componentId, images);
34
+ }
35
+ });
36
+
37
+ // Cleanup on component unmount
38
+ onMount(() => {
39
+ return () => {
40
+ if (imageStore) {
41
+ imageStore.remove(componentId);
42
+ }
43
+ };
44
+ });
45
+
46
+ /**
47
+ * Opens the lightbox for the image at the specified index
48
+ */
49
+ function handleImageClick(index: number): void {
50
+ if (imageStore) {
51
+ imageStore.setCurrent(componentId, index);
52
+ }
53
+ }
54
+
55
+ // WATERFALL MODE CALCULATIONS
56
+ let containerWidth = $state(0);
57
+
58
+ /**
59
+ * Calculate number of columns based on container width and breakpoints
60
+ */
61
+ let nColumns = $derived.by(() => {
62
+ if (mode !== 'waterfall') return 1;
63
+
64
+ let columns = 1;
65
+ const sortedBreakpoints = Object.entries(columnBreakpoints).sort(
66
+ ([a], [b]) => Number(a) - Number(b),
67
+ );
68
+
69
+ for (const [breakpoint, cols] of sortedBreakpoints) {
70
+ if (containerWidth >= Number(breakpoint)) {
71
+ columns = cols;
72
+ }
73
+ }
74
+
75
+ return columns;
76
+ });
77
+
78
+ /**
79
+ * Distribute images across columns in round-robin fashion
80
+ */
81
+ let colPhotos = $derived.by(() => {
82
+ if (mode !== 'waterfall') return [];
83
+
84
+ const cols: { image: ImageData; idx: number }[][] = Array.from({ length: nColumns }, () => []);
85
+ images.forEach((image, idx) => {
86
+ cols[idx % nColumns].push({ image, idx });
87
+ });
88
+
89
+ return cols;
90
+ });
91
+
92
+ /**
93
+ * Calculate filler heights to equalize column heights in waterfall mode
94
+ */
95
+ let colFillers = $derived.by(() => {
96
+ if (mode !== 'waterfall') return [];
97
+
98
+ const totalHeights = colPhotos.map((col) =>
99
+ col.reduce((sum, img) => sum + 1 / img.image.properties.aspectRatio, 0),
100
+ );
101
+ const maxHeight = Math.max(...totalHeights);
102
+
103
+ return totalHeights.map((height) => maxHeight - height);
104
+ });
105
+ </script>
106
+
107
+ {#if mode === 'wall' && images.length === 1}
108
+ {@const image = images[0]}
109
+ <figure class="flex justify-center" aria-label={image.properties.title || 'Image'}>
110
+ <Img
111
+ {image}
112
+ class={SINGLE_IMAGE_HEIGHT_CLASS}
113
+ onclick={imageStore ? () => handleImageClick(0) : undefined}
114
+ />
115
+ {#if image.properties.title && !nocaption}
116
+ <figcaption class="mt-2 text-sm text-gray-600">
117
+ {image.properties.title}
118
+ </figcaption>
119
+ {/if}
120
+ </figure>
121
+ {:else if mode === 'wall'}
122
+ <div class="flex gap-2 flex-col md:flex-row md:flex-wrap zone5-wall" role="list">
123
+ {#each images as image, idx (idx)}
124
+ <div class="grow {WALL_IMAGE_HEIGHT_CLASS} flex" role="listitem">
125
+ <Img
126
+ {image}
127
+ cover
128
+ class="grow"
129
+ onclick={imageStore ? () => handleImageClick(idx) : undefined}
130
+ />
131
+ </div>
132
+ {/each}
133
+ </div>
134
+ {:else if mode === 'waterfall'}
135
+ <div class="flex gap-2" bind:clientWidth={containerWidth} role="list">
136
+ {#each Array.from({ length: nColumns }, (_, i) => i) as columnId (columnId)}
137
+ <div class="flex flex-col gap-2" role="listitem">
138
+ {#each colPhotos[columnId] as { image, idx } (idx)}
139
+ <div>
140
+ <Img {image} onclick={imageStore ? () => handleImageClick(idx) : undefined} />
141
+ </div>
142
+ {/each}
143
+ {#if colFillers[columnId] > 0}
144
+ <div
145
+ class="bg-slate-200 rounded"
146
+ style:height={colFillers[columnId] * 100 + '%'}
147
+ aria-hidden="true"
148
+ ></div>
149
+ {/if}
150
+ </div>
151
+ {/each}
152
+ </div>
153
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { ImageData } from './types';
2
+ interface Props {
3
+ columnBreakpoints?: {
4
+ [key: number]: number;
5
+ };
6
+ images: ImageData[];
7
+ mode?: 'wall' | 'waterfall';
8
+ nocaption?: boolean;
9
+ }
10
+ declare const Zone5: import("svelte").Component<Props, {}, "">;
11
+ type Zone5 = ReturnType<typeof Zone5>;
12
+ export default Zone5;
@@ -0,0 +1,103 @@
1
+ <script lang="ts">
2
+ import type { Action } from 'svelte/action';
3
+
4
+ import type { ImageData } from './types';
5
+
6
+ interface Props {
7
+ image: ImageData;
8
+ class?: string;
9
+ cover?: boolean;
10
+ onclick?: () => void;
11
+ }
12
+
13
+ let { image, class: _class, cover, onclick }: Props = $props();
14
+
15
+ const sizes = $derived.by(() => {
16
+ const widths = image.assets.map((a) => a.width).sort((a, b) => a - b);
17
+ const maxWidth = Math.max(...widths);
18
+
19
+ // Generate breakpoints based on actual asset widths
20
+ const breakpoints = [];
21
+ if (maxWidth >= 1200) breakpoints.push('(min-width: 1200px) 1200px');
22
+ if (maxWidth >= 768) breakpoints.push('(min-width: 768px) 768px');
23
+ breakpoints.push(`${Math.min(maxWidth, 640)}px`);
24
+
25
+ return breakpoints.join(', ');
26
+ });
27
+
28
+ let img: HTMLImageElement;
29
+ let loaded = $state(false);
30
+ $effect(() => {
31
+ loaded = img.complete;
32
+ const handleLoad = () => (loaded = true);
33
+ img.addEventListener('load', handleLoad);
34
+ return () => {
35
+ img.removeEventListener('load', handleLoad);
36
+ };
37
+ });
38
+
39
+ const useOnclick: Action<HTMLDivElement, (() => void) | undefined> = (node, handler) => {
40
+ const setHandler = (handler?: () => void | undefined) => {
41
+ const keyHandler = (event: KeyboardEvent) => {
42
+ if (!handler) return;
43
+ if (event.key === ' ' || event.key === 'Enter') {
44
+ event.preventDefault();
45
+ handler();
46
+ }
47
+ };
48
+ if (handler) {
49
+ node.onclick = handler;
50
+ node.onkeydown = keyHandler;
51
+ node.role = 'button';
52
+ node.tabIndex = 0;
53
+ node.classList.add('cursor-pointer');
54
+ } else {
55
+ node.onclick = null;
56
+ node.onkeydown = null;
57
+ node.role = null;
58
+ node.tabIndex = -1;
59
+ node.classList.remove('cursor-pointer');
60
+ }
61
+ };
62
+ setHandler(handler);
63
+ return {
64
+ update: (handler) => setHandler(handler),
65
+ destroy: () => setHandler(),
66
+ };
67
+ };
68
+ </script>
69
+
70
+ <div
71
+ use:useOnclick={onclick}
72
+ style="background-color:{image.properties.averageColor.hex}; --aspect-ratio: {image.properties
73
+ .aspectRatio}"
74
+ class={['aspect-ratio', 'relative', _class]}
75
+ data-zone5-img="true"
76
+ >
77
+ <picture class="w-full h-full flex justify-center relative mb-0 mt-0">
78
+ <img
79
+ src={image.assets[0].href}
80
+ alt={image.properties.alt}
81
+ title={image.properties.title}
82
+ srcset={image.assets.map((asset) => `${asset.href} ${asset.width}w`).join(', ')}
83
+ {sizes}
84
+ loading="lazy"
85
+ decoding="async"
86
+ class={[
87
+ 'transition-opacity duration-100',
88
+ {
89
+ 'object-cover h-full w-full': cover,
90
+ 'opacity-0': !loaded,
91
+ 'opacity-100': loaded,
92
+ },
93
+ ]}
94
+ bind:this={img}
95
+ />
96
+ </picture>
97
+ </div>
98
+
99
+ <style>
100
+ .aspect-ratio {
101
+ aspect-ratio: var(--aspect-ratio) auto;
102
+ }
103
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { ImageData } from './types';
2
+ interface Props {
3
+ image: ImageData;
4
+ class?: string;
5
+ cover?: boolean;
6
+ onclick?: () => void;
7
+ }
8
+ declare const Zone5Img: import("svelte").Component<Props, {}, "">;
9
+ type Zone5Img = ReturnType<typeof Zone5Img>;
10
+ export default Zone5Img;
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+
4
+ import Img from './Zone5Img.svelte';
5
+ import CloseButton from './atoms/CloseButton.svelte';
6
+ import NextButton from './atoms/NextButton.svelte';
7
+ import PrevButton from './atoms/PrevButton.svelte';
8
+ import portal from './portal';
9
+ import type { ImageData } from './types';
10
+
11
+ interface Props {
12
+ force?: boolean;
13
+ image?: ImageData;
14
+ onclose: () => void;
15
+ onnext?: () => void;
16
+ onprevious?: () => void;
17
+ transitionDuration?: number;
18
+ }
19
+
20
+ let { force, image, onclose, onnext, onprevious, transitionDuration = 300 }: Props = $props();
21
+
22
+ let transitioning = $state(false);
23
+ let visible = $derived(image && !transitioning);
24
+ let onFigureOutroEnd: (() => void) | undefined = undefined;
25
+ const nextHandler = () => {
26
+ transitioning = true;
27
+ onFigureOutroEnd = onnext;
28
+ };
29
+ const prevHandler = () => {
30
+ transitioning = true;
31
+ onFigureOutroEnd = onprevious;
32
+ };
33
+
34
+ const keyHandler = (evt: KeyboardEvent) => {
35
+ if (!image) return;
36
+ switch (evt.key) {
37
+ case 'Escape':
38
+ evt.preventDefault();
39
+ onclose();
40
+ break;
41
+ case ' ':
42
+ // Only prevent default for spacebar when dialog is active
43
+ evt.preventDefault();
44
+ if (visible) {
45
+ nextHandler();
46
+ }
47
+ break;
48
+ case 'ArrowRight':
49
+ if (visible) {
50
+ evt.preventDefault();
51
+ nextHandler();
52
+ }
53
+ break;
54
+ case 'ArrowLeft':
55
+ if (visible) {
56
+ evt.preventDefault();
57
+ prevHandler();
58
+ }
59
+ break;
60
+ }
61
+ };
62
+ </script>
63
+
64
+ <svelte:window onkeydown={keyHandler} />
65
+ {#if image || force}
66
+ <section
67
+ use:portal
68
+ role="dialog"
69
+ transition:fade={{ duration: force ? 0 : transitionDuration }}
70
+ class="fixed z-50 inset-0 bg-zinc-50 text-zinc-950"
71
+ data-zone5-lightbox="true"
72
+ >
73
+ <nav>
74
+ <CloseButton
75
+ class={[
76
+ 'absolute right-0 top-0 z-30 cursor-pointer p-4',
77
+ 'transition delay-150 duration-300 hover:bg-zinc-100/10',
78
+ ]}
79
+ {onclose}
80
+ data-zone5-close
81
+ />
82
+ {#if onprevious}
83
+ <PrevButton
84
+ class={[
85
+ 'absolute left-0 top-1/2 z-30 -translate-y-1/2 cursor-pointer p-2',
86
+ 'transition delay-150 duration-300 hover:bg-zinc-100/10',
87
+ ]}
88
+ disabled={!visible}
89
+ onprevious={prevHandler}
90
+ data-zone5-prev
91
+ />
92
+ {/if}
93
+ {#if onnext}
94
+ <NextButton
95
+ class={[
96
+ 'absolute right-0 top-1/2 z-30 -translate-y-1/2 cursor-pointer p-2',
97
+ 'transition delay-150 duration-300 hover:bg-zinc-100/10',
98
+ ]}
99
+ disabled={!visible}
100
+ onnext={nextHandler}
101
+ data-zone5-next
102
+ />
103
+ {/if}
104
+ </nav>
105
+ {#if image && visible}
106
+ <figure
107
+ transition:fade={{ duration: transitionDuration }}
108
+ onoutroend={() => {
109
+ onFigureOutroEnd?.();
110
+ transitioning = false;
111
+ }}
112
+ class="h-full w-full flex flex-col justify-between items-center p-8"
113
+ >
114
+ <div class="flex-1 flex items-center justify-center min-h-0">
115
+ <Img {image} class="min-h-0 max-h-full" />
116
+ </div>
117
+ {#if image.properties.title}
118
+ <figcaption class="pt-2 text-center">
119
+ {image.properties.title}
120
+ </figcaption>
121
+ {/if}
122
+ </figure>
123
+ {/if}
124
+ </section>
125
+ {/if}
126
+
127
+ <style>
128
+ :global(html:has([role='dialog'])) {
129
+ overflow: hidden;
130
+ }
131
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { ImageData } from './types';
2
+ interface Props {
3
+ force?: boolean;
4
+ image?: ImageData;
5
+ onclose: () => void;
6
+ onnext?: () => void;
7
+ onprevious?: () => void;
8
+ transitionDuration?: number;
9
+ }
10
+ declare const Zone5Lightbox: import("svelte").Component<Props, {}, "">;
11
+ type Zone5Lightbox = ReturnType<typeof Zone5Lightbox>;
12
+ export default Zone5Lightbox;
@@ -0,0 +1,68 @@
1
+ <script lang="ts" module>
2
+ import { getContext, setContext } from 'svelte';
3
+
4
+ const key = Symbol('Zone5 provider');
5
+ export const useImageRegistry = () => getContext<Registry>(key);
6
+ </script>
7
+
8
+ <script lang="ts">
9
+ import type { Snippet } from 'svelte';
10
+ import { queryParameters } from 'sveltekit-search-params';
11
+
12
+ import { beforeNavigate } from '$app/navigation';
13
+
14
+ import { type Registry, registry } from '../stores';
15
+ import Zone5Lightbox from './Zone5Lightbox.svelte';
16
+
17
+ let { children }: { children: Snippet } = $props();
18
+
19
+ setContext<Registry>(key, registry);
20
+
21
+ // URL state store
22
+ const params = queryParameters({
23
+ z5: true,
24
+ });
25
+ let optimisticForce = $state($params.z5 !== null);
26
+
27
+ // clear registry when navigating away
28
+ beforeNavigate((navigation) => {
29
+ if (navigation.from?.route.id == navigation.to?.route.id) return;
30
+ registry.clear();
31
+ });
32
+
33
+ // update URL state, once Svelte's router has settled
34
+ let routerSettled = false;
35
+ $effect(function updateZ5UrlState() {
36
+ const current = $registry.current?.id ?? null;
37
+ if (routerSettled || current) {
38
+ routerSettled = true;
39
+ if (current !== $params.z5) {
40
+ $params.z5 = current;
41
+ }
42
+ }
43
+ });
44
+
45
+ // update from URL state
46
+ $effect(function updateZ5fromUrl() {
47
+ const z5 = $params.z5;
48
+ const current = $registry.current?.id ?? null;
49
+ if (z5 && z5 !== current) {
50
+ if (!registry.findCurrent(z5)) {
51
+ optimisticForce = false;
52
+ $params.z5 = null;
53
+ }
54
+ }
55
+ });
56
+ </script>
57
+
58
+ <Zone5Lightbox
59
+ force={optimisticForce}
60
+ image={$registry.current ?? undefined}
61
+ onclose={() => {
62
+ optimisticForce = false;
63
+ registry.clearCurrent();
64
+ }}
65
+ onprevious={registry.prev}
66
+ onnext={registry.next}
67
+ />
68
+ {@render children()}
@@ -0,0 +1,9 @@
1
+ export declare const useImageRegistry: () => Registry;
2
+ import type { Snippet } from 'svelte';
3
+ import { type Registry } from '../stores';
4
+ type $$ComponentProps = {
5
+ children: Snippet;
6
+ };
7
+ declare const Zone5Provider: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Zone5Provider = ReturnType<typeof Zone5Provider>;
9
+ export default Zone5Provider;
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ 'aria-label'?: string;
6
+ children: Snippet;
7
+ disabled?: boolean;
8
+ class?: string | string[];
9
+ onaction: () => void;
10
+ tabindex?: number;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ let {
15
+ 'aria-label': aria_label,
16
+ children,
17
+ class: class_,
18
+ disabled = false,
19
+ onaction,
20
+ tabindex = 0,
21
+ ...rest
22
+ }: Props = $props();
23
+ </script>
24
+
25
+ <button
26
+ class={class_}
27
+ {disabled}
28
+ onclick={onaction}
29
+ onkeydown={(evt) => {
30
+ if (evt.key == ' ' || evt.key == 'Enter') {
31
+ evt.preventDefault();
32
+ onaction();
33
+ }
34
+ }}
35
+ {tabindex}
36
+ aria-label={aria_label}
37
+ {...rest}
38
+ >
39
+ {@render children()}
40
+ </button>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ 'aria-label'?: string;
4
+ children: Snippet;
5
+ disabled?: boolean;
6
+ class?: string | string[];
7
+ onaction: () => void;
8
+ tabindex?: number;
9
+ [key: string]: unknown;
10
+ }
11
+ declare const Button: import("svelte").Component<Props, {}, "">;
12
+ type Button = ReturnType<typeof Button>;
13
+ export default Button;
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ import { X } from '@lucide/svelte';
3
+
4
+ import Button from './Button.svelte';
5
+
6
+ interface Props {
7
+ class?: string | string[];
8
+ onclose: () => void;
9
+ tabindex?: number;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ let { class: class_, onclose, tabindex = 0, ...rest }: Props = $props();
14
+ </script>
15
+
16
+ <Button class={class_} onaction={onclose} {tabindex} aria-label="close" {...rest}>
17
+ <X />
18
+ </Button>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ class?: string | string[];
3
+ onclose: () => void;
4
+ tabindex?: number;
5
+ [key: string]: unknown;
6
+ }
7
+ declare const CloseButton: import("svelte").Component<Props, {}, "">;
8
+ type CloseButton = ReturnType<typeof CloseButton>;
9
+ export default CloseButton;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { ChevronRight } from '@lucide/svelte';
3
+
4
+ import Button from './Button.svelte';
5
+
6
+ interface Props {
7
+ class?: string | string[];
8
+ disabled?: boolean;
9
+ onnext: () => void;
10
+ tabindex?: number;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ let { class: class_, disabled, onnext, tabindex = 0, ...rest }: Props = $props();
15
+ </script>
16
+
17
+ <Button class={class_} {disabled} onaction={onnext} {tabindex} aria-label="next" {...rest}>
18
+ <ChevronRight />
19
+ </Button>
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ class?: string | string[];
3
+ disabled?: boolean;
4
+ onnext: () => void;
5
+ tabindex?: number;
6
+ [key: string]: unknown;
7
+ }
8
+ declare const NextButton: import("svelte").Component<Props, {}, "">;
9
+ type NextButton = ReturnType<typeof NextButton>;
10
+ export default NextButton;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { ChevronLeft } from '@lucide/svelte';
3
+
4
+ import Button from './Button.svelte';
5
+
6
+ interface Props {
7
+ class?: string | string[];
8
+ disabled?: boolean;
9
+ onprevious: () => void;
10
+ tabindex?: number;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ let { class: class_, disabled, onprevious, tabindex = 0, ...rest }: Props = $props();
15
+ </script>
16
+
17
+ <Button class={class_} {disabled} onaction={onprevious} {tabindex} aria-label="previous" {...rest}>
18
+ <ChevronLeft />
19
+ </Button>
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ class?: string | string[];
3
+ disabled?: boolean;
4
+ onprevious: () => void;
5
+ tabindex?: number;
6
+ [key: string]: unknown;
7
+ }
8
+ declare const PrevButton: import("svelte").Component<Props, {}, "">;
9
+ type PrevButton = ReturnType<typeof PrevButton>;
10
+ export default PrevButton;
@@ -0,0 +1,4 @@
1
+ export { default as Button } from './Button.svelte';
2
+ export { default as CloseButton } from './CloseButton.svelte';
3
+ export { default as NextButton } from './NextButton.svelte';
4
+ export { default as PrevButton } from './PrevButton.svelte';
@@ -0,0 +1,5 @@
1
+ // Component exports
2
+ export { default as Button } from './Button.svelte';
3
+ export { default as CloseButton } from './CloseButton.svelte';
4
+ export { default as NextButton } from './NextButton.svelte';
5
+ export { default as PrevButton } from './PrevButton.svelte';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Default column breakpoints for waterfall mode.
3
+ * Maps viewport width (in pixels) to number of columns.
4
+ */
5
+ export declare const DEFAULT_COLUMN_BREAKPOINTS: {
6
+ readonly 640: 2;
7
+ readonly 768: 3;
8
+ readonly 1024: 4;
9
+ };
10
+ /**
11
+ * Height for single image in wall mode (Tailwind class)
12
+ */
13
+ export declare const SINGLE_IMAGE_HEIGHT_CLASS = "h-96";
14
+ /**
15
+ * Height for multiple images in wall mode (Tailwind class)
16
+ */
17
+ export declare const WALL_IMAGE_HEIGHT_CLASS = "h-96";