@mrintel/villain-ui 0.2.0 → 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 (133) hide show
  1. package/dist/components/buttons/Button.svelte +33 -0
  2. package/dist/components/buttons/Button.svelte.d.ts +11 -0
  3. package/dist/components/buttons/ButtonGroup.svelte +30 -0
  4. package/dist/components/buttons/ButtonGroup.svelte.d.ts +8 -0
  5. package/dist/components/buttons/FloatingActionButton.svelte +44 -0
  6. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +11 -0
  7. package/dist/components/buttons/IconButton.svelte +53 -0
  8. package/dist/components/buttons/IconButton.svelte.d.ts +13 -0
  9. package/dist/components/buttons/LinkButton.svelte +37 -0
  10. package/dist/components/buttons/LinkButton.svelte.d.ts +12 -0
  11. package/dist/components/buttons/buttonClasses.d.ts +10 -0
  12. package/dist/components/buttons/buttonClasses.js +10 -0
  13. package/dist/components/buttons/index.d.ts +5 -0
  14. package/dist/components/buttons/index.js +5 -0
  15. package/dist/components/cards/Card.svelte +46 -0
  16. package/dist/components/cards/Card.svelte.d.ts +11 -0
  17. package/dist/components/cards/Container.svelte +33 -0
  18. package/dist/components/cards/Container.svelte.d.ts +10 -0
  19. package/dist/components/cards/Divider.svelte +52 -0
  20. package/dist/components/cards/Divider.svelte.d.ts +9 -0
  21. package/dist/components/cards/Grid.svelte +44 -0
  22. package/dist/components/cards/Grid.svelte.d.ts +10 -0
  23. package/dist/components/cards/Panel.svelte +32 -0
  24. package/dist/components/cards/Panel.svelte.d.ts +10 -0
  25. package/dist/components/cards/SectionHeader.svelte +38 -0
  26. package/dist/components/cards/SectionHeader.svelte.d.ts +11 -0
  27. package/dist/components/cards/index.d.ts +6 -0
  28. package/dist/components/cards/index.js +6 -0
  29. package/dist/components/data/Avatar.svelte +67 -0
  30. package/dist/components/data/Avatar.svelte.d.ts +10 -0
  31. package/dist/components/data/Badge.svelte +32 -0
  32. package/dist/components/data/Badge.svelte.d.ts +8 -0
  33. package/dist/components/data/CodeBlock.svelte +121 -0
  34. package/dist/components/data/CodeBlock.svelte.d.ts +32 -0
  35. package/dist/components/data/List.svelte +64 -0
  36. package/dist/components/data/List.svelte.d.ts +8 -0
  37. package/dist/components/data/Pagination.svelte +123 -0
  38. package/dist/components/data/Pagination.svelte.d.ts +9 -0
  39. package/dist/components/data/Stat.svelte +103 -0
  40. package/dist/components/data/Stat.svelte.d.ts +11 -0
  41. package/dist/components/data/Table.svelte +76 -0
  42. package/dist/components/data/Table.svelte.d.ts +9 -0
  43. package/dist/components/data/Tag.svelte +53 -0
  44. package/dist/components/data/Tag.svelte.d.ts +9 -0
  45. package/dist/components/data/index.d.ts +8 -0
  46. package/dist/components/data/index.js +8 -0
  47. package/dist/components/forms/Checkbox.svelte +51 -0
  48. package/dist/components/forms/Checkbox.svelte.d.ts +10 -0
  49. package/dist/components/forms/FileUpload.svelte +164 -0
  50. package/dist/components/forms/FileUpload.svelte.d.ts +22 -0
  51. package/dist/components/forms/Input.svelte +57 -0
  52. package/dist/components/forms/Input.svelte.d.ts +13 -0
  53. package/dist/components/forms/InputGroup.svelte +7 -0
  54. package/dist/components/forms/InputGroup.svelte.d.ts +20 -0
  55. package/dist/components/forms/RadioGroup.svelte +87 -0
  56. package/dist/components/forms/RadioGroup.svelte.d.ts +15 -0
  57. package/dist/components/forms/RangeSlider.svelte +116 -0
  58. package/dist/components/forms/RangeSlider.svelte.d.ts +14 -0
  59. package/dist/components/forms/Select.svelte +71 -0
  60. package/dist/components/forms/Select.svelte.d.ts +16 -0
  61. package/dist/components/forms/Switch.svelte +56 -0
  62. package/dist/components/forms/Switch.svelte.d.ts +10 -0
  63. package/dist/components/forms/Textarea.svelte +57 -0
  64. package/dist/components/forms/Textarea.svelte.d.ts +13 -0
  65. package/dist/components/forms/index.d.ts +9 -0
  66. package/dist/components/forms/index.js +9 -0
  67. package/dist/components/navigation/Breadcrumbs.svelte +59 -0
  68. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +14 -0
  69. package/dist/components/navigation/ContextMenu.svelte +83 -0
  70. package/dist/components/navigation/ContextMenu.svelte.d.ts +11 -0
  71. package/dist/components/navigation/DropdownMenu.svelte +80 -0
  72. package/dist/components/navigation/DropdownMenu.svelte.d.ts +10 -0
  73. package/dist/components/navigation/Menu.svelte +48 -0
  74. package/dist/components/navigation/Menu.svelte.d.ts +15 -0
  75. package/dist/components/navigation/Navbar.svelte +32 -0
  76. package/dist/components/navigation/Navbar.svelte.d.ts +9 -0
  77. package/dist/components/navigation/Sidebar.svelte +35 -0
  78. package/dist/components/navigation/Sidebar.svelte.d.ts +10 -0
  79. package/dist/components/navigation/Tabs.svelte +54 -0
  80. package/dist/components/navigation/Tabs.svelte.d.ts +15 -0
  81. package/dist/components/navigation/index.d.ts +7 -0
  82. package/dist/components/navigation/index.js +7 -0
  83. package/dist/components/overlays/Alert.svelte +99 -0
  84. package/dist/components/overlays/Alert.svelte.d.ts +11 -0
  85. package/dist/components/overlays/CommandPalette.svelte +217 -0
  86. package/dist/components/overlays/CommandPalette.svelte.d.ts +16 -0
  87. package/dist/components/overlays/Drawer.svelte +167 -0
  88. package/dist/components/overlays/Drawer.svelte.d.ts +14 -0
  89. package/dist/components/overlays/Dropdown.svelte +30 -0
  90. package/dist/components/overlays/Dropdown.svelte.d.ts +9 -0
  91. package/dist/components/overlays/Modal.svelte +130 -0
  92. package/dist/components/overlays/Modal.svelte.d.ts +13 -0
  93. package/dist/components/overlays/Popover.svelte +131 -0
  94. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  95. package/dist/components/overlays/ProgressBar.svelte +45 -0
  96. package/dist/components/overlays/ProgressBar.svelte.d.ts +10 -0
  97. package/dist/components/overlays/SkeletonLoader.svelte +82 -0
  98. package/dist/components/overlays/SkeletonLoader.svelte.d.ts +9 -0
  99. package/dist/components/overlays/Spinner.svelte +43 -0
  100. package/dist/components/overlays/Spinner.svelte.d.ts +7 -0
  101. package/dist/components/overlays/Toast.svelte +140 -0
  102. package/dist/components/overlays/Toast.svelte.d.ts +13 -0
  103. package/dist/components/overlays/Tooltip.svelte +115 -0
  104. package/dist/components/overlays/Tooltip.svelte.d.ts +10 -0
  105. package/dist/components/overlays/index.d.ts +11 -0
  106. package/dist/components/overlays/index.js +11 -0
  107. package/dist/components/typography/Code.svelte +14 -0
  108. package/dist/components/typography/Code.svelte.d.ts +6 -0
  109. package/dist/components/typography/Heading.svelte +22 -0
  110. package/dist/components/typography/Heading.svelte.d.ts +9 -0
  111. package/dist/components/typography/Text.svelte +24 -0
  112. package/dist/components/typography/Text.svelte.d.ts +9 -0
  113. package/dist/components/typography/index.d.ts +3 -0
  114. package/dist/components/typography/index.js +3 -0
  115. package/dist/components/utilities/Accordion.svelte +67 -0
  116. package/dist/components/utilities/Accordion.svelte.d.ts +14 -0
  117. package/dist/components/utilities/Carousel.svelte +152 -0
  118. package/dist/components/utilities/Carousel.svelte.d.ts +16 -0
  119. package/dist/components/utilities/Collapse.svelte +60 -0
  120. package/dist/components/utilities/Collapse.svelte.d.ts +10 -0
  121. package/dist/components/utilities/Portal.svelte +72 -0
  122. package/dist/components/utilities/Portal.svelte.d.ts +21 -0
  123. package/dist/components/utilities/ScrollArea.svelte +41 -0
  124. package/dist/components/utilities/ScrollArea.svelte.d.ts +8 -0
  125. package/dist/components/utilities/index.d.ts +5 -0
  126. package/dist/components/utilities/index.js +5 -0
  127. package/dist/index.d.ts +15 -175
  128. package/dist/index.js +24 -4560
  129. package/dist/lib/internal/id.d.ts +12 -0
  130. package/dist/lib/internal/id.js +15 -0
  131. package/dist/theme.css +218 -0
  132. package/package.json +14 -7
  133. package/dist/index.css +0 -1
