@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,125 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { open = $bindable(false), title, size = 'md', closeOnBackdrop = true, closeOnEscape = true, children, footer, iconBefore, class: className = '' } = $props();
3
+ let modalElement = $state();
4
+ let previousFocus = $state(null);
5
+ const sizeClasses = {
6
+ sm: 'max-w-[28rem]',
7
+ md: 'max-w-[36rem]',
8
+ lg: 'max-w-[48rem]',
9
+ xl: 'max-w-[64rem]'
10
+ };
11
+ const titleId = createId('modal-title');
12
+ const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable="true"], summary, details, audio[controls], video[controls]';
13
+ function handleClose() {
14
+ open = false;
15
+ }
16
+ function handleBackdropClick(event) {
17
+ if (closeOnBackdrop && event.target === event.currentTarget) {
18
+ handleClose();
19
+ }
20
+ }
21
+ function handleEscape(event) {
22
+ if (closeOnEscape && event.key === 'Escape') {
23
+ handleClose();
24
+ }
25
+ }
26
+ function handleFocusTrap(event) {
27
+ if (event.key !== 'Tab' || !modalElement)
28
+ return;
29
+ const focusableElements = Array.from(modalElement.querySelectorAll(focusableSelector));
30
+ if (focusableElements.length === 0)
31
+ return;
32
+ const firstFocusable = focusableElements[0];
33
+ const lastFocusable = focusableElements[focusableElements.length - 1];
34
+ if (event.shiftKey) {
35
+ // Shift+Tab: moving backwards
36
+ if (document.activeElement === firstFocusable) {
37
+ event.preventDefault();
38
+ lastFocusable.focus();
39
+ }
40
+ }
41
+ else {
42
+ // Tab: moving forwards
43
+ if (document.activeElement === lastFocusable) {
44
+ event.preventDefault();
45
+ firstFocusable.focus();
46
+ }
47
+ }
48
+ }
49
+ $effect(() => {
50
+ if (typeof document === 'undefined')
51
+ return;
52
+ if (open) {
53
+ // Store previous focus
54
+ previousFocus = document.activeElement;
55
+ // Prevent body scroll
56
+ document.body.style.overflow = 'hidden';
57
+ // Add event listeners
58
+ document.addEventListener('keydown', handleEscape);
59
+ document.addEventListener('keydown', handleFocusTrap);
60
+ // Focus first interactive element
61
+ requestAnimationFrame(() => {
62
+ const firstInteractive = modalElement?.querySelector(focusableSelector);
63
+ firstInteractive?.focus();
64
+ });
65
+ return () => {
66
+ // Restore body scroll
67
+ document.body.style.overflow = '';
68
+ // Remove event listeners
69
+ document.removeEventListener('keydown', handleEscape);
70
+ document.removeEventListener('keydown', handleFocusTrap);
71
+ // Restore previous focus
72
+ previousFocus?.focus();
73
+ };
74
+ }
75
+ });
76
+ </script>
77
+
78
+ {#if open}
79
+ <div
80
+ class="fixed inset-0 z-[var(--z-50)] flex items-center justify-center p-4 bg-overlay backdrop-blur-md animate-[fade-in_0.2s_var(--ease-luxe)]"
81
+ onclick={handleBackdropClick}
82
+ role="presentation"
83
+ >
84
+ <div
85
+ bind:this={modalElement}
86
+ class="panel-floating rounded-[var(--radius-lg)] shadow-deep w-full {sizeClasses[size]} {className} animate-[fade-up_0.3s_var(--ease-luxe)] flex flex-col max-h-[90vh]"
87
+ role="dialog"
88
+ aria-modal="true"
89
+ aria-labelledby={title ? titleId : undefined}
90
+ >
91
+ {#if title}
92
+ <div class="flex items-center justify-between p-8 border-b border-border">
93
+ <h2 id={titleId} class="text-xl font-semibold text-text flex items-center gap-3">
94
+ {#if iconBefore}
95
+ <span class="inline-flex items-center justify-center">
96
+ {@render iconBefore()}
97
+ </span>
98
+ {/if}
99
+ {title}
100
+ </h2>
101
+ <button
102
+ type="button"
103
+ onclick={handleClose}
104
+ class="text-text-soft hover:text-text transition-colors"
105
+ aria-label="Close modal"
106
+ >
107
+ <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
108
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
109
+ </svg>
110
+ </button>
111
+ </div>
112
+ {/if}
113
+
114
+ <div class="flex-1 overflow-y-auto p-8 max-h-[70vh]" style="scrollbar-width: thin; scrollbar-color: var(--color-accent) var(--color-base-3);">
115
+ {@render children?.()}
116
+ </div>
117
+
118
+ {#if footer}
119
+ <div class="flex items-center justify-end gap-4 p-8 border-t border-border">
120
+ {@render footer?.()}
121
+ </div>
122
+ {/if}
123
+ </div>
124
+ </div>
125
+ {/if}
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface Props {
3
+ open?: boolean;
4
+ title?: string;
5
+ size?: 'sm' | 'md' | 'lg' | 'xl';
6
+ closeOnBackdrop?: boolean;
7
+ closeOnEscape?: boolean;
8
+ children?: Snippet;
9
+ footer?: Snippet;
10
+ iconBefore?: Snippet;
11
+ class?: string;
12
+ }
13
+ declare const Modal: import("svelte").Component<Props, {}, "open">;
14
+ type Modal = ReturnType<typeof Modal>;
15
+ export default Modal;
@@ -0,0 +1,106 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { open = $bindable(false), placement = 'bottom', closeOnClickOutside = true, trigger, children } = $props();
3
+ let popoverElement = $state();
4
+ let wrapperElement = $state();
5
+ let actualPlacement = $state(placement);
6
+ const popoverId = createId('popover');
7
+ const placementClasses = {
8
+ 'top': 'bottom-full left-1/2 -translate-x-1/2 mb-2',
9
+ 'top-start': 'bottom-full left-0 mb-2',
10
+ 'top-end': 'bottom-full right-0 mb-2',
11
+ 'bottom': 'top-full left-1/2 -translate-x-1/2 mt-2',
12
+ 'bottom-start': 'top-full left-0 mt-2',
13
+ 'bottom-end': 'top-full right-0 mt-2',
14
+ 'left': 'right-full top-1/2 -translate-y-1/2 mr-2',
15
+ 'right': 'left-full top-1/2 -translate-y-1/2 ml-2'
16
+ };
17
+ const oppositePlacement = {
18
+ 'top': 'bottom',
19
+ 'top-start': 'bottom-start',
20
+ 'top-end': 'bottom-end',
21
+ 'bottom': 'top',
22
+ 'bottom-start': 'top-start',
23
+ 'bottom-end': 'top-end',
24
+ 'left': 'right',
25
+ 'right': 'left'
26
+ };
27
+ function toggleOpen() {
28
+ open = !open;
29
+ }
30
+ function handleClickOutside(event) {
31
+ if (closeOnClickOutside && popoverElement && wrapperElement && !wrapperElement.contains(event.target)) {
32
+ open = false;
33
+ }
34
+ }
35
+ function handleEscape(event) {
36
+ if (event.key === 'Escape') {
37
+ open = false;
38
+ }
39
+ }
40
+ // Reset actualPlacement when visibility changes
41
+ $effect(() => {
42
+ if (!open) {
43
+ actualPlacement = placement;
44
+ }
45
+ });
46
+ // Check viewport bounds and flip placement if needed
47
+ $effect(() => {
48
+ if (typeof window === 'undefined')
49
+ return;
50
+ if (open && popoverElement) {
51
+ const rect = popoverElement.getBoundingClientRect();
52
+ const viewportWidth = window.innerWidth;
53
+ const viewportHeight = window.innerHeight;
54
+ // Determine if current placement overflows and flip if needed
55
+ if (actualPlacement.startsWith('top') && rect.top < 0) {
56
+ actualPlacement = oppositePlacement[actualPlacement] || placement;
57
+ }
58
+ else if (actualPlacement.startsWith('bottom') && rect.bottom > viewportHeight) {
59
+ actualPlacement = oppositePlacement[actualPlacement] || placement;
60
+ }
61
+ else if (actualPlacement === 'left' && rect.left < 0) {
62
+ actualPlacement = 'right';
63
+ }
64
+ else if (actualPlacement === 'right' && rect.right > viewportWidth) {
65
+ actualPlacement = 'left';
66
+ }
67
+ }
68
+ });
69
+ $effect(() => {
70
+ if (typeof document === 'undefined')
71
+ return;
72
+ if (open) {
73
+ document.addEventListener('click', handleClickOutside);
74
+ document.addEventListener('keydown', handleEscape);
75
+ return () => {
76
+ document.removeEventListener('click', handleClickOutside);
77
+ document.removeEventListener('keydown', handleEscape);
78
+ };
79
+ }
80
+ });
81
+ </script>
82
+
83
+ <div bind:this={wrapperElement} class="relative inline-block">
84
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
85
+ <div
86
+ onclick={toggleOpen}
87
+ onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleOpen(); } }}
88
+ aria-haspopup="true"
89
+ aria-expanded={open}
90
+ aria-controls={open ? popoverId : undefined}
91
+ class="inline-block"
92
+ >
93
+ {@render trigger?.()}
94
+ </div>
95
+
96
+ {#if open}
97
+ <div
98
+ bind:this={popoverElement}
99
+ id={popoverId}
100
+ class="absolute {placementClasses[actualPlacement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-lg)] shadow-deep p-4 animate-[fade-up_0.2s_var(--ease-luxe)]"
101
+ role="dialog"
102
+ >
103
+ {@render children?.()}
104
+ </div>
105
+ {/if}
106
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ open?: boolean;
4
+ placement?: 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'right';
5
+ closeOnClickOutside?: boolean;
6
+ trigger?: Snippet;
7
+ children?: Snippet;
8
+ }
9
+ declare const Popover: import("svelte").Component<Props, {}, "open">;
10
+ type Popover = ReturnType<typeof Popover>;
11
+ export default Popover;
@@ -0,0 +1,29 @@
1
+ <script lang="ts">"use strict";
2
+ let { value, max = 100, size = 'md', showLabel = false, label } = $props();
3
+ const percentage = $derived(Math.min(100, Math.max(0, (value / max) * 100)));
4
+ const heightClasses = {
5
+ sm: 'h-2',
6
+ md: 'h-3',
7
+ lg: 'h-4'
8
+ };
9
+ </script>
10
+
11
+ <div
12
+ class="relative bg-[var(--color-base-3)] border border-[var(--color-border)] rounded-[var(--radius-pill)] overflow-hidden shadow-[var(--shadow-deep)] {heightClasses[size]}"
13
+ role="progressbar"
14
+ aria-valuenow={value}
15
+ aria-valuemin="0"
16
+ aria-valuemax={max}
17
+ aria-label={label || `${percentage.toFixed(0)}% complete`}
18
+ >
19
+ <div
20
+ class="h-full bg-[var(--color-accent)] accent-glow transition-all duration-500 ease-[var(--ease-luxe)]"
21
+ style="width: {percentage}%"
22
+ />
23
+
24
+ {#if showLabel}
25
+ <div class="absolute inset-0 flex items-center justify-center text-xs font-semibold text-[var(--color-text)] text-glow">
26
+ {label || `${percentage.toFixed(0)}%`}
27
+ </div>
28
+ {/if}
29
+ </div>
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ value: number;
3
+ max?: number;
4
+ size?: 'sm' | 'md' | 'lg';
5
+ showLabel?: boolean;
6
+ label?: string;
7
+ }
8
+ declare const ProgressBar: import("svelte").Component<Props, {}, "">;
9
+ type ProgressBar = ReturnType<typeof ProgressBar>;
10
+ export default ProgressBar;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">"use strict";
2
+ let { variant = 'rectangular', width, height, count = 1 } = $props();
3
+ const defaultDimensions = {
4
+ text: { width: '100%', height: '1rem' },
5
+ circular: { width: '3rem', height: '3rem' },
6
+ rectangular: { width: '100%', height: '8rem' }
7
+ };
8
+ const roundingClasses = {
9
+ text: 'rounded-[var(--radius-sm)]',
10
+ circular: 'rounded-[var(--radius-pill)]',
11
+ rectangular: 'rounded-[var(--radius-lg)]'
12
+ };
13
+ const finalWidth = width || defaultDimensions[variant].width;
14
+ const finalHeight = height || defaultDimensions[variant].height;
15
+ </script>
16
+
17
+ <div
18
+ aria-busy="true"
19
+ aria-live="polite"
20
+ aria-label="Loading content"
21
+ >
22
+ {#if variant === 'text'}
23
+ {#each Array(count) as _, i}
24
+ <div
25
+ class="bg-[var(--color-base-3)] border border-[var(--color-border)] {roundingClasses[variant]} mb-2 skeleton-shimmer"
26
+ style="width: {i === count - 1 ? '80%' : finalWidth}; height: {finalHeight};"
27
+ />
28
+ {/each}
29
+ {:else}
30
+ <div
31
+ class="bg-[var(--color-base-3)] border border-[var(--color-border)] {roundingClasses[variant]} skeleton-shimmer"
32
+ style="width: {finalWidth}; height: {finalHeight};"
33
+ />
34
+ {/if}
35
+ </div>
36
+
37
+ <style>
38
+ .skeleton-shimmer {
39
+ position: relative;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .skeleton-shimmer::before {
44
+ content: '';
45
+ position: absolute;
46
+ top: 0;
47
+ left: 0;
48
+ width: 200%;
49
+ height: 100%;
50
+ background: linear-gradient(
51
+ 90deg,
52
+ transparent,
53
+ var(--color-accent-overlay-10),
54
+ transparent
55
+ );
56
+ animation: shimmer 1.5s ease-in-out infinite;
57
+ }
58
+
59
+ @keyframes shimmer {
60
+ 0% {
61
+ transform: translateX(-100%);
62
+ }
63
+ 100% {
64
+ transform: translateX(100%);
65
+ }
66
+ }</style>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ variant?: 'text' | 'circular' | 'rectangular';
3
+ width?: string;
4
+ height?: string;
5
+ count?: number;
6
+ }
7
+ declare const SkeletonLoader: import("svelte").Component<Props, {}, "">;
8
+ type SkeletonLoader = ReturnType<typeof SkeletonLoader>;
9
+ export default SkeletonLoader;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">"use strict";
2
+ let { size = 'md', label } = $props();
3
+ const sizeClasses = {
4
+ sm: 'w-4 h-4 border-2',
5
+ md: 'w-8 h-8 border-4',
6
+ lg: 'w-12 h-12 border-4'
7
+ };
8
+ </script>
9
+
10
+ <div
11
+ class="inline-flex items-center justify-center"
12
+ role="status"
13
+ aria-live="polite"
14
+ aria-label={label || 'Loading'}
15
+ >
16
+ <div
17
+ class="{sizeClasses[size]} border-[var(--color-base-3)] border-t-[var(--color-accent)] rounded-[var(--radius-pill)] accent-glow animate-spin"
18
+ />
19
+ <span class="sr-only">{label || 'Loading'}</span>
20
+ </div>
21
+
22
+ <style>
23
+ .sr-only {
24
+ position: absolute;
25
+ width: 1px;
26
+ height: 1px;
27
+ padding: 0;
28
+ margin: -1px;
29
+ overflow: hidden;
30
+ clip: rect(0, 0, 0, 0);
31
+ white-space: nowrap;
32
+ border-width: 0;
33
+ }</style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ size?: 'sm' | 'md' | 'lg';
3
+ label?: string;
4
+ }
5
+ declare const Spinner: import("svelte").Component<Props, {}, "">;
6
+ type Spinner = ReturnType<typeof Spinner>;
7
+ export default Spinner;
@@ -0,0 +1,111 @@
1
+ <script lang="ts">"use strict";
2
+ let { message, variant = 'info', duration = 3000, position = 'bottom-right', dismissible = true, onClose, onclose, index = 0, icon } = $props();
3
+ const onCloseCallback = $derived(onClose ?? onclose);
4
+ let visible = $state(true);
5
+ let isExiting = $state(false);
6
+ const positionClasses = {
7
+ 'top-left': 'top-4 left-4',
8
+ 'top-center': 'top-4 left-1/2 -translate-x-1/2',
9
+ 'top-right': 'top-4 right-4',
10
+ 'bottom-left': 'bottom-4 left-4',
11
+ 'bottom-center': 'bottom-4 left-1/2 -translate-x-1/2',
12
+ 'bottom-right': 'bottom-4 right-4'
13
+ };
14
+ const variantBorderClasses = {
15
+ info: 'border-l-4 border-[var(--color-accent)]',
16
+ success: 'border-l-4 border-[var(--color-success)]',
17
+ warning: 'border-l-4 border-[var(--color-warning)]',
18
+ error: 'border-l-4 border-[var(--color-error)]'
19
+ };
20
+ const variantIconClasses = {
21
+ info: 'text-[var(--color-accent-soft)]',
22
+ success: 'text-[var(--color-success)]',
23
+ warning: 'text-[var(--color-warning)]',
24
+ error: 'text-[var(--color-error)]'
25
+ };
26
+ const variantIcons = {
27
+ info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
28
+ success: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
29
+ warning: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',
30
+ error: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z'
31
+ };
32
+ const roleMap = {
33
+ info: 'status',
34
+ success: 'status',
35
+ warning: 'alert',
36
+ error: 'alert'
37
+ };
38
+ const ariaLiveMap = {
39
+ info: 'polite',
40
+ success: 'polite',
41
+ warning: 'polite',
42
+ error: 'assertive'
43
+ };
44
+ const animationClasses = $derived(position.startsWith('bottom')
45
+ ? 'animate-[fade-up_0.3s_var(--ease-luxe)]'
46
+ : 'animate-[fade-in_0.3s_var(--ease-luxe)]');
47
+ const stackOffset = $derived(position.startsWith('bottom')
48
+ ? `translateY(-${index * 5}rem)`
49
+ : `translateY(${index * 5}rem)`);
50
+ function handleClose() {
51
+ if (isExiting || !visible)
52
+ return; // Prevent double-close
53
+ isExiting = true;
54
+ setTimeout(() => {
55
+ visible = false;
56
+ onCloseCallback?.();
57
+ }, 200); // Match fade-out animation duration
58
+ }
59
+ $effect(() => {
60
+ // Only auto-dismiss if toast is visible and duration is set
61
+ if (visible && !isExiting && duration > 0) {
62
+ const timer = setTimeout(() => {
63
+ handleClose();
64
+ }, duration);
65
+ return () => {
66
+ clearTimeout(timer);
67
+ };
68
+ }
69
+ });
70
+ </script>
71
+
72
+ {#if visible}
73
+ <div
74
+ class="fixed z-[100] {positionClasses[position]} {isExiting ? 'animate-[fade-out_0.2s_var(--ease-sharp)]' : animationClasses}"
75
+ style="transform: {stackOffset}"
76
+ >
77
+ <div
78
+ class="panel-floating rounded-[var(--radius-lg)] p-4 min-w-[20rem] max-w-md flex gap-3 items-start {variantBorderClasses[variant]}"
79
+ role={roleMap[variant]}
80
+ aria-live={ariaLiveMap[variant]}
81
+ aria-atomic="true"
82
+ >
83
+ <div class="flex-shrink-0 {variantIconClasses[variant]}">
84
+ {#if icon}
85
+ {@render icon()}
86
+ {:else}
87
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={variantIcons[variant]} />
89
+ </svg>
90
+ {/if}
91
+ </div>
92
+
93
+ <div class="flex-1 text-sm text-[var(--color-text)]">
94
+ {message}
95
+ </div>
96
+
97
+ {#if dismissible}
98
+ <button
99
+ type="button"
100
+ onclick={handleClose}
101
+ class="flex-shrink-0 text-[var(--color-text-soft)] hover:text-[var(--color-text)] transition-colors"
102
+ aria-label="Dismiss notification"
103
+ >
104
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
105
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
106
+ </svg>
107
+ </button>
108
+ {/if}
109
+ </div>
110
+ </div>
111
+ {/if}
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ message: string;
3
+ variant?: 'info' | 'success' | 'warning' | 'error';
4
+ duration?: number;
5
+ position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
6
+ dismissible?: boolean;
7
+ onClose?: () => void;
8
+ /** @deprecated Use onClose */
9
+ onclose?: () => void;
10
+ /** Index in toast stack for vertical offset. Pass the toast's position in a list to enable stacking. */
11
+ index?: number;
12
+ icon?: import('svelte').Snippet;
13
+ }
14
+ declare const Toast: import("svelte").Component<Props, {}, "">;
15
+ type Toast = ReturnType<typeof Toast>;
16
+ export default Toast;
@@ -0,0 +1,94 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { content, placement = 'top', delay = 200, trigger, children, class: className = '' } = $props();
3
+ // Use children as fallback if trigger not provided
4
+ const renderedTrigger = $derived(trigger ?? children);
5
+ let visible = $state(false);
6
+ let actualPlacement = $state(placement);
7
+ let tooltipElement = $state();
8
+ let timeoutId;
9
+ const tooltipId = createId('tooltip');
10
+ const placementClasses = {
11
+ top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
12
+ bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
13
+ left: 'right-full top-1/2 -translate-y-1/2 mr-2',
14
+ right: 'left-full top-1/2 -translate-y-1/2 ml-2'
15
+ };
16
+ const oppositePlacement = {
17
+ top: 'bottom',
18
+ bottom: 'top',
19
+ left: 'right',
20
+ right: 'left'
21
+ };
22
+ function handleMouseEnter() {
23
+ if (typeof window === 'undefined')
24
+ return;
25
+ timeoutId = window.setTimeout(() => {
26
+ visible = true;
27
+ }, delay);
28
+ }
29
+ function handleMouseLeave() {
30
+ if (timeoutId) {
31
+ clearTimeout(timeoutId);
32
+ }
33
+ visible = false;
34
+ }
35
+ function handleFocus() {
36
+ handleMouseEnter();
37
+ }
38
+ function handleBlur() {
39
+ handleMouseLeave();
40
+ }
41
+ // Reset actualPlacement when visibility changes
42
+ $effect(() => {
43
+ if (!visible) {
44
+ actualPlacement = placement;
45
+ }
46
+ });
47
+ // Check viewport bounds and flip placement if needed
48
+ $effect(() => {
49
+ if (typeof window === 'undefined')
50
+ return;
51
+ if (visible && tooltipElement) {
52
+ const rect = tooltipElement.getBoundingClientRect();
53
+ const viewportWidth = window.innerWidth;
54
+ const viewportHeight = window.innerHeight;
55
+ // Determine if current placement overflows and flip if needed
56
+ if (actualPlacement === 'top' && rect.top < 0 && actualPlacement !== oppositePlacement[placement]) {
57
+ actualPlacement = 'bottom';
58
+ }
59
+ else if (actualPlacement === 'bottom' && rect.bottom > viewportHeight && actualPlacement !== oppositePlacement[placement]) {
60
+ actualPlacement = 'top';
61
+ }
62
+ else if (actualPlacement === 'left' && rect.left < 0 && actualPlacement !== oppositePlacement[placement]) {
63
+ actualPlacement = 'right';
64
+ }
65
+ else if (actualPlacement === 'right' && rect.right > viewportWidth && actualPlacement !== oppositePlacement[placement]) {
66
+ actualPlacement = 'left';
67
+ }
68
+ }
69
+ });
70
+ </script>
71
+
72
+ <div class="relative inline-block">
73
+ <div
74
+ aria-describedby={tooltipId}
75
+ onmouseenter={handleMouseEnter}
76
+ onmouseleave={handleMouseLeave}
77
+ onfocus={handleFocus}
78
+ onblur={handleBlur}
79
+ role="presentation"
80
+ class="contents"
81
+ >
82
+ {@render renderedTrigger?.()}
83
+ </div>
84
+
85
+ <div
86
+ bind:this={tooltipElement}
87
+ id={tooltipId}
88
+ role="tooltip"
89
+ aria-hidden={visible ? 'false' : 'true'}
90
+ class="absolute {placementClasses[actualPlacement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-md)] px-4 py-2.5 text-sm text-text whitespace-nowrap pointer-events-none {className} {visible ? 'animate-[fade-in_0.15s_var(--ease-luxe)] opacity-100' : 'opacity-0 invisible'}"
91
+ >
92
+ {content}
93
+ </div>
94
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface Props {
3
+ content: string;
4
+ placement?: 'top' | 'bottom' | 'left' | 'right';
5
+ delay?: number;
6
+ trigger?: Snippet;
7
+ children?: Snippet;
8
+ class?: string;
9
+ }
10
+ declare const Tooltip: import("svelte").Component<Props, {}, "">;
11
+ type Tooltip = ReturnType<typeof Tooltip>;
12
+ export default Tooltip;
@@ -0,0 +1,11 @@
1
+ export { default as Modal } from './Modal.svelte';
2
+ export { default as Alert } from './Alert.svelte';
3
+ export { default as Spinner } from './Spinner.svelte';
4
+ export { default as Tooltip } from './Tooltip.svelte';
5
+ export { default as ProgressBar } from './ProgressBar.svelte';
6
+ export { default as SkeletonLoader } from './SkeletonLoader.svelte';
7
+ export { default as Toast } from './Toast.svelte';
8
+ export { default as Drawer } from './Drawer.svelte';
9
+ export { default as Popover } from './Popover.svelte';
10
+ export { default as Dropdown } from './Dropdown.svelte';
11
+ export { default as CommandPalette } from './CommandPalette.svelte';