yaxa-svelte 1.0.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 (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/assets/favicon.svg +15 -0
  4. package/dist/components/elements/Avatar.svelte +106 -0
  5. package/dist/components/elements/Avatar.svelte.d.ts +40 -0
  6. package/dist/components/elements/AvatarGroup.svelte +18 -0
  7. package/dist/components/elements/AvatarGroup.svelte.d.ts +8 -0
  8. package/dist/components/elements/Badge.svelte +202 -0
  9. package/dist/components/elements/Badge.svelte.d.ts +75 -0
  10. package/dist/components/elements/Button.svelte +300 -0
  11. package/dist/components/elements/Button.svelte.d.ts +109 -0
  12. package/dist/components/elements/ButtonGroup.svelte +22 -0
  13. package/dist/components/elements/ButtonGroup.svelte.d.ts +9 -0
  14. package/dist/components/elements/Chip.svelte +117 -0
  15. package/dist/components/elements/Chip.svelte.d.ts +15 -0
  16. package/dist/components/elements/DataTable.svelte +222 -0
  17. package/dist/components/elements/DataTable.svelte.d.ts +43 -0
  18. package/dist/components/elements/Icon.svelte +87 -0
  19. package/dist/components/elements/Icon.svelte.d.ts +10 -0
  20. package/dist/components/elements/Kbd.svelte +37 -0
  21. package/dist/components/elements/Kbd.svelte.d.ts +32 -0
  22. package/dist/components/elements/Link.svelte +63 -0
  23. package/dist/components/elements/Link.svelte.d.ts +15 -0
  24. package/dist/components/elements/Logo.svelte +77 -0
  25. package/dist/components/elements/Logo.svelte.d.ts +9 -0
  26. package/dist/components/elements/Meter.svelte +87 -0
  27. package/dist/components/elements/Meter.svelte.d.ts +14 -0
  28. package/dist/components/elements/Progress.svelte +63 -0
  29. package/dist/components/elements/Progress.svelte.d.ts +49 -0
  30. package/dist/components/elements/Skeleton.svelte +12 -0
  31. package/dist/components/elements/Skeleton.svelte.d.ts +6 -0
  32. package/dist/components/elements/Spinner.svelte +39 -0
  33. package/dist/components/elements/Spinner.svelte.d.ts +7 -0
  34. package/dist/components/forms/Checkbox.svelte +77 -0
  35. package/dist/components/forms/Checkbox.svelte.d.ts +17 -0
  36. package/dist/components/forms/ColorPicker.svelte +142 -0
  37. package/dist/components/forms/ColorPicker.svelte.d.ts +10 -0
  38. package/dist/components/forms/Form.svelte +84 -0
  39. package/dist/components/forms/Form.svelte.d.ts +14 -0
  40. package/dist/components/forms/FormField.svelte +56 -0
  41. package/dist/components/forms/FormField.svelte.d.ts +14 -0
  42. package/dist/components/forms/Input.svelte +138 -0
  43. package/dist/components/forms/Input.svelte.d.ts +58 -0
  44. package/dist/components/forms/RadioGroup.svelte +74 -0
  45. package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
  46. package/dist/components/forms/Select.svelte +91 -0
  47. package/dist/components/forms/Select.svelte.d.ts +50 -0
  48. package/dist/components/forms/Slider.svelte +37 -0
  49. package/dist/components/forms/Slider.svelte.d.ts +12 -0
  50. package/dist/components/forms/Switch.svelte +81 -0
  51. package/dist/components/forms/Switch.svelte.d.ts +53 -0
  52. package/dist/components/forms/Textarea.svelte +73 -0
  53. package/dist/components/forms/Textarea.svelte.d.ts +64 -0
  54. package/dist/components/layout/Card.svelte +89 -0
  55. package/dist/components/layout/Card.svelte.d.ts +64 -0
  56. package/dist/components/layout/Container.svelte +36 -0
  57. package/dist/components/layout/Container.svelte.d.ts +37 -0
  58. package/dist/components/layout/Divider.svelte +45 -0
  59. package/dist/components/layout/Divider.svelte.d.ts +11 -0
  60. package/dist/components/layout/Footer.svelte +96 -0
  61. package/dist/components/layout/Footer.svelte.d.ts +10 -0
  62. package/dist/components/layout/Header.svelte +175 -0
  63. package/dist/components/layout/Header.svelte.d.ts +12 -0
  64. package/dist/components/layout/Section.svelte +62 -0
  65. package/dist/components/layout/Section.svelte.d.ts +14 -0
  66. package/dist/components/layout/YaxaApp.svelte +48 -0
  67. package/dist/components/layout/YaxaApp.svelte.d.ts +12 -0
  68. package/dist/components/navigation/Breadcrumb.svelte +58 -0
  69. package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
  70. package/dist/components/navigation/CommandPalette.svelte +201 -0
  71. package/dist/components/navigation/CommandPalette.svelte.d.ts +19 -0
  72. package/dist/components/navigation/DropdownMenu.svelte +75 -0
  73. package/dist/components/navigation/DropdownMenu.svelte.d.ts +20 -0
  74. package/dist/components/navigation/Pagination.svelte +106 -0
  75. package/dist/components/navigation/Pagination.svelte.d.ts +10 -0
  76. package/dist/components/navigation/Tabs.svelte +91 -0
  77. package/dist/components/navigation/Tabs.svelte.d.ts +18 -0
  78. package/dist/components/overlays/Accordion.svelte +54 -0
  79. package/dist/components/overlays/Accordion.svelte.d.ts +16 -0
  80. package/dist/components/overlays/Alert.svelte +110 -0
  81. package/dist/components/overlays/Alert.svelte.d.ts +37 -0
  82. package/dist/components/overlays/ContextMenu.svelte +67 -0
  83. package/dist/components/overlays/ContextMenu.svelte.d.ts +21 -0
  84. package/dist/components/overlays/Modal.svelte +99 -0
  85. package/dist/components/overlays/Modal.svelte.d.ts +40 -0
  86. package/dist/components/overlays/Popover.svelte +33 -0
  87. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  88. package/dist/components/overlays/Slideover.svelte +102 -0
  89. package/dist/components/overlays/Slideover.svelte.d.ts +37 -0
  90. package/dist/components/overlays/Tooltip.svelte +39 -0
  91. package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
  92. package/dist/components/saas/AuthCard.svelte +273 -0
  93. package/dist/components/saas/AuthCard.svelte.d.ts +16 -0
  94. package/dist/components/saas/PricingCard.svelte +166 -0
  95. package/dist/components/saas/PricingCard.svelte.d.ts +19 -0
  96. package/dist/components/saas/PricingTable.svelte +139 -0
  97. package/dist/components/saas/PricingTable.svelte.d.ts +11 -0
  98. package/dist/components/saas/SubscriptionCard.svelte +162 -0
  99. package/dist/components/saas/SubscriptionCard.svelte.d.ts +16 -0
  100. package/dist/components/saas/UserMenu.svelte +137 -0
  101. package/dist/components/saas/UserMenu.svelte.d.ts +19 -0
  102. package/dist/components/seo/Favicons.svelte +22 -0
  103. package/dist/components/seo/Favicons.svelte.d.ts +8 -0
  104. package/dist/components/seo/OgImage.svelte +61 -0
  105. package/dist/components/seo/OgImage.svelte.d.ts +12 -0
  106. package/dist/components/seo/Seo.svelte +170 -0
  107. package/dist/components/seo/Seo.svelte.d.ts +21 -0
  108. package/dist/composables/useAuth.svelte.d.ts +23 -0
  109. package/dist/composables/useAuth.svelte.js +157 -0
  110. package/dist/composables/useClipboard.svelte.d.ts +9 -0
  111. package/dist/composables/useClipboard.svelte.js +36 -0
  112. package/dist/composables/useColorMode.svelte.d.ts +8 -0
  113. package/dist/composables/useColorMode.svelte.js +20 -0
  114. package/dist/composables/useDebounce.svelte.d.ts +3 -0
  115. package/dist/composables/useDebounce.svelte.js +17 -0
  116. package/dist/composables/useMediaQuery.svelte.d.ts +3 -0
  117. package/dist/composables/useMediaQuery.svelte.js +17 -0
  118. package/dist/composables/useShortcuts.svelte.d.ts +10 -0
  119. package/dist/composables/useShortcuts.svelte.js +50 -0
  120. package/dist/composables/useToast.d.ts +19 -0
  121. package/dist/composables/useToast.js +34 -0
  122. package/dist/index.d.ts +101 -0
  123. package/dist/index.js +92 -0
  124. package/dist/server/auth/hook.d.ts +14 -0
  125. package/dist/server/auth/hook.js +36 -0
  126. package/dist/server/auth/index.d.ts +39 -0
  127. package/dist/server/auth/index.js +69 -0
  128. package/dist/server/db/index.d.ts +18 -0
  129. package/dist/server/db/index.js +43 -0
  130. package/dist/server/db/schema-pg.d.ts +977 -0
  131. package/dist/server/db/schema-pg.js +83 -0
  132. package/dist/server/db/schema-sqlite.d.ts +1045 -0
  133. package/dist/server/db/schema-sqlite.js +83 -0
  134. package/dist/server/db/types.d.ts +44 -0
  135. package/dist/server/db/types.js +1 -0
  136. package/dist/server/email/index.d.ts +27 -0
  137. package/dist/server/email/index.js +79 -0
  138. package/dist/server/polar/index.d.ts +29 -0
  139. package/dist/server/polar/index.js +49 -0
  140. package/dist/server/polar/webhook.d.ts +16 -0
  141. package/dist/server/polar/webhook.js +118 -0
  142. package/dist/site/config.d.ts +63 -0
  143. package/dist/site/config.js +48 -0
  144. package/dist/site/context.d.ts +11 -0
  145. package/dist/site/context.js +23 -0
  146. package/dist/site/hook.d.ts +15 -0
  147. package/dist/site/hook.js +38 -0
  148. package/dist/site/manifest.d.ts +6 -0
  149. package/dist/site/manifest.js +32 -0
  150. package/dist/site/og.d.ts +14 -0
  151. package/dist/site/og.js +116 -0
  152. package/dist/site/robots.d.ts +10 -0
  153. package/dist/site/robots.js +54 -0
  154. package/dist/site/schema.d.ts +67 -0
  155. package/dist/site/schema.js +67 -0
  156. package/dist/site/seo-helpers.d.ts +36 -0
  157. package/dist/site/seo-helpers.js +21 -0
  158. package/dist/site/sitemap-xsl.d.ts +6 -0
  159. package/dist/site/sitemap-xsl.js +164 -0
  160. package/dist/site/sitemap.d.ts +20 -0
  161. package/dist/site/sitemap.js +61 -0
  162. package/dist/styles/yaxa.css +84 -0
  163. package/dist/theme/theme.svelte.d.ts +74 -0
  164. package/dist/theme/theme.svelte.js +421 -0
  165. package/dist/utils/cn.d.ts +3 -0
  166. package/dist/utils/cn.js +3 -0
  167. package/dist/vite/index.d.ts +20 -0
  168. package/dist/vite/index.js +29 -0
  169. package/package.json +108 -0
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ value: number;
4
+ min?: number;
5
+ max?: number;
6
+ label?: string;
7
+ valueLabel?: string;
8
+ color?: 'primary' | 'success' | 'warning' | 'error' | 'neutral';
9
+ size?: 'xs' | 'sm' | 'md' | 'lg';
10
+ showValue?: boolean;
11
+ class?: string;
12
+ }
13
+
14
+ let {
15
+ value,
16
+ min = 0,
17
+ max = 100,
18
+ label,
19
+ valueLabel,
20
+ color = 'primary',
21
+ size = 'md',
22
+ showValue = true,
23
+ class: className = ''
24
+ }: Props = $props();
25
+
26
+ let percentage = $derived(
27
+ Math.min(100, Math.max(0, Math.round(((value - min) / (max - min)) * 100)))
28
+ );
29
+
30
+ let formattedValue = $derived(valueLabel || `${percentage}%`);
31
+
32
+ let heightClass = $derived.by(() => {
33
+ switch (size) {
34
+ case 'xs':
35
+ return 'h-1';
36
+ case 'sm':
37
+ return 'h-1.5';
38
+ case 'lg':
39
+ return 'h-3.5';
40
+ case 'md':
41
+ default:
42
+ return 'h-2.5';
43
+ }
44
+ });
45
+
46
+ let colorClass = $derived.by(() => {
47
+ switch (color) {
48
+ case 'success':
49
+ return 'bg-emerald-500';
50
+ case 'warning':
51
+ return 'bg-amber-500';
52
+ case 'error':
53
+ return 'bg-red-500';
54
+ case 'neutral':
55
+ return 'bg-neutral-600';
56
+ case 'primary':
57
+ default:
58
+ return 'bg-primary-600';
59
+ }
60
+ });
61
+ </script>
62
+
63
+ <div class="w-full space-y-1.5 {className}">
64
+ {#if label || showValue}
65
+ <div class="flex items-center justify-between text-sm">
66
+ {#if label}
67
+ <span class="font-medium text-neutral-700 dark:text-neutral-300">{label}</span>
68
+ {/if}
69
+ {#if showValue}
70
+ <span class="font-mono text-neutral-500 dark:text-neutral-400">{formattedValue}</span>
71
+ {/if}
72
+ </div>
73
+ {/if}
74
+
75
+ <div
76
+ class="relative w-full overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-800 {heightClass}"
77
+ role="meter"
78
+ aria-valuenow={value}
79
+ aria-valuemin={min}
80
+ aria-valuemax={max}
81
+ >
82
+ <div
83
+ class="h-full rounded-full transition-all duration-500 ease-out {colorClass}"
84
+ style="width: {percentage}%;"
85
+ ></div>
86
+ </div>
87
+ </div>
@@ -0,0 +1,14 @@
1
+ interface Props {
2
+ value: number;
3
+ min?: number;
4
+ max?: number;
5
+ label?: string;
6
+ valueLabel?: string;
7
+ color?: 'primary' | 'success' | 'warning' | 'error' | 'neutral';
8
+ size?: 'xs' | 'sm' | 'md' | 'lg';
9
+ showValue?: boolean;
10
+ class?: string;
11
+ }
12
+ declare const Meter: import("svelte").Component<Props, {}, "">;
13
+ type Meter = ReturnType<typeof Meter>;
14
+ export default Meter;
@@ -0,0 +1,63 @@
1
+ <script module lang="ts">
2
+ import { tv, type VariantProps } from '../../utils/cn';
3
+
4
+ export const progressVariants = tv({
5
+ base: 'relative w-full overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-800',
6
+ variants: {
7
+ size: {
8
+ xs: 'h-1',
9
+ sm: 'h-1.5',
10
+ md: 'h-2.5',
11
+ lg: 'h-4'
12
+ },
13
+ color: {
14
+ primary: '[&>div]:bg-primary-600',
15
+ success: '[&>div]:bg-emerald-600',
16
+ warning: '[&>div]:bg-amber-600',
17
+ error: '[&>div]:bg-rose-600'
18
+ }
19
+ },
20
+ defaultVariants: {
21
+ size: 'md',
22
+ color: 'primary'
23
+ }
24
+ });
25
+
26
+ export type ProgressProps = VariantProps<typeof progressVariants> & {
27
+ value?: number;
28
+ max?: number;
29
+ class?: string;
30
+ };
31
+ </script>
32
+
33
+ <script lang="ts">
34
+ let {
35
+ value,
36
+ max = 100,
37
+ size = 'md',
38
+ color = 'primary',
39
+ class: className = ''
40
+ }: ProgressProps = $props();
41
+
42
+ let percentage = $derived.by(() => {
43
+ if (value === undefined) return undefined;
44
+ return Math.min(100, Math.max(0, (value / max) * 100));
45
+ });
46
+ </script>
47
+
48
+ <div
49
+ class={progressVariants({ size, color, class: className })}
50
+ role="progressbar"
51
+ aria-valuenow={value}
52
+ aria-valuemin={0}
53
+ aria-valuemax={max}
54
+ >
55
+ {#if percentage !== undefined}
56
+ <div
57
+ class="h-full rounded-full transition-all duration-300 ease-in-out"
58
+ style="width: {percentage}%"
59
+ ></div>
60
+ {:else}
61
+ <div class="h-full w-1/3 animate-pulse rounded-full"></div>
62
+ {/if}
63
+ </div>
@@ -0,0 +1,49 @@
1
+ import { type VariantProps } from '../../utils/cn';
2
+ export declare const progressVariants: import("tailwind-variants").TVReturnType<{
3
+ size: {
4
+ xs: "h-1";
5
+ sm: "h-1.5";
6
+ md: "h-2.5";
7
+ lg: "h-4";
8
+ };
9
+ color: {
10
+ primary: "[&>div]:bg-primary-600";
11
+ success: "[&>div]:bg-emerald-600";
12
+ warning: "[&>div]:bg-amber-600";
13
+ error: "[&>div]:bg-rose-600";
14
+ };
15
+ }, undefined, "relative w-full overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-800", {
16
+ size: {
17
+ xs: "h-1";
18
+ sm: "h-1.5";
19
+ md: "h-2.5";
20
+ lg: "h-4";
21
+ };
22
+ color: {
23
+ primary: "[&>div]:bg-primary-600";
24
+ success: "[&>div]:bg-emerald-600";
25
+ warning: "[&>div]:bg-amber-600";
26
+ error: "[&>div]:bg-rose-600";
27
+ };
28
+ }, undefined, import("tailwind-variants").TVReturnTypeLike<{
29
+ size: {
30
+ xs: "h-1";
31
+ sm: "h-1.5";
32
+ md: "h-2.5";
33
+ lg: "h-4";
34
+ };
35
+ color: {
36
+ primary: "[&>div]:bg-primary-600";
37
+ success: "[&>div]:bg-emerald-600";
38
+ warning: "[&>div]:bg-amber-600";
39
+ error: "[&>div]:bg-rose-600";
40
+ };
41
+ }, undefined>>;
42
+ export type ProgressProps = VariantProps<typeof progressVariants> & {
43
+ value?: number;
44
+ max?: number;
45
+ class?: string;
46
+ };
47
+ declare const Progress: import("svelte").Component<ProgressProps, {}, "">;
48
+ type Progress = ReturnType<typeof Progress>;
49
+ export default Progress;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ class?: string;
4
+ }
5
+
6
+ let { class: className = '' }: Props = $props();
7
+ </script>
8
+
9
+ <div
10
+ class="animate-pulse rounded-md bg-neutral-200 dark:bg-neutral-800 {className}"
11
+ aria-hidden="true"
12
+ ></div>
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ class?: string;
3
+ }
4
+ declare const Skeleton: import("svelte").Component<Props, {}, "">;
5
+ type Skeleton = ReturnType<typeof Skeleton>;
6
+ export default Skeleton;
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
+ class?: string;
5
+ }
6
+
7
+ let { size = 'md', class: className = '' }: Props = $props();
8
+
9
+ let sizeClasses = $derived.by(() => {
10
+ switch (size) {
11
+ case 'xs':
12
+ return 'w-3.5 h-3.5';
13
+ case 'sm':
14
+ return 'w-4 h-4';
15
+ case 'lg':
16
+ return 'w-6 h-6';
17
+ case 'xl':
18
+ return 'w-8 h-8';
19
+ case 'md':
20
+ default:
21
+ return 'w-5 h-5';
22
+ }
23
+ });
24
+ </script>
25
+
26
+ <svg
27
+ class="animate-spin text-current {sizeClasses} {className}"
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ fill="none"
30
+ viewBox="0 0 24 24"
31
+ aria-hidden="true"
32
+ >
33
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
34
+ <path
35
+ class="opacity-75"
36
+ fill="currentColor"
37
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
38
+ ></path>
39
+ </svg>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
3
+ class?: string;
4
+ }
5
+ declare const Spinner: import("svelte").Component<Props, {}, "">;
6
+ type Spinner = ReturnType<typeof Spinner>;
7
+ export default Spinner;
@@ -0,0 +1,77 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Icon from '../elements/Icon.svelte';
4
+
5
+ interface Props {
6
+ checked?: boolean;
7
+ label?: string;
8
+ description?: string;
9
+ disabled?: boolean;
10
+ name?: string;
11
+ value?: string;
12
+ 'aria-label'?: string;
13
+ ariaLabel?: string;
14
+ class?: string;
15
+ onchange?: (checked: boolean) => void;
16
+ children?: Snippet;
17
+ }
18
+
19
+ let {
20
+ checked = $bindable(false),
21
+ label,
22
+ description,
23
+ disabled = false,
24
+ name,
25
+ value,
26
+ 'aria-label': ariaLabelAttr,
27
+ ariaLabel,
28
+ class: className = '',
29
+ onchange,
30
+ children
31
+ }: Props = $props();
32
+ </script>
33
+
34
+ <label
35
+ class="relative flex items-start gap-3 select-none {disabled
36
+ ? 'cursor-not-allowed opacity-60'
37
+ : 'cursor-pointer'} {className}"
38
+ >
39
+ <div class="flex h-5 items-center">
40
+ <input
41
+ type="checkbox"
42
+ bind:checked
43
+ aria-label={ariaLabelAttr || ariaLabel || label}
44
+ onchange={(e) => {
45
+ const isChecked = (e.currentTarget as HTMLInputElement).checked;
46
+ if (onchange) onchange(isChecked);
47
+ }}
48
+ {disabled}
49
+ {name}
50
+ {value}
51
+ class="sr-only"
52
+ />
53
+ <div
54
+ class="flex h-4.5 w-4.5 items-center justify-center rounded border transition-all duration-150 {checked
55
+ ? 'border-primary-600 bg-primary-600 text-white shadow-xs'
56
+ : 'border-neutral-300 bg-white hover:border-neutral-400 dark:border-neutral-700 dark:bg-neutral-900'}"
57
+ >
58
+ {#if checked}
59
+ <Icon name="check" size="xs" />
60
+ {/if}
61
+ </div>
62
+ </div>
63
+
64
+ {#if label || description || children}
65
+ <div class="text-sm leading-5">
66
+ {#if label}
67
+ <span class="font-medium text-neutral-800 dark:text-neutral-200">{label}</span>
68
+ {/if}
69
+ {#if children}
70
+ {@render children()}
71
+ {/if}
72
+ {#if description}
73
+ <p class="text-sm text-neutral-500 dark:text-neutral-400">{description}</p>
74
+ {/if}
75
+ </div>
76
+ {/if}
77
+ </label>
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ checked?: boolean;
4
+ label?: string;
5
+ description?: string;
6
+ disabled?: boolean;
7
+ name?: string;
8
+ value?: string;
9
+ 'aria-label'?: string;
10
+ ariaLabel?: string;
11
+ class?: string;
12
+ onchange?: (checked: boolean) => void;
13
+ children?: Snippet;
14
+ }
15
+ declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
16
+ type Checkbox = ReturnType<typeof Checkbox>;
17
+ export default Checkbox;
@@ -0,0 +1,142 @@
1
+ <script lang="ts">
2
+ import Popover from '../overlays/Popover.svelte';
3
+ import Icon from '../elements/Icon.svelte';
4
+
5
+ interface Props {
6
+ value?: string;
7
+ presets?: string[];
8
+ disabled?: boolean;
9
+ inline?: boolean;
10
+ class?: string;
11
+ }
12
+
13
+ const defaultPresets = [
14
+ '#ff3e00', // Svelte Flame
15
+ '#f59e0b', // Amber
16
+ '#10b981', // Emerald
17
+ '#0ea5e9', // Sky
18
+ '#8b5cf6', // Violet
19
+ '#f43f5e', // Rose
20
+ '#64748b', // Slate
21
+ '#18181b', // Dark
22
+ '#ffffff' // White
23
+ ];
24
+
25
+ let {
26
+ value = $bindable('#ff3e00'),
27
+ presets = defaultPresets,
28
+ disabled = false,
29
+ inline = false,
30
+ class: className = ''
31
+ }: Props = $props();
32
+
33
+ let popoverOpen = $state(false);
34
+ </script>
35
+
36
+ {#if inline}
37
+ <div
38
+ class="inline-flex flex-col gap-3 rounded-xl border border-neutral-200 bg-white p-3 shadow-xs dark:border-neutral-800 dark:bg-neutral-900 {className}"
39
+ >
40
+ <!-- Swatches Grid -->
41
+ <div class="grid grid-cols-5 gap-1.5">
42
+ {#each presets as hex}
43
+ <button
44
+ type="button"
45
+ {disabled}
46
+ onclick={() => (value = hex)}
47
+ class="h-6 w-6 rounded-md border border-neutral-300/40 transition-transform hover:scale-110 {value.toLowerCase() ===
48
+ hex.toLowerCase()
49
+ ? 'scale-105 ring-2 ring-primary-500 ring-offset-2 ring-offset-white dark:ring-offset-neutral-900'
50
+ : ''}"
51
+ style="background-color: {hex};"
52
+ title={hex}
53
+ aria-label="Select color {hex}"
54
+ ></button>
55
+ {/each}
56
+ </div>
57
+
58
+ <!-- Custom Hex & Native Color input -->
59
+ <div class="flex items-center gap-2 border-t border-neutral-200 pt-2 dark:border-neutral-800">
60
+ <label
61
+ class="relative flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-md border border-neutral-300 dark:border-neutral-700"
62
+ style="background-color: {value};"
63
+ >
64
+ <input
65
+ type="color"
66
+ bind:value
67
+ {disabled}
68
+ class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
69
+ aria-label="Pick color"
70
+ />
71
+ </label>
72
+ <input
73
+ type="text"
74
+ bind:value
75
+ {disabled}
76
+ placeholder="#ff3e00"
77
+ class="h-8 w-full rounded-md border border-neutral-200 bg-neutral-50 px-2.5 font-mono text-sm text-neutral-800 focus:border-primary-500 focus:outline-none dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-200"
78
+ />
79
+ </div>
80
+ </div>
81
+ {:else}
82
+ <Popover bind:open={popoverOpen}>
83
+ {#snippet trigger()}
84
+ <button
85
+ type="button"
86
+ {disabled}
87
+ class="inline-flex items-center gap-2 rounded-lg border border-neutral-200 bg-white px-3 py-1.5 text-sm font-medium text-neutral-700 transition hover:border-neutral-300 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-300 dark:hover:border-neutral-700 {className}"
88
+ >
89
+ <span
90
+ class="h-4 w-4 rounded border border-neutral-300/40"
91
+ style="background-color: {value};"
92
+ ></span>
93
+ <span class="font-mono">{value}</span>
94
+ <Icon name="chevron-down" size="xs" class="text-neutral-400" />
95
+ </button>
96
+ {/snippet}
97
+
98
+ <div class="w-48 space-y-3 p-3">
99
+ <div class="text-xs font-semibold text-neutral-500 dark:text-neutral-400">Color Swatches</div>
100
+ <!-- Swatches Grid -->
101
+ <div class="grid grid-cols-5 gap-1.5">
102
+ {#each presets as hex}
103
+ <button
104
+ type="button"
105
+ onclick={() => {
106
+ value = hex;
107
+ popoverOpen = false;
108
+ }}
109
+ class="h-6 w-6 rounded-md border border-neutral-300/40 transition-transform hover:scale-110 {value.toLowerCase() ===
110
+ hex.toLowerCase()
111
+ ? 'scale-105 ring-2 ring-primary-500 ring-offset-2 ring-offset-white dark:ring-offset-neutral-900'
112
+ : ''}"
113
+ style="background-color: {hex};"
114
+ title={hex}
115
+ aria-label="Select color {hex}"
116
+ ></button>
117
+ {/each}
118
+ </div>
119
+
120
+ <!-- Custom Hex & Native Color input -->
121
+ <div class="flex items-center gap-2 border-t border-neutral-200 pt-2 dark:border-neutral-800">
122
+ <label
123
+ class="relative flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-md border border-neutral-300 dark:border-neutral-700"
124
+ style="background-color: {value};"
125
+ >
126
+ <input
127
+ type="color"
128
+ bind:value
129
+ class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
130
+ aria-label="Pick color"
131
+ />
132
+ </label>
133
+ <input
134
+ type="text"
135
+ bind:value
136
+ placeholder="#ff3e00"
137
+ class="h-8 w-full rounded-md border border-neutral-200 bg-neutral-50 px-2.5 font-mono text-sm text-neutral-800 focus:border-primary-500 focus:outline-none dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-200"
138
+ />
139
+ </div>
140
+ </div>
141
+ </Popover>
142
+ {/if}
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ value?: string;
3
+ presets?: string[];
4
+ disabled?: boolean;
5
+ inline?: boolean;
6
+ class?: string;
7
+ }
8
+ declare const ColorPicker: import("svelte").Component<Props, {}, "value">;
9
+ type ColorPicker = ReturnType<typeof ColorPicker>;
10
+ export default ColorPicker;
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { ZodSchema } from 'zod';
4
+ import Alert from '../overlays/Alert.svelte';
5
+
6
+ interface Props {
7
+ schema?: ZodSchema;
8
+ values?: Record<string, any>;
9
+ loading?: boolean;
10
+ errorSummary?: boolean;
11
+ class?: string;
12
+ onsubmit?: (e: SubmitEvent, values: Record<string, any>) => void | Promise<void>;
13
+ children?: Snippet;
14
+ }
15
+
16
+ let {
17
+ schema,
18
+ values = {},
19
+ loading = false,
20
+ errorSummary = false,
21
+ class: className = '',
22
+ onsubmit,
23
+ children
24
+ }: Props = $props();
25
+
26
+ let errors = $state<Record<string, string>>({});
27
+ let isSubmitting = $state(false);
28
+ let isBusy = $derived(loading || isSubmitting);
29
+
30
+ function validate() {
31
+ if (!schema) return true;
32
+ try {
33
+ schema.parse(values);
34
+ errors = {};
35
+ return true;
36
+ } catch (err: any) {
37
+ const fieldErrors: Record<string, string> = {};
38
+ const issues = err?.issues || err?.errors || [];
39
+ for (const e of issues) {
40
+ const field = Array.isArray(e.path) ? e.path.join('.') : String(e.path || 'form');
41
+ if (!fieldErrors[field]) {
42
+ fieldErrors[field] = e.message;
43
+ }
44
+ }
45
+ errors = fieldErrors;
46
+ return false;
47
+ }
48
+ }
49
+
50
+ async function handleSubmit(e: SubmitEvent) {
51
+ e.preventDefault();
52
+ if (!validate()) {
53
+ return;
54
+ }
55
+
56
+ if (onsubmit) {
57
+ isSubmitting = true;
58
+ try {
59
+ await onsubmit(e, values);
60
+ } finally {
61
+ isSubmitting = false;
62
+ }
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <form
68
+ onsubmit={handleSubmit}
69
+ aria-busy={isBusy}
70
+ class="space-y-4 {className} {isBusy ? 'pointer-events-none opacity-80' : ''}"
71
+ novalidate
72
+ >
73
+ {#if errorSummary && Object.keys(errors).length > 0}
74
+ <Alert
75
+ color="error"
76
+ title="Please correct the following errors:"
77
+ description={Object.values(errors).join(' · ')}
78
+ />
79
+ {/if}
80
+
81
+ {#if children}
82
+ {@render children()}
83
+ {/if}
84
+ </form>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ZodSchema } from 'zod';
3
+ interface Props {
4
+ schema?: ZodSchema;
5
+ values?: Record<string, any>;
6
+ loading?: boolean;
7
+ errorSummary?: boolean;
8
+ class?: string;
9
+ onsubmit?: (e: SubmitEvent, values: Record<string, any>) => void | Promise<void>;
10
+ children?: Snippet;
11
+ }
12
+ declare const Form: import("svelte").Component<Props, {}, "">;
13
+ type Form = ReturnType<typeof Form>;
14
+ export default Form;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ label?: string;
6
+ description?: string;
7
+ error?: string;
8
+ hint?: string;
9
+ required?: boolean;
10
+ name?: string;
11
+ class?: string;
12
+ children?: Snippet;
13
+ }
14
+
15
+ let {
16
+ label,
17
+ description,
18
+ error,
19
+ hint,
20
+ required = false,
21
+ name,
22
+ class: className = '',
23
+ children
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ <div class="space-y-1.5 {className}">
28
+ {#if label || hint}
29
+ <div class="flex items-center justify-between">
30
+ {#if label}
31
+ <label for={name} class="block text-sm font-medium text-neutral-700 dark:text-neutral-300">
32
+ {label}
33
+ {#if required}
34
+ <span class="ml-0.5 text-rose-500">*</span>
35
+ {/if}
36
+ </label>
37
+ {/if}
38
+
39
+ {#if hint}
40
+ <span class="text-sm text-neutral-500 dark:text-neutral-400">{hint}</span>
41
+ {/if}
42
+ </div>
43
+ {/if}
44
+
45
+ {#if description}
46
+ <p class="text-sm text-neutral-500 dark:text-neutral-400">{description}</p>
47
+ {/if}
48
+
49
+ {#if children}
50
+ {@render children()}
51
+ {/if}
52
+
53
+ {#if error}
54
+ <p class="text-sm font-medium text-rose-600 dark:text-rose-400">{error}</p>
55
+ {/if}
56
+ </div>