@autumnsgrove/groveengine 0.1.1 → 0.3.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.
Files changed (61) hide show
  1. package/dist/components/admin/GutterManager.svelte +1 -2
  2. package/dist/components/admin/MarkdownEditor.svelte +1 -2
  3. package/dist/components/custom/InternalsPostViewer.svelte +95 -0
  4. package/dist/components/custom/InternalsPostViewer.svelte.d.ts +13 -0
  5. package/dist/components/ui/index.d.ts +0 -12
  6. package/dist/components/ui/index.js +2 -13
  7. package/dist/components/ui/select/select-separator.svelte +2 -3
  8. package/dist/components/ui/select/select-separator.svelte.d.ts +1 -1
  9. package/dist/utils/markdown.d.ts +42 -0
  10. package/dist/utils/markdown.js +104 -0
  11. package/package.json +2 -1
  12. package/dist/components/ui/Badge.svelte +0 -48
  13. package/dist/components/ui/Badge.svelte.d.ts +0 -26
  14. package/dist/components/ui/Button.svelte +0 -74
  15. package/dist/components/ui/Button.svelte.d.ts +0 -34
  16. package/dist/components/ui/Card.svelte +0 -102
  17. package/dist/components/ui/Card.svelte.d.ts +0 -46
  18. package/dist/components/ui/Input.svelte +0 -81
  19. package/dist/components/ui/Input.svelte.d.ts +0 -35
  20. package/dist/components/ui/Skeleton.svelte +0 -31
  21. package/dist/components/ui/Skeleton.svelte.d.ts +0 -26
  22. package/dist/components/ui/Textarea.svelte +0 -81
  23. package/dist/components/ui/Textarea.svelte.d.ts +0 -35
  24. package/dist/components/ui/badge/badge.svelte +0 -50
  25. package/dist/components/ui/badge/badge.svelte.d.ts +0 -60
  26. package/dist/components/ui/badge/index.d.ts +0 -2
  27. package/dist/components/ui/badge/index.js +0 -2
  28. package/dist/components/ui/button/button.svelte +0 -82
  29. package/dist/components/ui/button/button.svelte.d.ts +0 -132
  30. package/dist/components/ui/button/index.d.ts +0 -2
  31. package/dist/components/ui/button/index.js +0 -4
  32. package/dist/components/ui/card/card-content.svelte +0 -16
  33. package/dist/components/ui/card/card-content.svelte.d.ts +0 -5
  34. package/dist/components/ui/card/card-description.svelte +0 -16
  35. package/dist/components/ui/card/card-description.svelte.d.ts +0 -5
  36. package/dist/components/ui/card/card-footer.svelte +0 -16
  37. package/dist/components/ui/card/card-footer.svelte.d.ts +0 -5
  38. package/dist/components/ui/card/card-header.svelte +0 -16
  39. package/dist/components/ui/card/card-header.svelte.d.ts +0 -5
  40. package/dist/components/ui/card/card-title.svelte +0 -25
  41. package/dist/components/ui/card/card-title.svelte.d.ts +0 -8
  42. package/dist/components/ui/card/card.svelte +0 -20
  43. package/dist/components/ui/card/card.svelte.d.ts +0 -5
  44. package/dist/components/ui/card/index.d.ts +0 -7
  45. package/dist/components/ui/card/index.js +0 -9
  46. package/dist/components/ui/input/index.d.ts +0 -2
  47. package/dist/components/ui/input/index.js +0 -4
  48. package/dist/components/ui/input/input.svelte +0 -46
  49. package/dist/components/ui/input/input.svelte.d.ts +0 -13
  50. package/dist/components/ui/separator/index.d.ts +0 -2
  51. package/dist/components/ui/separator/index.js +0 -4
  52. package/dist/components/ui/separator/separator.svelte +0 -22
  53. package/dist/components/ui/separator/separator.svelte.d.ts +0 -4
  54. package/dist/components/ui/skeleton/index.d.ts +0 -2
  55. package/dist/components/ui/skeleton/index.js +0 -4
  56. package/dist/components/ui/skeleton/skeleton.svelte +0 -17
  57. package/dist/components/ui/skeleton/skeleton.svelte.d.ts +0 -5
  58. package/dist/components/ui/textarea/index.d.ts +0 -2
  59. package/dist/components/ui/textarea/index.js +0 -4
  60. package/dist/components/ui/textarea/textarea.svelte +0 -24
  61. package/dist/components/ui/textarea/textarea.svelte.d.ts +0 -6