@@ -0,0 +1,67 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ src?: string;
4
+ alt: string;
5
+ size?: 'sm' | 'md' | 'lg' | 'xl';
6
+ fallback?: string;
7
+ glow?: boolean;
8
+ }
9
+
10
+ let { src, alt, size = 'md', fallback, glow = false }: Props = $props();
11
+
12
+ let imageLoaded = $state(!!src);
13
+ let imageError = $state(false);
14
+
15
+ const sizeClasses = {
16
+ sm: 'w-8 h-8',
17
+ md: 'w-12 h-12',
18
+ lg: 'w-16 h-16',
19
+ xl: 'w-24 h-24'
20
+ };
21
+
22
+ const fontSizes = {
23
+ sm: '0.75rem',
24
+ md: '1rem',
25
+ lg: '1.25rem',
26
+ xl: '1.5rem'
27
+ };
28
+
29
+ const classes = $derived(
30
+ `${sizeClasses[size]} rounded-full overflow-hidden inline-flex items-center justify-center transition-all duration-300 hover:scale-105 ${glow ? 'border-2 border-[var(--color-accent)] accent-glow' : ''}`
31
+ );
32
+
33
+ const fallbackText = $derived(
34
+ fallback || alt.charAt(0).toUpperCase()
35
+ );
36
+
37
+ function handleImageError() {
38
+ imageError = true;
39
+ imageLoaded = false;
40
+ }
41
+
42
+ function handleImageLoad() {
43
+ imageLoaded = true;
44
+ imageError = false;
45
+ }
46
+ </script>
47
+
48
+ {#if src && !imageError}
49
+ <div class={classes}>
50
+ <img
51
+ {src}
52
+ {alt}
53
+ class="w-full h-full object-cover"
54
+ onerror={handleImageError}
55
+ onload={handleImageLoad}
56
+ />
57
+ </div>
58
+ {:else}
59
+ <div
60
+ class={classes}
61
+ style="background: linear-gradient(135deg, var(--color-accent) 0%, rgba(127, 61, 255, 0.7) 100%); color: var(--color-text); font-weight: 600; text-transform: uppercase; font-size: {fontSizes[size]};"
62
+ role="img"
63
+ aria-label={alt}
64
+ >
65
+ {fallbackText}
66
+ </div>
67
+ {/if}
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ src?: string;
3
+ alt: string;
4
+ size?: 'sm' | 'md' | 'lg' | 'xl';
5
+ fallback?: string;
6
+ glow?: boolean;
7
+ }
8
+ declare const Avatar: import("svelte").Component<Props, {}, "">;
9
+ type Avatar = ReturnType<typeof Avatar>;
10
+ export default Avatar;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ variant?: 'default' | 'success' | 'warning' | 'error' | 'accent';
4
+ size?: 'sm' | 'md';
5
+ children?: import('svelte').Snippet;
6
+ }
7
+
8
+ let { variant = 'default', size = 'md', children }: Props = $props();
9
+
10
+ const variantClasses = {
11
+ default: 'bg-[var(--color-base-3)] text-[var(--color-text-soft)] border-[var(--color-base-3)]',
12
+ success:
13
+ 'bg-[rgba(0,232,151,0.15)] text-[var(--color-success)] border-[var(--color-success)]',
14
+ warning:
15
+ 'bg-[rgba(255,200,97,0.15)] text-[var(--color-warning)] border-[var(--color-warning)]',
16
+ error: 'bg-[rgba(255,74,106,0.15)] text-[var(--color-error)] border-[var(--color-error)]',
17
+ accent: 'bg-[rgba(127,61,255,0.15)] text-[var(--color-accent-soft)] border-[var(--color-accent)] accent-glow'
18
+ };
19
+
20
+ const sizeClasses = {
21
+ sm: 'px-2 py-0.5 text-xs',
22
+ md: 'px-2.5 py-1 text-sm'
23
+ };
24
+
25
+ const classes = $derived(
26
+ `inline-flex items-center justify-center rounded-[var(--radius-pill)] border font-[var(--font-body)] font-medium transition-all duration-300 ${variantClasses[variant]} ${sizeClasses[size]}`
27
+ );
28
+ </script>
29
+
30
+ <span class={classes}>
31
+ {@render children?.()}
32
+ </span>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ variant?: 'default' | 'success' | 'warning' | 'error' | 'accent';
3
+ size?: 'sm' | 'md';
4
+ children?: import('svelte').Snippet;
5
+ }
6
+ declare const Badge: import("svelte").Component<Props, {}, "">;
7
+ type Badge = ReturnType<typeof Badge>;
8
+ export default Badge;
@@ -0,0 +1,121 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Props for the CodeBlock component.
4
+ *
5
+ * This is a presentational component that provides layout and styling for syntax-highlighted code.
6
+ * Consumers are responsible for providing pre-highlighted HTML via the default slot and ensuring
7
+ * the content is properly sanitized to prevent XSS attacks.
8
+ */
9
+ interface Props {
10
+ /**
11
+ * Optional filename to display in the header.
12
+ */
13
+ filename?: string;
14
+ /**
15
+ * Whether to show line numbers in the gutter.
16
+ */
17
+ showLineNumbers?: boolean;
18
+ /**
19
+ * Total number of lines in the code. Required when showLineNumbers is true.
20
+ */
21
+ lineCount?: number;
22
+ /**
23
+ * Array of line numbers (1-indexed) to highlight in the gutter.
24
+ */
25
+ highlightLines?: number[];
26
+ /**
27
+ * Slot for pre-highlighted code HTML.
28
+ */
29
+ children?: import('svelte').Snippet;
30
+ }
31
+
32
+ let {
33
+ filename,
34
+ showLineNumbers = false,
35
+ lineCount = 0,
36
+ highlightLines = [],
37
+ children
38
+ }: Props = $props();
39
+
40
+ // Development-only validation
41
+ </script>
42
+
43
+ <div class="glass-panel rounded-lg overflow-hidden">
44
+ {#if filename}
45
+ <div
46
+ class="px-4 py-2 border-b border-border"
47
+ style="background: var(--color-base-2); color: var(--color-text-soft); font-family: var(--font-mono); font-size: 0.875rem;"
48
+ >
49
+ {filename}
50
+ </div>
51
+ {/if}
52
+
53
+ <div
54
+ class="p-4 overflow-x-auto"
55
+ style="background: var(--color-base-1); font-family: var(--font-mono); font-size: 0.875rem; line-height: 1.6;"
56
+ >
57
+ {#if showLineNumbers}
58
+ <div class="flex">
59
+ <div
60
+ class="pr-4 border-r border-border select-none"
61
+ style="color: var(--color-text-muted);"
62
+ >
63
+ {#each Array.from({ length: lineCount }, (_, i) => i + 1) as lineNum}
64
+ <div
65
+ class:highlighted={highlightLines.includes(lineNum)}
66
+ style={highlightLines.includes(lineNum)
67
+ ? 'background: rgba(127, 61, 255, 0.1);'
68
+ : ''}
69
+ >
70
+ {lineNum}
71
+ </div>
72
+ {/each}
73
+ </div>
74
+ <div class="pl-4 flex-1">
75
+ {@render children?.()}
76
+ </div>
77
+ </div>
78
+ {:else}
79
+ <div>
80
+ {@render children?.()}
81
+ </div>
82
+ {/if}
83
+ </div>
84
+ </div>
85
+
86
+ <style>
87
+ /* Custom scrollbar styling */
88
+ div::-webkit-scrollbar {
89
+ height: 8px;
90
+ }
91
+
92
+ div::-webkit-scrollbar-track {
93
+ background: var(--color-base-1);
94
+ }
95
+
96
+ div::-webkit-scrollbar-thumb {
97
+ background: var(--color-accent);
98
+ border-radius: var(--radius-sm);
99
+ }
100
+
101
+ div::-webkit-scrollbar-thumb:hover {
102
+ background: var(--color-accent-soft);
103
+ }
104
+
105
+ /* Highlight gutter numbers */
106
+ .highlighted {
107
+ background: rgba(127, 61, 255, 0.1);
108
+ }
109
+
110
+ /*
111
+ * Consumer-provided line highlighting:
112
+ * Consumers should apply the `.line` class to each code line element
113
+ * and the `.highlighted` class to lines that should be highlighted.
114
+ * This ensures consistent styling with the component's luxury aesthetic.
115
+ */
116
+ :global(.line.highlighted) {
117
+ background: rgba(127, 61, 255, 0.1);
118
+ display: inline-block;
119
+ width: 100%;
120
+ }
121
+ </style>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Props for the CodeBlock component.
3
+ *
4
+ * This is a presentational component that provides layout and styling for syntax-highlighted code.
5
+ * Consumers are responsible for providing pre-highlighted HTML via the default slot and ensuring
6
+ * the content is properly sanitized to prevent XSS attacks.
7
+ */
8
+ interface Props {
9
+ /**
10
+ * Optional filename to display in the header.
11
+ */
12
+ filename?: string;
13
+ /**
14
+ * Whether to show line numbers in the gutter.
15
+ */
16
+ showLineNumbers?: boolean;
17
+ /**
18
+ * Total number of lines in the code. Required when showLineNumbers is true.
19
+ */
20
+ lineCount?: number;
21
+ /**
22
+ * Array of line numbers (1-indexed) to highlight in the gutter.
23
+ */
24
+ highlightLines?: number[];
25
+ /**
26
+ * Slot for pre-highlighted code HTML.
27
+ */
28
+ children?: import('svelte').Snippet;
29
+ }
30
+ declare const CodeBlock: import("svelte").Component<Props, {}, "">;
31
+ type CodeBlock = ReturnType<typeof CodeBlock>;
32
+ export default CodeBlock;
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ variant?: 'plain' | 'bordered' | 'divided';
4
+ hoverable?: boolean;
5
+ children?: import('svelte').Snippet;
6
+ }
7
+
8
+ let { variant = 'plain', hoverable = false, children }: Props = $props();
9
+
10
+ const containerClasses = $derived(
11
+ variant === 'bordered'
12
+ ? 'glass-panel rounded-[var(--radius-lg)] border border-[var(--color-border)]'
13
+ : ''
14
+ );
15
+
16
+ const listClasses = $derived(`list-none ${variant} ${hoverable ? 'hoverable' : ''}`);
17
+ </script>
18
+
19
+ {#if variant === 'bordered'}
20
+ <div class={containerClasses}>
21
+ <ul class={listClasses}>
22
+ {@render children?.()}
23
+ </ul>
24
+ </div>
25
+ {:else}
26
+ <ul class={listClasses}>
27
+ {@render children?.()}
28
+ </ul>
29
+ {/if}
30
+
31
+ <style>
32
+ ul {
33
+ margin: 0;
34
+ padding: 0;
35
+ color: var(--color-text);
36
+ font-family: var(--font-body);
37
+ }
38
+
39
+ :global(ul.plain > li) {
40
+ padding: 0.5rem 0;
41
+ }
42
+
43
+ :global(ul.bordered > li) {
44
+ padding: 0.75rem 1rem;
45
+ }
46
+
47
+ :global(ul.divided > li) {
48
+ padding: 0.75rem 1rem;
49
+ border-bottom: 1px solid var(--color-border);
50
+ }
51
+
52
+ :global(ul.divided > li:last-child) {
53
+ border-bottom: none;
54
+ }
55
+
56
+ :global(ul.hoverable > li) {
57
+ transition: all var(--ease-luxe);
58
+ cursor: pointer;
59
+ }
60
+
61
+ :global(ul.hoverable > li:hover) {
62
+ background: rgba(127, 61, 255, 0.05);
63
+ }
64
+ </style>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ variant?: 'plain' | 'bordered' | 'divided';
3
+ hoverable?: boolean;
4
+ children?: import('svelte').Snippet;
5
+ }
6
+ declare const List: import("svelte").Component<Props, {}, "">;
7
+ type List = ReturnType<typeof List>;
8
+ export default List;
@@ -0,0 +1,123 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ currentPage: number;
4
+ totalPages: number;
5
+ maxVisible?: number;
6
+ onpagechange?: (page: number) => void;
7
+ }
8
+
9
+ let { currentPage = $bindable(1), totalPages, maxVisible = 7, onpagechange }: Props = $props();
10
+
11
+ const visiblePages = $derived((() => {
12
+ const pages: (number | 'ellipsis')[] = [];
13
+
14
+ if (totalPages <= maxVisible) {
15
+ // Show all pages
16
+ for (let i = 1; i <= totalPages; i++) {
17
+ pages.push(i);
18
+ }
19
+ } else {
20
+ // Always show first page
21
+ pages.push(1);
22
+
23
+ const halfVisible = Math.floor((maxVisible - 2) / 2);
24
+ let startPage = Math.max(2, currentPage - halfVisible);
25
+ let endPage = Math.min(totalPages - 1, currentPage + halfVisible);
26
+
27
+ // Adjust range if at boundaries
28
+ if (currentPage <= halfVisible + 1) {
29
+ endPage = maxVisible - 1;
30
+ } else if (currentPage >= totalPages - halfVisible) {
31
+ startPage = totalPages - maxVisible + 2;
32
+ }
33
+
34
+ // Add ellipsis before start if needed
35
+ if (startPage > 2) {
36
+ pages.push('ellipsis');
37
+ }
38
+
39
+ // Add middle pages
40
+ for (let i = startPage; i <= endPage; i++) {
41
+ pages.push(i);
42
+ }
43
+
44
+ // Add ellipsis after end if needed
45
+ if (endPage < totalPages - 1) {
46
+ pages.push('ellipsis');
47
+ }
48
+
49
+ // Always show last page
50
+ pages.push(totalPages);
51
+ }
52
+
53
+ return pages;
54
+ })());
55
+
56
+ function goToPage(page: number) {
57
+ if (page < 1 || page > totalPages || page === currentPage) return;
58
+ currentPage = page;
59
+ onpagechange?.(page);
60
+ }
61
+ </script>
62
+
63
+ <nav role="navigation" aria-label="Pagination" class="flex items-center gap-2">
64
+ <!-- Previous Button -->
65
+ <button
66
+ class="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-[rgba(127,61,255,0.1)] hover:border-[var(--color-accent)] disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border)]"
67
+ onclick={() => goToPage(currentPage - 1)}
68
+ disabled={currentPage === 1}
69
+ aria-disabled={currentPage === 1}
70
+ >
71
+ Previous
72
+ </button>
73
+
74
+ <!-- Page Numbers -->
75
+ {#each visiblePages as page}
76
+ {#if page === 'ellipsis'}
77
+ <span class="px-3 py-2 text-[var(--color-text-muted)]">...</span>
78
+ {:else}
79
+ <button
80
+ class="px-3 py-2 rounded-[var(--radius-md)] transition-all duration-300 font-[var(--font-body)]"
81
+ class:active={page === currentPage}
82
+ class:inactive={page !== currentPage}
83
+ onclick={() => goToPage(page)}
84
+ aria-current={page === currentPage ? 'page' : undefined}
85
+ >
86
+ {page}
87
+ </button>
88
+ {/if}
89
+ {/each}
90
+
91
+ <!-- Next Button -->
92
+ <button
93
+ class="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-[rgba(127,61,255,0.1)] hover:border-[var(--color-accent)] disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border)]"
94
+ onclick={() => goToPage(currentPage + 1)}
95
+ disabled={currentPage === totalPages}
96
+ aria-disabled={currentPage === totalPages}
97
+ >
98
+ Next
99
+ </button>
100
+ </nav>
101
+
102
+ <style>
103
+ button.active {
104
+ background: var(--color-accent);
105
+ color: var(--color-text);
106
+ cursor: default;
107
+ }
108
+
109
+ button.active {
110
+ box-shadow: 0 0 20px rgba(127, 61, 255, 0.5);
111
+ }
112
+
113
+ button.inactive {
114
+ background: transparent;
115
+ border: 1px solid var(--color-border);
116
+ color: var(--color-text);
117
+ }
118
+
119
+ button.inactive:hover {
120
+ background: rgba(127, 61, 255, 0.1);
121
+ border-color: var(--color-accent);
122
+ }
123
+ </style>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ currentPage: number;
3
+ totalPages: number;
4
+ maxVisible?: number;
5
+ onpagechange?: (page: number) => void;
6
+ }
7
+ declare const Pagination: import("svelte").Component<Props, {}, "currentPage">;
8
+ type Pagination = ReturnType<typeof Pagination>;
9
+ export default Pagination;
@@ -0,0 +1,103 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ label: string;
4
+ value: string | number;
5
+ change?: number;
6
+ trend?: 'up' | 'down' | 'neutral';
7
+ icon?: import('svelte').Snippet;
8
+ description?: string;
9
+ }
10
+
11
+ let { label, value, change, trend, icon, description }: Props = $props();
12
+
13
+ const trendColors = {
14
+ up: 'var(--color-success)',
15
+ down: 'var(--color-error)',
16
+ neutral: 'var(--color-text-muted)'
17
+ };
18
+
19
+ const trendColor = $derived(trend ? trendColors[trend] : 'var(--color-text-muted)');
20
+ const showGlow = $derived(trend === 'up');
21
+ </script>
22
+
23
+ <div class="glass-panel rounded-[var(--radius-xl)] p-6">
24
+ <!-- Top Row: Icon & Label -->
25
+ <div class="flex items-start justify-between mb-4">
26
+ <div class="flex-1">
27
+ <div
28
+ class="uppercase tracking-wider"
29
+ style="color: var(--color-text-soft); font-family: var(--font-body); font-size: 0.875rem; font-weight: 500;"
30
+ >
31
+ {label}
32
+ </div>
33
+ </div>
34
+
35
+ {#if icon}
36
+ <div class="w-10 h-10" style="color: var(--color-accent-soft);">
37
+ {@render icon()}
38
+ </div>
39
+ {/if}
40
+ </div>
41
+
42
+ <!-- Value Display -->
43
+ <div
44
+ class="mb-2"
45
+ class:text-glow={showGlow}
46
+ style="color: var(--color-text); font-family: var(--font-heading); font-size: 2.5rem; font-weight: 700; line-height: 1.2;"
47
+ >
48
+ {value}
49
+ </div>
50
+
51
+ <!-- Change Indicator & Description -->
52
+ {#if change !== undefined || description}
53
+ <div class="flex items-center gap-2 flex-wrap">
54
+ {#if change !== undefined}
55
+ <div
56
+ class="inline-flex items-center gap-1 font-semibold text-sm"
57
+ style="color: {trendColor};"
58
+ >
59
+ {#if trend === 'up'}
60
+ <svg
61
+ width="16"
62
+ height="16"
63
+ viewBox="0 0 16 16"
64
+ fill="none"
65
+ xmlns="http://www.w3.org/2000/svg"
66
+ >
67
+ <path
68
+ d="M8 12V4M8 4L4 8M8 4L12 8"
69
+ stroke="currentColor"
70
+ stroke-width="2"
71
+ stroke-linecap="round"
72
+ stroke-linejoin="round"
73
+ />
74
+ </svg>
75
+ {:else if trend === 'down'}
76
+ <svg
77
+ width="16"
78
+ height="16"
79
+ viewBox="0 0 16 16"
80
+ fill="none"
81
+ xmlns="http://www.w3.org/2000/svg"
82
+ >
83
+ <path
84
+ d="M8 4V12M8 12L4 8M8 12L12 8"
85
+ stroke="currentColor"
86
+ stroke-width="2"
87
+ stroke-linecap="round"
88
+ stroke-linejoin="round"
89
+ />
90
+ </svg>
91
+ {/if}
92
+ {change > 0 ? '+' : ''}{change}%
93
+ </div>
94
+ {/if}
95
+
96
+ {#if description}
97
+ <div class="text-xs" style="color: var(--color-text-muted);">
98
+ {description}
99
+ </div>
100
+ {/if}
101
+ </div>
102
+ {/if}
103
+ </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;
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ striped?: boolean;
4
+ hoverable?: boolean;
5
+ compact?: boolean;
6
+ children?: import('svelte').Snippet;
7
+ }
8
+
9
+ let { striped = false, hoverable = true, compact = false, children }: Props = $props();
10
+ </script>
11
+
12
+ <div class="glass-panel rounded-[var(--radius-xl)] overflow-hidden">
13
+ <table class="mrdv-table w-full" class:striped class:hoverable class:compact>
14
+ {@render children?.()}
15
+ </table>
16
+ </div>
17
+
18
+ <style>
19
+ .mrdv-table :global(thead) {
20
+ background: var(--color-base-2);
21
+ border-bottom: 2px solid var(--color-border-strong);
22
+ }
23
+
24
+ .mrdv-table :global(thead th) {
25
+ color: var(--color-text);
26
+ font-weight: 600;
27
+ text-transform: uppercase;
28
+ letter-spacing: 0.05em;
29
+ text-align: left;
30
+ }
31
+
32
+ .mrdv-table.compact :global(th),
33
+ .mrdv-table.compact :global(td) {
34
+ padding: 0.5rem 1rem;
35
+ }
36
+
37
+ .mrdv-table:not(.compact) :global(th),
38
+ .mrdv-table:not(.compact) :global(td) {
39
+ padding: 1rem 1.5rem;
40
+ }
41
+
42
+ .mrdv-table :global(tbody) {
43
+ position: relative;
44
+ z-index: 1;
45
+ }
46
+
47
+ .mrdv-table :global(tbody tr) {
48
+ border-bottom: 1px solid var(--color-border);
49
+ transition: all var(--ease-luxe);
50
+ }
51
+
52
+ .mrdv-table.hoverable :global(tbody tr:hover) {
53
+ background: rgba(127, 61, 255, 0.05);
54
+ transform: translateY(-1px);
55
+ box-shadow:
56
+ 0 2px 8px rgba(0, 0, 0, 0.2),
57
+ var(--shadow-accent-glow);
58
+ }
59
+
60
+ .mrdv-table.striped :global(tbody tr:nth-child(even)) {
61
+ background: rgba(255, 255, 255, 0.02);
62
+ }
63
+
64
+ .mrdv-table.striped.hoverable :global(tbody tr:nth-child(even):hover) {
65
+ background: rgba(127, 61, 255, 0.05);
66
+ transform: translateY(-1px);
67
+ box-shadow:
68
+ 0 2px 8px rgba(0, 0, 0, 0.2),
69
+ var(--shadow-accent-glow);
70
+ }
71
+
72
+ .mrdv-table :global(th),
73
+ .mrdv-table :global(td) {
74
+ text-align: left;
75
+ }
76
+ </style>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ striped?: boolean;
3
+ hoverable?: boolean;
4
+ compact?: boolean;
5
+ children?: import('svelte').Snippet;
6
+ }
7
+ declare const Table: import("svelte").Component<Props, {}, "">;
8
+ type Table = ReturnType<typeof Table>;
9
+ export default Table;