@mrintel/villain-ui 0.2.2 → 0.6.3

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 (159) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3490 -1296
  3. package/dist/components/buttons/Button.svelte +27 -0
  4. package/dist/components/buttons/Button.svelte.d.ts +14 -0
  5. package/dist/components/buttons/ButtonGroup.svelte +17 -0
  6. package/dist/components/buttons/ButtonGroup.svelte.d.ts +8 -0
  7. package/dist/components/buttons/FloatingActionButton.svelte +20 -0
  8. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +12 -0
  9. package/dist/components/buttons/IconButton.svelte +23 -0
  10. package/dist/components/buttons/IconButton.svelte.d.ts +14 -0
  11. package/dist/components/buttons/LinkButton.svelte +24 -0
  12. package/dist/components/buttons/LinkButton.svelte.d.ts +15 -0
  13. package/dist/components/buttons/buttonClasses.d.ts +15 -0
  14. package/dist/components/buttons/buttonClasses.js +15 -0
  15. package/dist/components/buttons/index.d.ts +5 -0
  16. package/dist/components/buttons/index.js +5 -0
  17. package/dist/components/cards/Card.svelte +60 -0
  18. package/dist/components/cards/Card.svelte.d.ts +15 -0
  19. package/dist/components/cards/Container.svelte +17 -0
  20. package/dist/components/cards/Container.svelte.d.ts +10 -0
  21. package/dist/components/cards/Divider.svelte +36 -0
  22. package/dist/components/cards/Divider.svelte.d.ts +11 -0
  23. package/dist/components/cards/Grid.svelte +55 -0
  24. package/dist/components/cards/Grid.svelte.d.ts +10 -0
  25. package/dist/components/cards/Panel.svelte +18 -0
  26. package/dist/components/cards/Panel.svelte.d.ts +11 -0
  27. package/dist/components/cards/SectionHeader.svelte +24 -0
  28. package/dist/components/cards/SectionHeader.svelte.d.ts +12 -0
  29. package/dist/components/cards/index.d.ts +6 -0
  30. package/dist/components/cards/index.js +6 -0
  31. package/dist/components/data/Avatar.svelte +48 -0
  32. package/dist/components/data/Avatar.svelte.d.ts +10 -0
  33. package/dist/components/data/Badge.svelte +45 -0
  34. package/dist/components/data/Badge.svelte.d.ts +14 -0
  35. package/dist/components/data/CalendarGrid.svelte +433 -0
  36. package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
  37. package/dist/components/data/CalendarGrid.types.d.ts +7 -0
  38. package/dist/components/data/CalendarGrid.types.js +1 -0
  39. package/dist/components/data/CodeBlock.svelte +119 -0
  40. package/dist/components/data/CodeBlock.svelte.d.ts +40 -0
  41. package/dist/components/data/List.svelte +87 -0
  42. package/dist/components/data/List.svelte.d.ts +15 -0
  43. package/dist/components/data/Pagination.svelte +121 -0
  44. package/dist/components/data/Pagination.svelte.d.ts +14 -0
  45. package/dist/components/data/Sparkline.svelte +117 -0
  46. package/dist/components/data/Sparkline.svelte.d.ts +43 -0
  47. package/dist/components/data/Stat.svelte +92 -0
  48. package/dist/components/data/Stat.svelte.d.ts +11 -0
  49. package/dist/components/data/Table.svelte +443 -0
  50. package/dist/components/data/Table.svelte.d.ts +30 -0
  51. package/dist/components/data/Table.types.d.ts +14 -0
  52. package/dist/components/data/Table.types.js +1 -0
  53. package/dist/components/data/Tag.svelte +51 -0
  54. package/dist/components/data/Tag.svelte.d.ts +13 -0
  55. package/dist/components/data/index.d.ts +12 -0
  56. package/dist/components/data/index.js +10 -0
  57. package/dist/components/forms/Checkbox.svelte +39 -0
  58. package/dist/components/forms/Checkbox.svelte.d.ts +12 -0
  59. package/dist/components/forms/DatePicker.svelte +61 -0
  60. package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
  61. package/dist/components/forms/DateTimePicker.svelte +63 -0
  62. package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
  63. package/dist/components/forms/FileUpload.svelte +136 -0
  64. package/dist/components/forms/FileUpload.svelte.d.ts +23 -0
  65. package/dist/components/forms/Input.svelte +282 -0
  66. package/dist/components/forms/Input.svelte.d.ts +19 -0
  67. package/dist/components/forms/InputGroup.svelte +7 -0
  68. package/dist/components/forms/InputGroup.svelte.d.ts +20 -0
  69. package/dist/components/forms/RadioGroup.svelte +77 -0
  70. package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
  71. package/dist/components/forms/RangeSlider.svelte +90 -0
  72. package/dist/components/forms/RangeSlider.svelte.d.ts +14 -0
  73. package/dist/components/forms/Select.svelte +106 -0
  74. package/dist/components/forms/Select.svelte.d.ts +18 -0
  75. package/dist/components/forms/Switch.svelte +44 -0
  76. package/dist/components/forms/Switch.svelte.d.ts +12 -0
  77. package/dist/components/forms/Textarea.svelte +52 -0
  78. package/dist/components/forms/Textarea.svelte.d.ts +15 -0
  79. package/dist/components/forms/TimePicker.svelte +63 -0
  80. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  81. package/dist/components/forms/formClasses.d.ts +3 -0
  82. package/dist/components/forms/formClasses.js +3 -0
  83. package/dist/components/forms/index.d.ts +12 -0
  84. package/dist/components/forms/index.js +12 -0
  85. package/dist/components/navigation/Breadcrumbs.svelte +56 -0
  86. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +15 -0
  87. package/dist/components/navigation/ContextMenu.svelte +133 -0
  88. package/dist/components/navigation/ContextMenu.svelte.d.ts +18 -0
  89. package/dist/components/navigation/DropdownMenu.svelte +139 -0
  90. package/dist/components/navigation/DropdownMenu.svelte.d.ts +17 -0
  91. package/dist/components/navigation/Menu.svelte +72 -0
  92. package/dist/components/navigation/Menu.svelte.d.ts +15 -0
  93. package/dist/components/navigation/Navbar.svelte +111 -0
  94. package/dist/components/navigation/Navbar.svelte.d.ts +15 -0
  95. package/dist/components/navigation/Sidebar.svelte +236 -0
  96. package/dist/components/navigation/Sidebar.svelte.d.ts +12 -0
  97. package/dist/components/navigation/Tabs.svelte +86 -0
  98. package/dist/components/navigation/Tabs.svelte.d.ts +19 -0
  99. package/dist/components/navigation/index.d.ts +7 -0
  100. package/dist/components/navigation/index.js +7 -0
  101. package/dist/components/overlays/Alert.svelte +81 -0
  102. package/dist/components/overlays/Alert.svelte.d.ts +15 -0
  103. package/dist/components/overlays/CommandPalette.svelte +182 -0
  104. package/dist/components/overlays/CommandPalette.svelte.d.ts +16 -0
  105. package/dist/components/overlays/Drawer.svelte +158 -0
  106. package/dist/components/overlays/Drawer.svelte.d.ts +16 -0
  107. package/dist/components/overlays/Dropdown.svelte +62 -0
  108. package/dist/components/overlays/Dropdown.svelte.d.ts +11 -0
  109. package/dist/components/overlays/Modal.svelte +125 -0
  110. package/dist/components/overlays/Modal.svelte.d.ts +15 -0
  111. package/dist/components/overlays/Popover.svelte +106 -0
  112. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  113. package/dist/components/overlays/ProgressBar.svelte +29 -0
  114. package/dist/components/overlays/ProgressBar.svelte.d.ts +10 -0
  115. package/dist/components/overlays/SkeletonLoader.svelte +66 -0
  116. package/dist/components/overlays/SkeletonLoader.svelte.d.ts +9 -0
  117. package/dist/components/overlays/Spinner.svelte +33 -0
  118. package/dist/components/overlays/Spinner.svelte.d.ts +7 -0
  119. package/dist/components/overlays/Toast.svelte +111 -0
  120. package/dist/components/overlays/Toast.svelte.d.ts +16 -0
  121. package/dist/components/overlays/Tooltip.svelte +94 -0
  122. package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
  123. package/dist/components/overlays/index.d.ts +11 -0
  124. package/dist/components/overlays/index.js +11 -0
  125. package/dist/components/typography/Code.svelte +10 -0
  126. package/dist/components/typography/Code.svelte.d.ts +6 -0
  127. package/dist/components/typography/Heading.svelte +15 -0
  128. package/dist/components/typography/Heading.svelte.d.ts +10 -0
  129. package/dist/components/typography/Text.svelte +21 -0
  130. package/dist/components/typography/Text.svelte.d.ts +10 -0
  131. package/dist/components/typography/index.d.ts +3 -0
  132. package/dist/components/typography/index.js +3 -0
  133. package/dist/components/utilities/Accordion.svelte +54 -0
  134. package/dist/components/utilities/Accordion.svelte.d.ts +17 -0
  135. package/dist/components/utilities/Carousel.svelte +124 -0
  136. package/dist/components/utilities/Carousel.svelte.d.ts +16 -0
  137. package/dist/components/utilities/Collapse.svelte +46 -0
  138. package/dist/components/utilities/Collapse.svelte.d.ts +10 -0
  139. package/dist/components/utilities/Hero.svelte +42 -0
  140. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  141. package/dist/components/utilities/Portal.svelte +47 -0
  142. package/dist/components/utilities/Portal.svelte.d.ts +21 -0
  143. package/dist/components/utilities/ScrollArea.svelte +33 -0
  144. package/dist/components/utilities/ScrollArea.svelte.d.ts +8 -0
  145. package/dist/components/utilities/SystemConsole.svelte +310 -0
  146. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  147. package/dist/components/utilities/SystemInterface.svelte +726 -0
  148. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  149. package/dist/components/utilities/index.d.ts +9 -0
  150. package/dist/components/utilities/index.js +8 -0
  151. package/dist/components/utilities/utilities.types.d.ts +46 -0
  152. package/dist/components/utilities/utilities.types.js +4 -0
  153. package/dist/index.d.ts +60 -175
  154. package/dist/index.js +24 -4560
  155. package/dist/lib/internal/id.d.ts +12 -0
  156. package/dist/lib/internal/id.js +15 -0
  157. package/dist/theme.css +2821 -0
  158. package/package.json +83 -75
  159. package/dist/index.css +0 -1