@@ -1,8 +1,7 @@
1
1
  <script>
2
2
  import { marked } from "marked";
3
+ import { Input, Button } from '@groveengine/ui';
3
4
  import Dialog from "../ui/Dialog.svelte";
4
- import Input from "../ui/Input.svelte";
5
- import Button from "../ui/Button.svelte";
6
5
  import Select from "../ui/Select.svelte";
7
6
  import { toast } from "../ui/toast";
8
7
 
@@ -4,9 +4,8 @@
4
4
  import { onMount, tick } from "svelte";
5
5
  import { sanitizeMarkdown } from "../../utils/sanitize.js";
6
6
  import "../../styles/content.css";
7
+ import { Button, Input } from '@groveengine/ui';
7
8
  import Dialog from "../ui/Dialog.svelte";
8
- import Button from "../ui/Button.svelte";
9
- import Input from "../ui/Input.svelte";
10
9
 
11
10
  // Initialize mermaid with grove-themed dark config
12
11
  mermaid.initialize({
@@ -0,0 +1,95 @@
1
+ <script>
2
+ /**
3
+ * Simple component to display a featured blog post
4
+ * @prop {{ title: string; description?: string; slug: string; date?: string }} post - Post data
5
+ * @prop {string} [caption] - Optional caption text
6
+ */
7
+ let { post, caption = "" } = $props();
8
+
9
+ const formattedDate = $derived(post.date ? new Date(post.date).toLocaleDateString('en-US', {
10
+ year: 'numeric',
11
+ month: 'long',
12
+ day: 'numeric'
13
+ }) : null);
14
+ </script>
15
+
16
+ <article class="post-viewer">
17
+ {#if caption}
18
+ <span class="caption">{caption}</span>
19
+ {/if}
20
+ <a href="/blog/{post.slug}" class="post-link">
21
+ <h3 class="title">{post.title}</h3>
22
+ {#if post.description}
23
+ <p class="description">{post.description}</p>
24
+ {/if}
25
+ {#if formattedDate}
26
+ <time class="date">{formattedDate}</time>
27
+ {/if}
28
+ </a>
29
+ </article>
30
+
31
+ <style>
32
+ .post-viewer {
33
+ background: var(--color-bg-secondary);
34
+ border: 1px solid var(--color-border);
35
+ border-radius: var(--border-radius-standard);
36
+ padding: 1.5rem;
37
+ transition: all 0.3s ease;
38
+ }
39
+
40
+ .caption {
41
+ display: block;
42
+ font-size: 0.75rem;
43
+ text-transform: uppercase;
44
+ letter-spacing: 0.05em;
45
+ color: var(--color-primary);
46
+ margin-bottom: 0.5rem;
47
+ }
48
+
49
+ .post-link {
50
+ text-decoration: none;
51
+ color: inherit;
52
+ display: block;
53
+ }
54
+
55
+ .post-link:hover .title {
56
+ color: var(--color-primary);
57
+ }
58
+
59
+ .title {
60
+ font-size: 1.25rem;
61
+ margin: 0 0 0.5rem 0;
62
+ color: var(--color-text);
63
+ transition: color 0.2s ease;
64
+ }
65
+
66
+ .description {
67
+ margin: 0 0 0.75rem 0;
68
+ color: var(--color-text-muted);
69
+ font-size: 0.95rem;
70
+ line-height: 1.6;
71
+ }
72
+
73
+ .date {
74
+ display: block;
75
+ font-size: 0.875rem;
76
+ color: var(--color-text-subtle);
77
+ }
78
+
79
+ :global(.dark) .post-viewer {
80
+ background: var(--color-bg-tertiary-dark);
81
+ border-color: var(--color-border-dark);
82
+ }
83
+
84
+ :global(.dark) .title {
85
+ color: var(--color-text-dark);
86
+ }
87
+
88
+ :global(.dark) .description {
89
+ color: var(--color-text-subtle-dark);
90
+ }
91
+
92
+ :global(.dark) .date {
93
+ color: var(--color-text-subtle-dark);
94
+ }
95
+ </style>
@@ -0,0 +1,13 @@
1
+ export default InternalsPostViewer;
2
+ type InternalsPostViewer = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const InternalsPostViewer: import("svelte").Component<{
7
+ post: any;
8
+ caption?: string;
9
+ }, {}, "">;
10
+ type $$ComponentProps = {
11
+ post: any;
12
+ caption?: string;
13
+ };
@@ -1,26 +1,14 @@
1
- export { default as Button } from "./Button.svelte";
2
- export { default as Card } from "./Card.svelte";
3
- export { default as Badge } from "./Badge.svelte";
4
1
  export { default as Dialog } from "./Dialog.svelte";
5
- export { default as Input } from "./Input.svelte";
6
- export { default as Textarea } from "./Textarea.svelte";
7
2
  export { default as Select } from "./Select.svelte";
8
3
  export { default as Tabs } from "./Tabs.svelte";
9
4
  export { default as Accordion } from "./Accordion.svelte";
10
5
  export { default as Sheet } from "./Sheet.svelte";
11
6
  export { default as Toast } from "./Toast.svelte";
12
- export { default as Skeleton } from "./Skeleton.svelte";
13
7
  export { default as Table } from "./Table.svelte";
14
8
  export { toast } from "./toast";
15
9
  export { Root as DialogRoot, Trigger as DialogTrigger, Close as DialogClose, Portal as DialogPortal } from "./dialog";
16
10
  export { Root as SheetRoot, Trigger as SheetTrigger, Close as SheetClose, Portal as SheetPortal, Content as SheetContent, Header as SheetHeader, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription } from "./sheet";
17
11
  export { Root as TableRoot, Body as TableBody, Caption as TableCaption, Cell as TableCell, Footer as TableFooter, Head as TableHead, Header as TableHeader, Row as TableRow } from "./table";
18
- export { Root as SkeletonRoot, Skeleton as SkeletonComponent } from "./skeleton";
19
12
  export { Root as AccordionRoot } from "./accordion";
20
- export { Root as BadgeRoot } from "./badge";
21
- export { Root as ButtonRoot } from "./button";
22
- export { Root as CardRoot, Header as CardHeader, Title as CardTitle, Description as CardDescription, Content as CardContent, Footer as CardFooter } from "./card";
23
- export { Root as InputRoot } from "./input";
24
- export { Root as TextareaRoot } from "./textarea";
25
13
  export { Root as SelectRoot, Trigger as SelectTrigger, Content as SelectContent, Item as SelectItem, Group as SelectGroup, GroupHeading as SelectLabel, Separator as SelectSeparator, ScrollUpButton as SelectScrollUpButton, ScrollDownButton as SelectScrollDownButton } from "./select";
26
14
  export { Root as TabsRoot, List as TabsList, Trigger as TabsTrigger, Content as TabsContent } from "./tabs";
@@ -1,16 +1,11 @@
1
- // Wrappers
2
- export { default as Button } from "./Button.svelte";
3
- export { default as Card } from "./Card.svelte";
4
- export { default as Badge } from "./Badge.svelte";
1
+ // Admin-specific UI component wrappers
2
+ // Generic UI components (Button, Card, Badge, Input, Textarea, Skeleton) are now in @groveengine/ui
5
3
  export { default as Dialog } from "./Dialog.svelte";
6
- export { default as Input } from "./Input.svelte";
7
- export { default as Textarea } from "./Textarea.svelte";
8
4
  export { default as Select } from "./Select.svelte";
9
5
  export { default as Tabs } from "./Tabs.svelte";
10
6
  export { default as Accordion } from "./Accordion.svelte";
11
7
  export { default as Sheet } from "./Sheet.svelte";
12
8
  export { default as Toast } from "./Toast.svelte";
13
- export { default as Skeleton } from "./Skeleton.svelte";
14
9
  export { default as Table } from "./Table.svelte";
15
10
  // Toast utilities
16
11
  export { toast } from "./toast";
@@ -18,12 +13,6 @@ export { toast } from "./toast";
18
13
  export { Root as DialogRoot, Trigger as DialogTrigger, Close as DialogClose, Portal as DialogPortal } from "./dialog";
19
14
  export { Root as SheetRoot, Trigger as SheetTrigger, Close as SheetClose, Portal as SheetPortal, Content as SheetContent, Header as SheetHeader, Footer as SheetFooter, Title as SheetTitle, Description as SheetDescription } from "./sheet";
20
15
  export { Root as TableRoot, Body as TableBody, Caption as TableCaption, Cell as TableCell, Footer as TableFooter, Head as TableHead, Header as TableHeader, Row as TableRow } from "./table";
21
- export { Root as SkeletonRoot, Skeleton as SkeletonComponent } from "./skeleton";
22
16
  export { Root as AccordionRoot } from "./accordion";
23
- export { Root as BadgeRoot } from "./badge";
24
- export { Root as ButtonRoot } from "./button";
25
- export { Root as CardRoot, Header as CardHeader, Title as CardTitle, Description as CardDescription, Content as CardContent, Footer as CardFooter } from "./card";
26
- export { Root as InputRoot } from "./input";
27
- export { Root as TextareaRoot } from "./textarea";
28
17
  export { Root as SelectRoot, Trigger as SelectTrigger, Content as SelectContent, Item as SelectItem, Group as SelectGroup, GroupHeading as SelectLabel, Separator as SelectSeparator, ScrollUpButton as SelectScrollUpButton, ScrollDownButton as SelectScrollDownButton } from "./select";
29
18
  export { Root as TabsRoot, List as TabsList, Trigger as TabsTrigger, Content as TabsContent } from "./tabs";
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
- import type { Separator as SeparatorPrimitive } from "bits-ui";
3
- import { Separator } from "../separator/index.js";
2
+ import { Separator as SeparatorPrimitive } from "bits-ui";
4
3
  import { cn } from "../../../utils";
5
4
 
6
5
  let {
@@ -10,4 +9,4 @@
10
9
  }: SeparatorPrimitive.RootProps = $props();
11
10
  </script>
12
11
 
13
- <Separator bind:ref class={cn("bg-muted -mx-1 my-1 h-px", className)} {...restProps} />
12
+ <SeparatorPrimitive.Root bind:ref class={cn("bg-muted -mx-1 my-1 h-px", className)} {...restProps} />
@@ -1,4 +1,4 @@
1
- import type { Separator as SeparatorPrimitive } from "bits-ui";
1
+ import { Separator as SeparatorPrimitive } from "bits-ui";
2
2
  declare const SelectSeparator: import("svelte").Component<SeparatorPrimitive.RootProps, {}, "ref">;
3
3
  type SelectSeparator = ReturnType<typeof SelectSeparator>;
4
4
  export default SelectSeparator;
@@ -121,3 +121,45 @@ export function createContentLoader(config: {
121
121
  homeGutter: Object;
122
122
  contactGutter: Object;
123
123
  }): Object;
124
+ /**
125
+ * Register a content loader for the site
126
+ * This should be called by the consuming site to provide access to content
127
+ * @param {Object} loader - Object with getAllPosts, getSiteConfig, getLatestPost functions
128
+ */
129
+ export function registerContentLoader(loader: Object): void;
130
+ /**
131
+ * Get all blog posts
132
+ * @returns {Array} Array of post objects
133
+ */
134
+ export function getAllPosts(): any[];
135
+ /**
136
+ * Get site configuration
137
+ * @returns {Object} Site config object
138
+ */
139
+ export function getSiteConfig(): Object;
140
+ /**
141
+ * Get the latest post
142
+ * @returns {Object|null} Latest post or null
143
+ */
144
+ export function getLatestPost(): Object | null;
145
+ /**
146
+ * Get home page content
147
+ * @returns {Object|null} Home page data or null
148
+ */
149
+ export function getHomePage(): Object | null;
150
+ /**
151
+ * Get a post by its slug
152
+ * @param {string} slug - The post slug
153
+ * @returns {Object|null} Post object or null
154
+ */
155
+ export function getPostBySlug(slug: string): Object | null;
156
+ /**
157
+ * Get about page content
158
+ * @returns {Object|null} About page data or null
159
+ */
160
+ export function getAboutPage(): Object | null;
161
+ /**
162
+ * Get contact page content
163
+ * @returns {Object|null} Contact page data or null
164
+ */
165
+ export function getContactPage(): Object | null;
@@ -755,3 +755,107 @@ export function createContentLoader(config) {
755
755
  },
756
756
  };
757
757
  }
758
+
759
+ /**
760
+ * Registry for site-specific content loaders
761
+ * Sites must register their content loaders using registerContentLoader()
762
+ */
763
+ let contentLoader = null;
764
+
765
+ /**
766
+ * Register a content loader for the site
767
+ * This should be called by the consuming site to provide access to content
768
+ * @param {Object} loader - Object with getAllPosts, getSiteConfig, getLatestPost functions
769
+ */
770
+ export function registerContentLoader(loader) {
771
+ contentLoader = loader;
772
+ }
773
+
774
+ /**
775
+ * Get all blog posts
776
+ * @returns {Array} Array of post objects
777
+ */
778
+ export function getAllPosts() {
779
+ if (!contentLoader || !contentLoader.getAllPosts) {
780
+ console.warn('getAllPosts: No content loader registered. Call registerContentLoader() in your site.');
781
+ return [];
782
+ }
783
+ return contentLoader.getAllPosts();
784
+ }
785
+
786
+ /**
787
+ * Get site configuration
788
+ * @returns {Object} Site config object
789
+ */
790
+ export function getSiteConfig() {
791
+ if (!contentLoader || !contentLoader.getSiteConfig) {
792
+ console.warn('getSiteConfig: No content loader registered. Call registerContentLoader() in your site.');
793
+ return {
794
+ owner: { name: "Admin", email: "" },
795
+ site: { title: "GroveEngine Site", description: "", copyright: "" },
796
+ social: {},
797
+ };
798
+ }
799
+ return contentLoader.getSiteConfig();
800
+ }
801
+
802
+ /**
803
+ * Get the latest post
804
+ * @returns {Object|null} Latest post or null
805
+ */
806
+ export function getLatestPost() {
807
+ if (!contentLoader || !contentLoader.getLatestPost) {
808
+ console.warn('getLatestPost: No content loader registered. Call registerContentLoader() in your site.');
809
+ return null;
810
+ }
811
+ return contentLoader.getLatestPost();
812
+ }
813
+
814
+ /**
815
+ * Get home page content
816
+ * @returns {Object|null} Home page data or null
817
+ */
818
+ export function getHomePage() {
819
+ if (!contentLoader || !contentLoader.getHomePage) {
820
+ console.warn('getHomePage: No content loader registered. Call registerContentLoader() in your site.');
821
+ return null;
822
+ }
823
+ return contentLoader.getHomePage();
824
+ }
825
+
826
+ /**
827
+ * Get a post by its slug
828
+ * @param {string} slug - The post slug
829
+ * @returns {Object|null} Post object or null
830
+ */
831
+ export function getPostBySlug(slug) {
832
+ if (!contentLoader || !contentLoader.getPostBySlug) {
833
+ console.warn('getPostBySlug: No content loader registered. Call registerContentLoader() in your site.');
834
+ return null;
835
+ }
836
+ return contentLoader.getPostBySlug(slug);
837
+ }
838
+
839
+ /**
840
+ * Get about page content
841
+ * @returns {Object|null} About page data or null
842
+ */
843
+ export function getAboutPage() {
844
+ if (!contentLoader || !contentLoader.getAboutPage) {
845
+ console.warn('getAboutPage: No content loader registered. Call registerContentLoader() in your site.');
846
+ return null;
847
+ }
848
+ return contentLoader.getAboutPage();
849
+ }
850
+
851
+ /**
852
+ * Get contact page content
853
+ * @returns {Object|null} Contact page data or null
854
+ */
855
+ export function getContactPage() {
856
+ if (!contentLoader || !contentLoader.getContactPage) {
857
+ console.warn('getContactPage: No content loader registered. Call registerContentLoader() in your site.');
858
+ return null;
859
+ }
860
+ return contentLoader.getContactPage();
861
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autumnsgrove/groveengine",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Multi-tenant blog engine for Grove Platform. Features gutter annotations, markdown editing, magic code auth, and Cloudflare Workers deployment.",
5
5
  "author": "AutumnsGrove",
6
6
  "license": "MIT",
@@ -114,6 +114,7 @@
114
114
  "vitest": "^4.0.14"
115
115
  },
116
116
  "dependencies": {
117
+ "@groveengine/ui": "^0.3.0",
117
118
  "@types/dompurify": "^3.0.5",
118
119
  "chart.js": "^4.5.1",
119
120
  "clsx": "^2.1.1",
@@ -1,48 +0,0 @@
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>
@@ -1,26 +0,0 @@
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;
@@ -1,74 +0,0 @@
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>
@@ -1,34 +0,0 @@
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;
@@ -1,102 +0,0 @@
1
- <script lang="ts">
2
- import {
3
- Card as ShadcnCard,
4
- CardHeader,
5
- CardTitle,
6
- CardDescription,
7
- CardContent,
8
- CardFooter
9
- } from "./card";
10
- import type { Snippet } from "svelte";
11
- import { cn } from "../../utils";
12
-
13
- /**
14
- * Card component wrapper providing structured layout with header, content, and footer
15
- *
16
- * @prop {string} [title] - Card title (renders in CardHeader with CardTitle)
17
- * @prop {string} [description] - Card description (renders in CardHeader with CardDescription)
18
- * @prop {boolean} [hoverable=false] - Enable hover shadow effect for interactive cards
19
- * @prop {Snippet} [header] - Custom header content (overrides title/description)
20
- * @prop {Snippet} [footer] - Footer content (rendered in CardFooter)
21
- * @prop {Snippet} [children] - Main card content (rendered in CardContent)
22
- * @prop {string} [class] - Additional CSS classes for Card root
23
- *
24
- * @example
25
- * <Card title="Profile" description="Update your profile settings">
26
- * <p>Card content here</p>
27
- * </Card>
28
- *
29
- * @example
30
- * <Card hoverable>
31
- * {#snippet header()}
32
- * <CustomHeader />
33
- * {/snippet}
34
- * Content here
35
- * </Card>
36
- *
37
- * @example
38
- * <Card title="Actions">
39
- * {#snippet footer()}
40
- * <Button>Save</Button>
41
- * {/snippet}
42
- * Form content
43
- * </Card>
44
- */
45
- interface Props {
46
- title?: string;
47
- description?: string;
48
- hoverable?: boolean;
49
- class?: string;
50
- header?: Snippet;
51
- footer?: Snippet;
52
- children?: Snippet;
53
- [key: string]: any; // Allow any additional props to be forwarded
54
- }
55
-
56
- let {
57
- title,
58
- description,
59
- hoverable = false,
60
- class: className,
61
- header,
62
- footer,
63
- children,
64
- ...restProps
65
- }: Props = $props();
66
-
67
- const cardClass = $derived(
68
- cn(
69
- hoverable && "hover:shadow-lg transition-shadow cursor-pointer",
70
- className
71
- )
72
- );
73
- </script>
74
-
75
- <ShadcnCard class={cardClass} {...restProps}>
76
- {#if header || title || description}
77
- <CardHeader>
78
- {#if header}
79
- {@render header()}
80
- {:else}
81
- {#if title}
82
- <CardTitle>{title}</CardTitle>
83
- {/if}
84
- {#if description}
85
- <CardDescription>{description}</CardDescription>
86
- {/if}
87
- {/if}
88
- </CardHeader>
89
- {/if}
90
-
91
- {#if children}
92
- <CardContent>
93
- {@render children()}
94
- </CardContent>
95
- {/if}
96
-
97
- {#if footer}
98
- <CardFooter>
99
- {@render footer()}
100
- </CardFooter>
101
- {/if}
102
- </ShadcnCard>