@autumnsgrove/groveengine 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.
- package/README.md +163 -0
- package/dist/auth/jwt.d.ts +14 -0
- package/dist/auth/jwt.js +109 -0
- package/dist/auth/session.d.ts +42 -0
- package/dist/auth/session.js +105 -0
- package/dist/components/admin/GutterManager.svelte +910 -0
- package/dist/components/admin/GutterManager.svelte.d.ts +15 -0
- package/dist/components/admin/MarkdownEditor.svelte +3114 -0
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +43 -0
- package/dist/components/custom/CollapsibleSection.svelte +74 -0
- package/dist/components/custom/CollapsibleSection.svelte.d.ts +15 -0
- package/dist/components/custom/ContentWithGutter.svelte +646 -0
- package/dist/components/custom/ContentWithGutter.svelte.d.ts +19 -0
- package/dist/components/custom/GutterItem.svelte +201 -0
- package/dist/components/custom/GutterItem.svelte.d.ts +11 -0
- package/dist/components/custom/LeftGutter.svelte +271 -0
- package/dist/components/custom/LeftGutter.svelte.d.ts +17 -0
- package/dist/components/custom/MobileTOC.svelte +273 -0
- package/dist/components/custom/MobileTOC.svelte.d.ts +11 -0
- package/dist/components/custom/TableOfContents.svelte +163 -0
- package/dist/components/custom/TableOfContents.svelte.d.ts +11 -0
- package/dist/components/gallery/ImageGallery.svelte +681 -0
- package/dist/components/gallery/ImageGallery.svelte.d.ts +11 -0
- package/dist/components/gallery/Lightbox.svelte +107 -0
- package/dist/components/gallery/Lightbox.svelte.d.ts +19 -0
- package/dist/components/gallery/LightboxCaption.svelte +25 -0
- package/dist/components/gallery/LightboxCaption.svelte.d.ts +11 -0
- package/dist/components/gallery/ZoomableImage.svelte +163 -0
- package/dist/components/gallery/ZoomableImage.svelte.d.ts +17 -0
- package/dist/components/ui/Accordion.svelte +74 -0
- package/dist/components/ui/Accordion.svelte.d.ts +42 -0
- package/dist/components/ui/Badge.svelte +48 -0
- package/dist/components/ui/Badge.svelte.d.ts +26 -0
- package/dist/components/ui/Button.svelte +74 -0
- package/dist/components/ui/Button.svelte.d.ts +34 -0
- package/dist/components/ui/Card.svelte +102 -0
- package/dist/components/ui/Card.svelte.d.ts +46 -0
- package/dist/components/ui/Dialog.svelte +91 -0
- package/dist/components/ui/Dialog.svelte.d.ts +43 -0
- package/dist/components/ui/Input.svelte +81 -0
- package/dist/components/ui/Input.svelte.d.ts +35 -0
- package/dist/components/ui/Select.svelte +69 -0
- package/dist/components/ui/Select.svelte.d.ts +36 -0
- package/dist/components/ui/Sheet.svelte +98 -0
- package/dist/components/ui/Sheet.svelte.d.ts +45 -0
- package/dist/components/ui/Skeleton.svelte +31 -0
- package/dist/components/ui/Skeleton.svelte.d.ts +26 -0
- package/dist/components/ui/Table.svelte +59 -0
- package/dist/components/ui/Table.svelte.d.ts +44 -0
- package/dist/components/ui/Tabs.svelte +76 -0
- package/dist/components/ui/Tabs.svelte.d.ts +41 -0
- package/dist/components/ui/Textarea.svelte +81 -0
- package/dist/components/ui/Textarea.svelte.d.ts +35 -0
- package/dist/components/ui/Toast.svelte +18 -0
- package/dist/components/ui/Toast.svelte.d.ts +7 -0
- package/dist/components/ui/accordion/accordion-content.svelte +24 -0
- package/dist/components/ui/accordion/accordion-content.svelte.d.ts +4 -0
- package/dist/components/ui/accordion/accordion-item.svelte +12 -0
- package/dist/components/ui/accordion/accordion-item.svelte.d.ts +4 -0
- package/dist/components/ui/accordion/accordion-trigger.svelte +29 -0
- package/dist/components/ui/accordion/accordion-trigger.svelte.d.ts +7 -0
- package/dist/components/ui/accordion/index.d.ts +6 -0
- package/dist/components/ui/accordion/index.js +8 -0
- package/dist/components/ui/badge/badge.svelte +50 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +60 -0
- package/dist/components/ui/badge/index.d.ts +2 -0
- package/dist/components/ui/badge/index.js +2 -0
- package/dist/components/ui/button/button.svelte +82 -0
- package/dist/components/ui/button/button.svelte.d.ts +132 -0
- package/dist/components/ui/button/index.d.ts +2 -0
- package/dist/components/ui/button/index.js +4 -0
- package/dist/components/ui/card/card-content.svelte +16 -0
- package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-description.svelte +16 -0
- package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-footer.svelte +16 -0
- package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-header.svelte +16 -0
- package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
- package/dist/components/ui/card/card-title.svelte +25 -0
- package/dist/components/ui/card/card-title.svelte.d.ts +8 -0
- package/dist/components/ui/card/card.svelte +20 -0
- package/dist/components/ui/card/card.svelte.d.ts +5 -0
- package/dist/components/ui/card/index.d.ts +7 -0
- package/dist/components/ui/card/index.js +9 -0
- package/dist/components/ui/dialog/dialog-content.svelte +38 -0
- package/dist/components/ui/dialog/dialog-content.svelte.d.ts +9 -0
- package/dist/components/ui/dialog/dialog-description.svelte +16 -0
- package/dist/components/ui/dialog/dialog-description.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-footer.svelte +20 -0
- package/dist/components/ui/dialog/dialog-footer.svelte.d.ts +5 -0
- package/dist/components/ui/dialog/dialog-header.svelte +20 -0
- package/dist/components/ui/dialog/dialog-header.svelte.d.ts +5 -0
- package/dist/components/ui/dialog/dialog-overlay.svelte +19 -0
- package/dist/components/ui/dialog/dialog-overlay.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/dialog-title.svelte +16 -0
- package/dist/components/ui/dialog/dialog-title.svelte.d.ts +4 -0
- package/dist/components/ui/dialog/index.d.ts +12 -0
- package/dist/components/ui/dialog/index.js +14 -0
- package/dist/components/ui/index.d.ts +26 -0
- package/dist/components/ui/index.js +29 -0
- package/dist/components/ui/input/index.d.ts +2 -0
- package/dist/components/ui/input/index.js +4 -0
- package/dist/components/ui/input/input.svelte +46 -0
- package/dist/components/ui/input/input.svelte.d.ts +13 -0
- package/dist/components/ui/select/index.d.ts +11 -0
- package/dist/components/ui/select/index.js +13 -0
- package/dist/components/ui/select/select-content.svelte +39 -0
- package/dist/components/ui/select/select-content.svelte.d.ts +7 -0
- package/dist/components/ui/select/select-group-heading.svelte +16 -0
- package/dist/components/ui/select/select-group-heading.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-item.svelte +37 -0
- package/dist/components/ui/select/select-item.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-scroll-down-button.svelte +19 -0
- package/dist/components/ui/select/select-scroll-down-button.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-scroll-up-button.svelte +19 -0
- package/dist/components/ui/select/select-scroll-up-button.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-separator.svelte +13 -0
- package/dist/components/ui/select/select-separator.svelte.d.ts +4 -0
- package/dist/components/ui/select/select-trigger.svelte +24 -0
- package/dist/components/ui/select/select-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/separator/index.d.ts +2 -0
- package/dist/components/ui/separator/index.js +4 -0
- package/dist/components/ui/separator/separator.svelte +22 -0
- package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/index.d.ts +12 -0
- package/dist/components/ui/sheet/index.js +14 -0
- package/dist/components/ui/sheet/sheet-content.svelte +53 -0
- package/dist/components/ui/sheet/sheet-content.svelte.d.ts +62 -0
- package/dist/components/ui/sheet/sheet-description.svelte +16 -0
- package/dist/components/ui/sheet/sheet-description.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet-footer.svelte +20 -0
- package/dist/components/ui/sheet/sheet-footer.svelte.d.ts +5 -0
- package/dist/components/ui/sheet/sheet-header.svelte +20 -0
- package/dist/components/ui/sheet/sheet-header.svelte.d.ts +5 -0
- package/dist/components/ui/sheet/sheet-overlay.svelte +21 -0
- package/dist/components/ui/sheet/sheet-overlay.svelte.d.ts +6 -0
- package/dist/components/ui/sheet/sheet-title.svelte +16 -0
- package/dist/components/ui/sheet/sheet-title.svelte.d.ts +4 -0
- package/dist/components/ui/skeleton/index.d.ts +2 -0
- package/dist/components/ui/skeleton/index.js +4 -0
- package/dist/components/ui/skeleton/skeleton.svelte +17 -0
- package/dist/components/ui/skeleton/skeleton.svelte.d.ts +5 -0
- package/dist/components/ui/table/index.d.ts +9 -0
- package/dist/components/ui/table/index.js +11 -0
- package/dist/components/ui/table/table-body.svelte +16 -0
- package/dist/components/ui/table/table-body.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-caption.svelte +16 -0
- package/dist/components/ui/table/table-caption.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-cell.svelte +20 -0
- package/dist/components/ui/table/table-cell.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-footer.svelte +16 -0
- package/dist/components/ui/table/table-footer.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-head.svelte +23 -0
- package/dist/components/ui/table/table-head.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-header.svelte +16 -0
- package/dist/components/ui/table/table-header.svelte.d.ts +5 -0
- package/dist/components/ui/table/table-row.svelte +23 -0
- package/dist/components/ui/table/table-row.svelte.d.ts +5 -0
- package/dist/components/ui/table/table.svelte +18 -0
- package/dist/components/ui/table/table.svelte.d.ts +5 -0
- package/dist/components/ui/tabs/index.d.ts +6 -0
- package/dist/components/ui/tabs/index.js +8 -0
- package/dist/components/ui/tabs/tabs-content.svelte +19 -0
- package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-list.svelte +19 -0
- package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +19 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/textarea/index.d.ts +2 -0
- package/dist/components/ui/textarea/index.js +4 -0
- package/dist/components/ui/textarea/textarea.svelte +24 -0
- package/dist/components/ui/textarea/textarea.svelte.d.ts +6 -0
- package/dist/components/ui/toast.d.ts +86 -0
- package/dist/components/ui/toast.js +99 -0
- package/dist/db/schema.sql +238 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +20 -0
- package/dist/payments/index.d.ts +33 -0
- package/dist/payments/index.js +47 -0
- package/dist/payments/shop.d.ts +165 -0
- package/dist/payments/shop.js +588 -0
- package/dist/payments/stripe/client.d.ts +231 -0
- package/dist/payments/stripe/client.js +198 -0
- package/dist/payments/stripe/index.d.ts +18 -0
- package/dist/payments/stripe/index.js +17 -0
- package/dist/payments/stripe/provider.d.ts +50 -0
- package/dist/payments/stripe/provider.js +530 -0
- package/dist/payments/types.d.ts +355 -0
- package/dist/payments/types.js +7 -0
- package/dist/server/logger.d.ts +53 -0
- package/dist/server/logger.js +252 -0
- package/dist/styles/content.css +514 -0
- package/dist/styles/tokens.css +175 -0
- package/dist/utils/api.d.ts +20 -0
- package/dist/utils/api.js +109 -0
- package/dist/utils/cn.d.ts +15 -0
- package/dist/utils/cn.js +18 -0
- package/dist/utils/csrf.d.ts +22 -0
- package/dist/utils/csrf.js +72 -0
- package/dist/utils/debounce.d.ts +7 -0
- package/dist/utils/debounce.js +14 -0
- package/dist/utils/gallery.d.ts +66 -0
- package/dist/utils/gallery.js +181 -0
- package/dist/utils/gutter.d.ts +54 -0
- package/dist/utils/gutter.js +169 -0
- package/dist/utils/imageProcessor.d.ts +58 -0
- package/dist/utils/imageProcessor.js +205 -0
- package/dist/utils/json.d.ts +17 -0
- package/dist/utils/json.js +26 -0
- package/dist/utils/markdown.d.ts +101 -0
- package/dist/utils/markdown.js +947 -0
- package/dist/utils/sanitize.d.ts +25 -0
- package/dist/utils/sanitize.js +127 -0
- package/dist/utils/validation.d.ts +46 -0
- package/dist/utils/validation.js +169 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +5 -0
- package/package.json +129 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import ZoomableImage from './ZoomableImage.svelte';
|
|
3
|
+
import LightboxCaption from './LightboxCaption.svelte';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lightbox - Full-screen image viewer
|
|
7
|
+
* Click to expand images to full size with zoom and pan support
|
|
8
|
+
*/
|
|
9
|
+
let { src = '', alt = '', caption = '', isOpen = false, onClose = () => {} } = $props();
|
|
10
|
+
|
|
11
|
+
function handleKeydown(event) {
|
|
12
|
+
if (event.key === 'Escape') {
|
|
13
|
+
onClose();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function handleBackdropClick(event) {
|
|
18
|
+
if (event.target === event.currentTarget) {
|
|
19
|
+
onClose();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<svelte:window onkeydown={handleKeydown} />
|
|
25
|
+
|
|
26
|
+
{#if isOpen}
|
|
27
|
+
<div
|
|
28
|
+
class="lightbox-backdrop"
|
|
29
|
+
onclick={handleBackdropClick}
|
|
30
|
+
role="dialog"
|
|
31
|
+
aria-modal="true"
|
|
32
|
+
aria-label="Image viewer"
|
|
33
|
+
>
|
|
34
|
+
<button class="close-button" onclick={onClose} aria-label="Close">
|
|
35
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
36
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
37
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
38
|
+
</svg>
|
|
39
|
+
</button>
|
|
40
|
+
<div class="lightbox-content" onclick={handleBackdropClick}>
|
|
41
|
+
<ZoomableImage {src} {alt} isActive={isOpen} class="lightbox-image" />
|
|
42
|
+
</div>
|
|
43
|
+
<LightboxCaption {caption} />
|
|
44
|
+
</div>
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
.lightbox-backdrop {
|
|
49
|
+
position: fixed;
|
|
50
|
+
top: 0;
|
|
51
|
+
left: 0;
|
|
52
|
+
right: 0;
|
|
53
|
+
bottom: 0;
|
|
54
|
+
background: rgba(0, 0, 0, 0.9);
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
z-index: 9999;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
padding: 2rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.lightbox-content {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
align-items: center;
|
|
68
|
+
max-width: 90vw;
|
|
69
|
+
max-height: 90vh;
|
|
70
|
+
overflow: auto;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:global(.lightbox-content .lightbox-image) {
|
|
74
|
+
max-width: 90vw;
|
|
75
|
+
-o-object-fit: contain;
|
|
76
|
+
object-fit: contain;
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
flex: 1 1 auto;
|
|
79
|
+
min-height: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.close-button {
|
|
83
|
+
position: absolute;
|
|
84
|
+
top: 1rem;
|
|
85
|
+
right: 1rem;
|
|
86
|
+
width: 48px;
|
|
87
|
+
height: 48px;
|
|
88
|
+
border-radius: 50%;
|
|
89
|
+
background: rgba(255, 255, 255, 0.1);
|
|
90
|
+
border: none;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
color: white;
|
|
96
|
+
transition: background 0.2s;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.close-button:hover {
|
|
100
|
+
background: rgba(255, 255, 255, 0.2);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.close-button svg {
|
|
104
|
+
width: 24px;
|
|
105
|
+
height: 24px;
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default Lightbox;
|
|
2
|
+
type Lightbox = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Lightbox: import("svelte").Component<{
|
|
7
|
+
src?: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
caption?: string;
|
|
10
|
+
isOpen?: boolean;
|
|
11
|
+
onClose?: Function;
|
|
12
|
+
}, {}, "">;
|
|
13
|
+
type $$ComponentProps = {
|
|
14
|
+
src?: string;
|
|
15
|
+
alt?: string;
|
|
16
|
+
caption?: string;
|
|
17
|
+
isOpen?: boolean;
|
|
18
|
+
onClose?: Function;
|
|
19
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* LightboxCaption - Shared caption component for lightbox viewers
|
|
4
|
+
* Displays image captions with consistent styling
|
|
5
|
+
*
|
|
6
|
+
* @prop {string} caption - The caption text to display
|
|
7
|
+
*/
|
|
8
|
+
let { caption = '' } = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
{#if caption}
|
|
12
|
+
<div class="lightbox-caption">{caption}</div>
|
|
13
|
+
{/if}
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.lightbox-caption {
|
|
17
|
+
padding: 0.75rem 1rem;
|
|
18
|
+
color: rgba(255, 255, 255, 0.9);
|
|
19
|
+
font-size: 0.9rem;
|
|
20
|
+
font-style: italic;
|
|
21
|
+
text-align: center;
|
|
22
|
+
max-width: 90vw;
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default LightboxCaption;
|
|
2
|
+
type LightboxCaption = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const LightboxCaption: import("svelte").Component<{
|
|
7
|
+
caption?: string;
|
|
8
|
+
}, {}, "">;
|
|
9
|
+
type $$ComponentProps = {
|
|
10
|
+
caption?: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* ZoomableImage - Image with zoom and pan functionality
|
|
4
|
+
* Click to cycle through zoom levels, drag to pan when zoomed
|
|
5
|
+
*
|
|
6
|
+
* @prop {string} src - Image source URL
|
|
7
|
+
* @prop {string} alt - Alt text for accessibility
|
|
8
|
+
* @prop {boolean} isActive - When false, resets zoom/pan state
|
|
9
|
+
* @prop {string} class - Additional CSS classes
|
|
10
|
+
*/
|
|
11
|
+
let { src = '', alt = '', isActive = true, class: className = '' } = $props();
|
|
12
|
+
|
|
13
|
+
// Zoom state: 0 = normal, 1 = medium zoom (1.5x), 2 = max zoom (2.5x)
|
|
14
|
+
let zoomLevel = $state(0);
|
|
15
|
+
|
|
16
|
+
// Pan/drag state for zoomed images
|
|
17
|
+
let isDragging = $state(false);
|
|
18
|
+
let panX = $state(0);
|
|
19
|
+
let panY = $state(0);
|
|
20
|
+
let dragStartX = $state(0);
|
|
21
|
+
let dragStartY = $state(0);
|
|
22
|
+
let dragStartPanX = $state(0);
|
|
23
|
+
let dragStartPanY = $state(0);
|
|
24
|
+
let totalDragDistance = $state(0);
|
|
25
|
+
|
|
26
|
+
// Derived scale value based on zoom level
|
|
27
|
+
let scaleValue = $derived(zoomLevel === 0 ? 1 : zoomLevel === 1 ? 1.5 : 2.5);
|
|
28
|
+
|
|
29
|
+
// Reset zoom and pan when isActive becomes false or src changes
|
|
30
|
+
$effect(() => {
|
|
31
|
+
if (!isActive) {
|
|
32
|
+
zoomLevel = 0;
|
|
33
|
+
panX = 0;
|
|
34
|
+
panY = 0;
|
|
35
|
+
isDragging = false;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Reset when image source changes
|
|
40
|
+
$effect(() => {
|
|
41
|
+
// Track src to trigger reset
|
|
42
|
+
src;
|
|
43
|
+
zoomLevel = 0;
|
|
44
|
+
panX = 0;
|
|
45
|
+
panY = 0;
|
|
46
|
+
isDragging = false;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function cycleZoom() {
|
|
50
|
+
zoomLevel = (zoomLevel + 1) % 3;
|
|
51
|
+
// Reset pan when zooming out to level 0
|
|
52
|
+
if (zoomLevel === 0) {
|
|
53
|
+
panX = 0;
|
|
54
|
+
panY = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Mouse event handlers for drag/pan
|
|
59
|
+
function handleMouseDown(event) {
|
|
60
|
+
if (zoomLevel === 0) return;
|
|
61
|
+
|
|
62
|
+
isDragging = true;
|
|
63
|
+
dragStartX = event.clientX;
|
|
64
|
+
dragStartY = event.clientY;
|
|
65
|
+
dragStartPanX = panX;
|
|
66
|
+
dragStartPanY = panY;
|
|
67
|
+
totalDragDistance = 0;
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function handleMouseMove(event) {
|
|
72
|
+
if (!isDragging) return;
|
|
73
|
+
|
|
74
|
+
const deltaX = event.clientX - dragStartX;
|
|
75
|
+
const deltaY = event.clientY - dragStartY;
|
|
76
|
+
|
|
77
|
+
panX = dragStartPanX + deltaX;
|
|
78
|
+
panY = dragStartPanY + deltaY;
|
|
79
|
+
totalDragDistance += Math.abs(deltaX) + Math.abs(deltaY);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function handleMouseUp() {
|
|
83
|
+
isDragging = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Touch event handlers for drag/pan on mobile
|
|
87
|
+
function handleTouchStart(event) {
|
|
88
|
+
if (zoomLevel === 0) return;
|
|
89
|
+
|
|
90
|
+
// Only handle single touch for panning
|
|
91
|
+
if (event.touches.length === 1) {
|
|
92
|
+
isDragging = true;
|
|
93
|
+
dragStartX = event.touches[0].clientX;
|
|
94
|
+
dragStartY = event.touches[0].clientY;
|
|
95
|
+
dragStartPanX = panX;
|
|
96
|
+
dragStartPanY = panY;
|
|
97
|
+
totalDragDistance = 0;
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleTouchMove(event) {
|
|
103
|
+
if (!isDragging || event.touches.length !== 1) return;
|
|
104
|
+
|
|
105
|
+
const deltaX = event.touches[0].clientX - dragStartX;
|
|
106
|
+
const deltaY = event.touches[0].clientY - dragStartY;
|
|
107
|
+
|
|
108
|
+
panX = dragStartPanX + deltaX;
|
|
109
|
+
panY = dragStartPanY + deltaY;
|
|
110
|
+
totalDragDistance += Math.abs(deltaX) + Math.abs(deltaY);
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function handleTouchEnd() {
|
|
115
|
+
isDragging = false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Click handler that distinguishes between click and drag
|
|
119
|
+
function handleClick(event) {
|
|
120
|
+
// If we dragged more than 5px, don't treat as click
|
|
121
|
+
if (totalDragDistance > 5) {
|
|
122
|
+
totalDragDistance = 0;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
cycleZoom();
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp} />
|
|
130
|
+
|
|
131
|
+
<img
|
|
132
|
+
{src}
|
|
133
|
+
{alt}
|
|
134
|
+
class="zoomable-image {className}"
|
|
135
|
+
class:zoomed={zoomLevel > 0}
|
|
136
|
+
class:dragging={isDragging}
|
|
137
|
+
style="transform: translate({panX}px, {panY}px) scale({scaleValue})"
|
|
138
|
+
onmousedown={handleMouseDown}
|
|
139
|
+
ontouchstart={handleTouchStart}
|
|
140
|
+
ontouchmove={handleTouchMove}
|
|
141
|
+
ontouchend={handleTouchEnd}
|
|
142
|
+
onclick={handleClick}
|
|
143
|
+
/>
|
|
144
|
+
|
|
145
|
+
<style>
|
|
146
|
+
.zoomable-image {
|
|
147
|
+
cursor: zoom-in;
|
|
148
|
+
transition: transform 0.3s ease;
|
|
149
|
+
-webkit-user-select: none;
|
|
150
|
+
-moz-user-select: none;
|
|
151
|
+
user-select: none;
|
|
152
|
+
-webkit-user-drag: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.zoomable-image.zoomed {
|
|
156
|
+
cursor: grab;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.zoomable-image.dragging {
|
|
160
|
+
cursor: grabbing;
|
|
161
|
+
transition: none;
|
|
162
|
+
}
|
|
163
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default ZoomableImage;
|
|
2
|
+
type ZoomableImage = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const ZoomableImage: import("svelte").Component<{
|
|
7
|
+
src?: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
isActive?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
}, {}, "">;
|
|
12
|
+
type $$ComponentProps = {
|
|
13
|
+
src?: string;
|
|
14
|
+
alt?: string;
|
|
15
|
+
isActive?: boolean;
|
|
16
|
+
class?: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
Accordion as ShadcnAccordion,
|
|
4
|
+
AccordionItem,
|
|
5
|
+
AccordionTrigger,
|
|
6
|
+
AccordionContent
|
|
7
|
+
} from "./accordion";
|
|
8
|
+
import type { Snippet } from "svelte";
|
|
9
|
+
|
|
10
|
+
interface AccordionItemConfig {
|
|
11
|
+
value: string;
|
|
12
|
+
title: string;
|
|
13
|
+
content?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Accordion component wrapper for collapsible content sections
|
|
19
|
+
*
|
|
20
|
+
* @prop {AccordionItemConfig[]} items - Array of accordion items with value, title, optional content and disabled flag
|
|
21
|
+
* @prop {string} [type="single"] - Accordion behavior: "single" (one open) or "multiple" (many open)
|
|
22
|
+
* @prop {boolean} [collapsible=false] - Allow closing all items (only for type="single")
|
|
23
|
+
* @prop {Snippet<[AccordionItemConfig]>} [contentSnippet] - Custom content renderer (receives item data)
|
|
24
|
+
* @prop {string} [class] - Additional CSS classes for Accordion root
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <Accordion items={[
|
|
28
|
+
* { value: "faq1", title: "What is this?", content: "This is an FAQ" },
|
|
29
|
+
* { value: "faq2", title: "How does it work?", content: "Very well!" }
|
|
30
|
+
* ]} type="single" collapsible />
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* <Accordion items={sections} type="multiple">
|
|
34
|
+
* {#snippet contentSnippet(item)}
|
|
35
|
+
* <DetailedContent data={item} />
|
|
36
|
+
* {/snippet}
|
|
37
|
+
* </Accordion>
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* <Accordion items={helpTopics} type="single" class="w-full" />
|
|
41
|
+
*/
|
|
42
|
+
interface Props {
|
|
43
|
+
items: AccordionItemConfig[];
|
|
44
|
+
type?: "single" | "multiple";
|
|
45
|
+
collapsible?: boolean;
|
|
46
|
+
class?: string;
|
|
47
|
+
contentSnippet?: Snippet<[item: AccordionItemConfig]>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let {
|
|
51
|
+
items,
|
|
52
|
+
type = "single",
|
|
53
|
+
collapsible = false,
|
|
54
|
+
class: className,
|
|
55
|
+
contentSnippet
|
|
56
|
+
}: Props = $props();
|
|
57
|
+
|
|
58
|
+
const accordionType = $derived(type === "single" ? "single" : "multiple");
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<ShadcnAccordion type={accordionType} {collapsible} class={className}>
|
|
62
|
+
{#each items as item (item.value)}
|
|
63
|
+
<AccordionItem value={item.value} disabled={item.disabled ?? false}>
|
|
64
|
+
<AccordionTrigger>{item.title}</AccordionTrigger>
|
|
65
|
+
<AccordionContent>
|
|
66
|
+
{#if contentSnippet}
|
|
67
|
+
{@render contentSnippet(item)}
|
|
68
|
+
{:else if item.content}
|
|
69
|
+
{item.content}
|
|
70
|
+
{/if}
|
|
71
|
+
</AccordionContent>
|
|
72
|
+
</AccordionItem>
|
|
73
|
+
{/each}
|
|
74
|
+
</ShadcnAccordion>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface AccordionItemConfig {
|
|
3
|
+
value: string;
|
|
4
|
+
title: string;
|
|
5
|
+
content?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Accordion component wrapper for collapsible content sections
|
|
10
|
+
*
|
|
11
|
+
* @prop {AccordionItemConfig[]} items - Array of accordion items with value, title, optional content and disabled flag
|
|
12
|
+
* @prop {string} [type="single"] - Accordion behavior: "single" (one open) or "multiple" (many open)
|
|
13
|
+
* @prop {boolean} [collapsible=false] - Allow closing all items (only for type="single")
|
|
14
|
+
* @prop {Snippet<[AccordionItemConfig]>} [contentSnippet] - Custom content renderer (receives item data)
|
|
15
|
+
* @prop {string} [class] - Additional CSS classes for Accordion root
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <Accordion items={[
|
|
19
|
+
* { value: "faq1", title: "What is this?", content: "This is an FAQ" },
|
|
20
|
+
* { value: "faq2", title: "How does it work?", content: "Very well!" }
|
|
21
|
+
* ]} type="single" collapsible />
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <Accordion items={sections} type="multiple">
|
|
25
|
+
* {#snippet contentSnippet(item)}
|
|
26
|
+
* <DetailedContent data={item} />
|
|
27
|
+
* {/snippet}
|
|
28
|
+
* </Accordion>
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <Accordion items={helpTopics} type="single" class="w-full" />
|
|
32
|
+
*/
|
|
33
|
+
interface Props {
|
|
34
|
+
items: AccordionItemConfig[];
|
|
35
|
+
type?: "single" | "multiple";
|
|
36
|
+
collapsible?: boolean;
|
|
37
|
+
class?: string;
|
|
38
|
+
contentSnippet?: Snippet<[item: AccordionItemConfig]>;
|
|
39
|
+
}
|
|
40
|
+
declare const Accordion: import("svelte").Component<Props, {}, "">;
|
|
41
|
+
type Accordion = ReturnType<typeof Accordion>;
|
|
42
|
+
export default Accordion;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge as ShadcnBadge } from "./badge";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
|
|
5
|
+
type BadgeVariant = "default" | "secondary" | "destructive" | "tag";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Badge component wrapper for displaying small labels, tags, or status indicators
|
|
9
|
+
*
|
|
10
|
+
* @prop {BadgeVariant} [variant="default"] - Badge style variant (default|secondary|destructive|tag)
|
|
11
|
+
* @prop {string} [class] - Additional CSS classes to apply
|
|
12
|
+
* @prop {Snippet} [children] - Badge content (typically short text)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <Badge variant="default">New</Badge>
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <Badge variant="destructive">Error</Badge>
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <Badge variant="tag">TypeScript</Badge>
|
|
22
|
+
*/
|
|
23
|
+
interface Props {
|
|
24
|
+
variant?: BadgeVariant;
|
|
25
|
+
class?: string;
|
|
26
|
+
children?: Snippet;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
variant = "default",
|
|
31
|
+
class: className,
|
|
32
|
+
children
|
|
33
|
+
}: Props = $props();
|
|
34
|
+
|
|
35
|
+
// Map tag variant to secondary styling
|
|
36
|
+
const variantMap: Record<BadgeVariant, "default" | "secondary" | "destructive"> = {
|
|
37
|
+
default: "default",
|
|
38
|
+
secondary: "secondary",
|
|
39
|
+
destructive: "destructive",
|
|
40
|
+
tag: "secondary"
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const shadcnVariant = $derived(variantMap[variant]);
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<ShadcnBadge variant={shadcnVariant} class={className}>
|
|
47
|
+
{@render children?.()}
|
|
48
|
+
</ShadcnBadge>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
type BadgeVariant = "default" | "secondary" | "destructive" | "tag";
|
|
3
|
+
/**
|
|
4
|
+
* Badge component wrapper for displaying small labels, tags, or status indicators
|
|
5
|
+
*
|
|
6
|
+
* @prop {BadgeVariant} [variant="default"] - Badge style variant (default|secondary|destructive|tag)
|
|
7
|
+
* @prop {string} [class] - Additional CSS classes to apply
|
|
8
|
+
* @prop {Snippet} [children] - Badge content (typically short text)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <Badge variant="default">New</Badge>
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <Badge variant="destructive">Error</Badge>
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <Badge variant="tag">TypeScript</Badge>
|
|
18
|
+
*/
|
|
19
|
+
interface Props {
|
|
20
|
+
variant?: BadgeVariant;
|
|
21
|
+
class?: string;
|
|
22
|
+
children?: Snippet;
|
|
23
|
+
}
|
|
24
|
+
declare const Badge: import("svelte").Component<Props, {}, "">;
|
|
25
|
+
type Badge = ReturnType<typeof Badge>;
|
|
26
|
+
export default Badge;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button as ShadcnButton } from "./button";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
5
|
+
|
|
6
|
+
type ButtonVariant = "primary" | "secondary" | "danger" | "ghost" | "link";
|
|
7
|
+
type ButtonSize = "sm" | "md" | "lg";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Button component wrapper around shadcn-svelte Button
|
|
11
|
+
*
|
|
12
|
+
* @prop {ButtonVariant} [variant="primary"] - Button style variant (primary|secondary|danger|ghost|link)
|
|
13
|
+
* @prop {ButtonSize} [size="md"] - Button size (sm|md|lg)
|
|
14
|
+
* @prop {boolean} [disabled=false] - Whether button is disabled
|
|
15
|
+
* @prop {Function} [onclick] - Click handler function
|
|
16
|
+
* @prop {string} [href] - Optional link href (renders as anchor element)
|
|
17
|
+
* @prop {string} [class] - Additional CSS classes to apply
|
|
18
|
+
* @prop {Snippet} [children] - Button content (text/icons/etc)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <Button variant="primary" size="lg">Save Changes</Button>
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* <Button variant="danger" onclick={() => handleDelete()}>Delete</Button>
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <Button variant="ghost" href="/settings">Settings</Button>
|
|
28
|
+
*/
|
|
29
|
+
interface Props extends Omit<HTMLButtonAttributes, "class"> {
|
|
30
|
+
variant?: ButtonVariant;
|
|
31
|
+
size?: ButtonSize;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
class?: string;
|
|
34
|
+
children?: Snippet;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
variant = "primary",
|
|
39
|
+
size = "md",
|
|
40
|
+
disabled = false,
|
|
41
|
+
class: className,
|
|
42
|
+
children,
|
|
43
|
+
...restProps
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
// Map our simplified variants to shadcn variants
|
|
47
|
+
const variantMap: Record<ButtonVariant, "default" | "secondary" | "destructive" | "ghost" | "link"> = {
|
|
48
|
+
primary: "default",
|
|
49
|
+
secondary: "secondary",
|
|
50
|
+
danger: "destructive",
|
|
51
|
+
ghost: "ghost",
|
|
52
|
+
link: "link"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Map our size variants to shadcn sizes
|
|
56
|
+
const sizeMap: Record<ButtonSize, "sm" | "default" | "lg"> = {
|
|
57
|
+
sm: "sm",
|
|
58
|
+
md: "default",
|
|
59
|
+
lg: "lg"
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const shadcnVariant = $derived(variantMap[variant]);
|
|
63
|
+
const shadcnSize = $derived(sizeMap[size]);
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<ShadcnButton
|
|
67
|
+
variant={shadcnVariant}
|
|
68
|
+
size={shadcnSize}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
class={className}
|
|
71
|
+
{...restProps}
|
|
72
|
+
>
|
|
73
|
+
{@render children?.()}
|
|
74
|
+
</ShadcnButton>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
type ButtonVariant = "primary" | "secondary" | "danger" | "ghost" | "link";
|
|
4
|
+
type ButtonSize = "sm" | "md" | "lg";
|
|
5
|
+
/**
|
|
6
|
+
* Button component wrapper around shadcn-svelte Button
|
|
7
|
+
*
|
|
8
|
+
* @prop {ButtonVariant} [variant="primary"] - Button style variant (primary|secondary|danger|ghost|link)
|
|
9
|
+
* @prop {ButtonSize} [size="md"] - Button size (sm|md|lg)
|
|
10
|
+
* @prop {boolean} [disabled=false] - Whether button is disabled
|
|
11
|
+
* @prop {Function} [onclick] - Click handler function
|
|
12
|
+
* @prop {string} [href] - Optional link href (renders as anchor element)
|
|
13
|
+
* @prop {string} [class] - Additional CSS classes to apply
|
|
14
|
+
* @prop {Snippet} [children] - Button content (text/icons/etc)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <Button variant="primary" size="lg">Save Changes</Button>
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <Button variant="danger" onclick={() => handleDelete()}>Delete</Button>
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* <Button variant="ghost" href="/settings">Settings</Button>
|
|
24
|
+
*/
|
|
25
|
+
interface Props extends Omit<HTMLButtonAttributes, "class"> {
|
|
26
|
+
variant?: ButtonVariant;
|
|
27
|
+
size?: ButtonSize;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
class?: string;
|
|
30
|
+
children?: Snippet;
|
|
31
|
+
}
|
|
32
|
+
declare const Button: import("svelte").Component<Props, {}, "">;
|
|
33
|
+
type Button = ReturnType<typeof Button>;
|
|
34
|
+
export default Button;
|