@@ -0,0 +1,87 @@
1
+ <script lang="ts">let { variant = 'plain', hoverable = false, items, children } = $props();
2
+ const containerClasses = $derived(variant === 'bordered'
3
+ ? 'panel-raised rounded-[var(--radius-lg)] border border-[var(--color-border)]'
4
+ : '');
5
+ const listClasses = $derived(`list-none ${variant} ${hoverable ? 'hoverable' : ''}`);
6
+ export {};
7
+ </script>
8
+
9
+ {#if variant === 'bordered'}
10
+ <div class={containerClasses}>
11
+ <ul class={listClasses}>
12
+ {#if items}
13
+ {#each items as item (item.id)}
14
+ <li class="flex items-center gap-2">
15
+ {#if item.icon}
16
+ <span class="inline-flex items-center justify-center">
17
+ {@render item.icon()}
18
+ </span>
19
+ {/if}
20
+ {#if typeof item.content === 'string'}
21
+ {item.content}
22
+ {:else}
23
+ {@render item.content()}
24
+ {/if}
25
+ </li>
26
+ {/each}
27
+ {:else}
28
+ {@render children?.()}
29
+ {/if}
30
+ </ul>
31
+ </div>
32
+ {:else}
33
+ <ul class={listClasses}>
34
+ {#if items}
35
+ {#each items as item (item.id)}
36
+ <li class="flex items-center gap-2">
37
+ {#if item.icon}
38
+ <span class="inline-flex items-center justify-center">
39
+ {@render item.icon()}
40
+ </span>
41
+ {/if}
42
+ {#if typeof item.content === 'string'}
43
+ {item.content}
44
+ {:else}
45
+ {@render item.content()}
46
+ {/if}
47
+ </li>
48
+ {/each}
49
+ {:else}
50
+ {@render children?.()}
51
+ {/if}
52
+ </ul>
53
+ {/if}
54
+
55
+ <style>
56
+ ul {
57
+ margin: 0;
58
+ padding: 0;
59
+ color: var(--color-text);
60
+ font-family: var(--font-body);
61
+ }
62
+
63
+ :global(ul.plain > li) {
64
+ padding: 0.5rem 0;
65
+ }
66
+
67
+ :global(ul.bordered > li) {
68
+ padding: 0.75rem 1rem;
69
+ }
70
+
71
+ :global(ul.divided > li) {
72
+ padding: 0.75rem 1rem;
73
+ border-bottom: 1px solid var(--color-border);
74
+ }
75
+
76
+ :global(ul.divided > li:last-child) {
77
+ border-bottom: none;
78
+ }
79
+
80
+ :global(ul.hoverable > li) {
81
+ transition: all var(--ease-luxe);
82
+ cursor: pointer;
83
+ }
84
+
85
+ :global(ul.hoverable > li:hover) {
86
+ background: var(--color-accent-overlay-5);
87
+ }</style>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface ListItem {
3
+ id: string;
4
+ content: string | Snippet;
5
+ icon?: Snippet;
6
+ }
7
+ interface Props {
8
+ variant?: 'plain' | 'bordered' | 'divided';
9
+ hoverable?: boolean;
10
+ items?: ListItem[];
11
+ children?: import('svelte').Snippet;
12
+ }
13
+ declare const List: import("svelte").Component<Props, {}, "">;
14
+ type List = ReturnType<typeof List>;
15
+ export default List;
@@ -0,0 +1,121 @@
1
+ <script lang="ts">"use strict";
2
+ let { currentPage = $bindable(1), totalPages, maxVisible = 7, onPageChange, onpagechange, prevIcon, nextIcon, showLabels } = $props();
3
+ const onPageChangeCallback = $derived(onPageChange ?? onpagechange);
4
+ const displayPrevLabel = $derived(showLabels !== false);
5
+ const displayNextLabel = $derived(showLabels !== false);
6
+ const visiblePages = $derived((() => {
7
+ const pages = [];
8
+ if (totalPages <= maxVisible) {
9
+ // Show all pages
10
+ for (let i = 1; i <= totalPages; i++) {
11
+ pages.push(i);
12
+ }
13
+ }
14
+ else {
15
+ // Always show first page
16
+ pages.push(1);
17
+ const halfVisible = Math.floor((maxVisible - 2) / 2);
18
+ let startPage = Math.max(2, currentPage - halfVisible);
19
+ let endPage = Math.min(totalPages - 1, currentPage + halfVisible);
20
+ // Adjust range if at boundaries
21
+ if (currentPage <= halfVisible + 1) {
22
+ endPage = maxVisible - 1;
23
+ }
24
+ else if (currentPage >= totalPages - halfVisible) {
25
+ startPage = totalPages - maxVisible + 2;
26
+ }
27
+ // Add ellipsis before start if needed
28
+ if (startPage > 2) {
29
+ pages.push('ellipsis');
30
+ }
31
+ // Add middle pages
32
+ for (let i = startPage; i <= endPage; i++) {
33
+ pages.push(i);
34
+ }
35
+ // Add ellipsis after end if needed
36
+ if (endPage < totalPages - 1) {
37
+ pages.push('ellipsis');
38
+ }
39
+ // Always show last page
40
+ pages.push(totalPages);
41
+ }
42
+ return pages;
43
+ })());
44
+ function goToPage(page) {
45
+ if (page < 1 || page > totalPages || page === currentPage)
46
+ return;
47
+ currentPage = page;
48
+ onPageChangeCallback?.(page);
49
+ }
50
+ </script>
51
+
52
+ <nav role="navigation" aria-label="Pagination" class="flex items-center gap-2">
53
+ <!-- Previous Button -->
54
+ <button
55
+ class="flex items-center gap-2 px-3 py-2 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-transparent transition-all duration-300 font-[var(--font-body)] hover:bg-[var(--color-secondary-overlay-10)] hover:border-[var(--color-accent)] disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border)]"
56
+ onclick={() => goToPage(currentPage - 1)}
57
+ disabled={currentPage === 1}
58
+ aria-disabled={currentPage === 1}
59
+ >
60
+ {#if prevIcon}
61
+ <span class="inline-flex items-center justify-center">
62
+ {@render prevIcon()}
63
+ </span>
64
+ {/if}
65
+ {#if displayPrevLabel}Previous{/if}
66
+ </button>
67
+
68
+ <!-- Page Numbers -->
69
+ {#each visiblePages as page}
70
+ {#if page === 'ellipsis'}
71
+ <span class="px-3 py-2 text-[var(--color-text-muted)]">...</span>
72
+ {:else}
73
+ <button
74
+ class="px-3 py-2 rounded-[var(--radius-md)] transition-all duration-300 font-[var(--font-body)]"
75
+ class:active={page === currentPage}
76
+ class:inactive={page !== currentPage}
77
+ onclick={() => goToPage(page)}
78
+ aria-current={page === currentPage ? 'page' : undefined}
79
+ >
80
+ {page}
81
+ </button>
82
+ {/if}
83
+ {/each}
84
+
85
+ <!-- Next Button -->
86
+ <button
87
+ class="flex items-center gap-2 px-3 py-2 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-transparent transition-all duration-300 font-[var(--font-body)] hover:bg-[var(--color-secondary-overlay-10)] hover:border-[var(--color-accent)] disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border)]"
88
+ onclick={() => goToPage(currentPage + 1)}
89
+ disabled={currentPage === totalPages}
90
+ aria-disabled={currentPage === totalPages}
91
+ >
92
+ {#if displayNextLabel}Next{/if}
93
+ {#if nextIcon}
94
+ <span class="inline-flex items-center justify-center">
95
+ {@render nextIcon()}
96
+ </span>
97
+ {/if}
98
+ </button>
99
+ </nav>
100
+
101
+ <style>
102
+ button.active {
103
+ background: var(--color-accent);
104
+ color: var(--color-text);
105
+ cursor: default;
106
+ }
107
+
108
+ button.active {
109
+ box-shadow: 0 0 20px var(--color-accent-overlay-50);
110
+ }
111
+
112
+ button.inactive {
113
+ background: transparent;
114
+ border: 1px solid var(--color-border);
115
+ color: var(--color-text);
116
+ }
117
+
118
+ button.inactive:hover {
119
+ background: var(--color-accent-overlay-10);
120
+ border-color: var(--color-accent);
121
+ }</style>
@@ -0,0 +1,14 @@
1
+ interface Props {
2
+ currentPage: number;
3
+ totalPages: number;
4
+ maxVisible?: number;
5
+ onPageChange?: (page: number) => void;
6
+ /** @deprecated Use onPageChange */
7
+ onpagechange?: (page: number) => void;
8
+ prevIcon?: import('svelte').Snippet;
9
+ nextIcon?: import('svelte').Snippet;
10
+ showLabels?: boolean;
11
+ }
12
+ declare const Pagination: import("svelte").Component<Props, {}, "currentPage">;
13
+ type Pagination = ReturnType<typeof Pagination>;
14
+ export default Pagination;
@@ -0,0 +1,117 @@
1
+ <script lang="ts">import { onMount } from 'svelte';
2
+ let { data, color = 'var(--color-accent-soft)', height = 40, width = 200, showDots = false, showFill = false, strokeWidth = 2, animationDuration = 500 } = $props();
3
+ let pathElement = $state();
4
+ let mounted = $state(false);
5
+ // Calculate min/max for scaling
6
+ const min = $derived(Math.min(...data));
7
+ const max = $derived(Math.max(...data));
8
+ const range = $derived(max - min || 1); // Avoid division by zero
9
+ // Padding for dots and stroke
10
+ const padding = $derived(showDots ? 4 : strokeWidth);
11
+ // Calculate viewBox dimensions
12
+ const viewBoxWidth = $derived(width);
13
+ const viewBoxHeight = $derived(height);
14
+ // Generate SVG path from data points
15
+ const points = $derived(data.map((value, index) => {
16
+ const x = padding + (index / (data.length - 1)) * (viewBoxWidth - padding * 2);
17
+ const y = viewBoxHeight - padding - ((value - min) / range) * (viewBoxHeight - padding * 2);
18
+ return { x, y, value };
19
+ }));
20
+ const pathData = $derived(points.length > 0
21
+ ? points.reduce((path, point, index) => {
22
+ if (index === 0) {
23
+ return `M ${point.x},${point.y}`;
24
+ }
25
+ return `${path} L ${point.x},${point.y}`;
26
+ }, '')
27
+ : '');
28
+ // Generate fill path (line + close to bottom)
29
+ const fillPathData = $derived(points.length > 0
30
+ ? `${pathData} L ${viewBoxWidth - padding},${viewBoxHeight} L ${padding},${viewBoxHeight} Z`
31
+ : '');
32
+ // Gradient ID for unique identification
33
+ const gradientId = $derived(`sparkline-gradient-${Math.random().toString(36).slice(2, 9)}`);
34
+ // Animation
35
+ onMount(() => {
36
+ if (animationDuration > 0 && pathElement) {
37
+ const length = pathElement.getTotalLength();
38
+ pathElement.style.strokeDasharray = `${length}`;
39
+ pathElement.style.strokeDashoffset = `${length}`;
40
+ // Check for reduced motion preference
41
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
42
+ if (!prefersReducedMotion) {
43
+ // Animate the stroke
44
+ requestAnimationFrame(() => {
45
+ if (pathElement) {
46
+ pathElement.style.transition = `stroke-dashoffset ${animationDuration}ms var(--ease-luxe)`;
47
+ pathElement.style.strokeDashoffset = '0';
48
+ }
49
+ });
50
+ }
51
+ else {
52
+ // Skip animation if reduced motion is preferred
53
+ pathElement.style.strokeDashoffset = '0';
54
+ }
55
+ }
56
+ mounted = true;
57
+ });
58
+ </script>
59
+
60
+ <svg
61
+ {width}
62
+ {height}
63
+ viewBox="0 0 {viewBoxWidth} {viewBoxHeight}"
64
+ class="sparkline"
65
+ role="img"
66
+ aria-label="Trend sparkline visualization"
67
+ style="overflow: visible;"
68
+ >
69
+ <!-- Gradient definition for fill -->
70
+ {#if showFill}
71
+ <defs>
72
+ <linearGradient id={gradientId} x1="0%" y1="0%" x2="0%" y2="100%">
73
+ <stop offset="0%" style="stop-color: {color}; stop-opacity: 0.3;" />
74
+ <stop offset="100%" style="stop-color: {color}; stop-opacity: 0.05;" />
75
+ </linearGradient>
76
+ </defs>
77
+ {/if}
78
+
79
+ <!-- Gradient fill area -->
80
+ {#if showFill && mounted}
81
+ <path
82
+ d={fillPathData}
83
+ fill="url(#{gradientId})"
84
+ style="opacity: 0; animation: fade-in {animationDuration}ms var(--ease-luxe) forwards;"
85
+ />
86
+ {/if}
87
+
88
+ <!-- Line path -->
89
+ <path
90
+ bind:this={pathElement}
91
+ d={pathData}
92
+ fill="none"
93
+ stroke={color}
94
+ stroke-width={strokeWidth}
95
+ stroke-linecap="round"
96
+ stroke-linejoin="round"
97
+ />
98
+
99
+ <!-- Data point dots -->
100
+ {#if showDots && mounted}
101
+ {#each points as point, i}
102
+ <circle
103
+ cx={point.x}
104
+ cy={point.y}
105
+ r="3"
106
+ fill={color}
107
+ style="opacity: 0; animation: fade-in {animationDuration}ms var(--ease-luxe) {i *
108
+ 50}ms forwards;"
109
+ />
110
+ {/each}
111
+ {/if}
112
+ </svg>
113
+
114
+ <style>
115
+ .sparkline {
116
+ display: block;
117
+ }</style>
@@ -0,0 +1,43 @@
1
+ interface Props {
2
+ /**
3
+ * Array of numeric data points to plot
4
+ */
5
+ data: number[];
6
+ /**
7
+ * Line color (defaults to accent primary)
8
+ */
9
+ color?: string;
10
+ /**
11
+ * Chart height in pixels
12
+ * @default 40
13
+ */
14
+ height?: number;
15
+ /**
16
+ * Chart width in pixels
17
+ * @default 200
18
+ */
19
+ width?: number;
20
+ /**
21
+ * Show dots at each data point
22
+ * @default false
23
+ */
24
+ showDots?: boolean;
25
+ /**
26
+ * Show gradient fill below the line
27
+ * @default false
28
+ */
29
+ showFill?: boolean;
30
+ /**
31
+ * Line thickness in pixels
32
+ * @default 2
33
+ */
34
+ strokeWidth?: number;
35
+ /**
36
+ * Animation duration in milliseconds (set to 0 to disable)
37
+ * @default 500
38
+ */
39
+ animationDuration?: number;
40
+ }
41
+ declare const Sparkline: import("svelte").Component<Props, {}, "">;
42
+ type Sparkline = ReturnType<typeof Sparkline>;
43
+ export default Sparkline;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">"use strict";
2
+ let { label, value, change, trend, icon, description } = $props();
3
+ const trendColors = {
4
+ up: 'var(--color-success)',
5
+ down: 'var(--color-error)',
6
+ neutral: 'var(--color-text-muted)'
7
+ };
8
+ const trendColor = $derived(trend ? trendColors[trend] : 'var(--color-text-muted)');
9
+ const showGlow = $derived(trend === 'up');
10
+ </script>
11
+
12
+ <div class="panel-spectral rounded-[var(--radius-xl)] p-6">
13
+ <!-- Top Row: Icon & Label -->
14
+ <div class="flex items-start justify-between mb-4">
15
+ <div class="flex-1">
16
+ <div
17
+ class="uppercase tracking-wider"
18
+ style="color: var(--color-text-soft); font-family: var(--font-body); font-size: 0.875rem; font-weight: 500;"
19
+ >
20
+ {label}
21
+ </div>
22
+ </div>
23
+
24
+ {#if icon}
25
+ <div class="w-10 h-10" style="color: var(--color-accent-soft);">
26
+ {@render icon()}
27
+ </div>
28
+ {/if}
29
+ </div>
30
+
31
+ <!-- Value Display -->
32
+ <div
33
+ class="mb-2"
34
+ class:text-glow={showGlow}
35
+ style="color: var(--color-text); font-family: var(--font-heading); font-size: 2.5rem; font-weight: 700; line-height: 1.2;"
36
+ >
37
+ {value}
38
+ </div>
39
+
40
+ <!-- Change Indicator & Description -->
41
+ {#if change !== undefined || description}
42
+ <div class="flex items-center gap-2 flex-wrap">
43
+ {#if change !== undefined}
44
+ <div
45
+ class="inline-flex items-center gap-1 font-semibold text-sm"
46
+ style="color: {trendColor};"
47
+ >
48
+ {#if trend === 'up'}
49
+ <svg
50
+ width="16"
51
+ height="16"
52
+ viewBox="0 0 16 16"
53
+ fill="none"
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ >
56
+ <path
57
+ d="M8 12V4M8 4L4 8M8 4L12 8"
58
+ stroke="currentColor"
59
+ stroke-width="2"
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ />
63
+ </svg>
64
+ {:else if trend === 'down'}
65
+ <svg
66
+ width="16"
67
+ height="16"
68
+ viewBox="0 0 16 16"
69
+ fill="none"
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ >
72
+ <path
73
+ d="M8 4V12M8 12L4 8M8 12L12 8"
74
+ stroke="currentColor"
75
+ stroke-width="2"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ />
79
+ </svg>
80
+ {/if}
81
+ {change > 0 ? '+' : ''}{change}%
82
+ </div>
83
+ {/if}
84
+
85
+ {#if description}
86
+ <div class="text-xs" style="color: var(--color-text-muted);">
87
+ {description}
88
+ </div>
89
+ {/if}
90
+ </div>
91
+ {/if}
92
+ </div>
@@ -0,0 +1,11 @@
1
+ interface Props {
2
+ label: string;
3
+ value: string | number;
4
+ change?: number;
5
+ trend?: 'up' | 'down' | 'neutral';
6
+ icon?: import('svelte').Snippet;
7
+ description?: string;
8
+ }
9
+ declare const Stat: import("svelte").Component<Props, {}, "">;
10
+ type Stat = ReturnType<typeof Stat>;
11
+ export default Stat;