@mrintel/villain-ui 0.3.0 → 0.7.1

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 (140) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3490 -1296
  3. package/dist/components/buttons/Button.svelte +27 -33
  4. package/dist/components/buttons/Button.svelte.d.ts +4 -1
  5. package/dist/components/buttons/ButtonGroup.svelte +17 -30
  6. package/dist/components/buttons/FloatingActionButton.svelte +20 -44
  7. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +2 -1
  8. package/dist/components/buttons/IconButton.svelte +23 -53
  9. package/dist/components/buttons/IconButton.svelte.d.ts +2 -1
  10. package/dist/components/buttons/LinkButton.svelte +24 -37
  11. package/dist/components/buttons/LinkButton.svelte.d.ts +4 -1
  12. package/dist/components/buttons/buttonClasses.d.ts +5 -0
  13. package/dist/components/buttons/buttonClasses.js +8 -3
  14. package/dist/components/cards/Card.svelte +54 -46
  15. package/dist/components/cards/Card.svelte.d.ts +9 -2
  16. package/dist/components/cards/Container.svelte +17 -33
  17. package/dist/components/cards/Divider.svelte +36 -52
  18. package/dist/components/cards/Divider.svelte.d.ts +2 -0
  19. package/dist/components/cards/Grid.svelte +55 -44
  20. package/dist/components/cards/Panel.svelte +18 -32
  21. package/dist/components/cards/Panel.svelte.d.ts +2 -1
  22. package/dist/components/cards/SectionHeader.svelte +24 -38
  23. package/dist/components/cards/SectionHeader.svelte.d.ts +1 -0
  24. package/dist/components/data/Avatar.svelte +48 -67
  25. package/dist/components/data/Badge.svelte +45 -32
  26. package/dist/components/data/Badge.svelte.d.ts +7 -1
  27. package/dist/components/data/CalendarGrid.svelte +433 -0
  28. package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
  29. package/dist/components/data/CalendarGrid.types.d.ts +7 -0
  30. package/dist/components/data/CalendarGrid.types.js +1 -0
  31. package/dist/components/data/CodeBlock.svelte +119 -121
  32. package/dist/components/data/CodeBlock.svelte.d.ts +8 -0
  33. package/dist/components/data/List.svelte +87 -64
  34. package/dist/components/data/List.svelte.d.ts +7 -0
  35. package/dist/components/data/Pagination.svelte +121 -123
  36. package/dist/components/data/Pagination.svelte.d.ts +5 -0
  37. package/dist/components/data/Sparkline.svelte +117 -0
  38. package/dist/components/data/Sparkline.svelte.d.ts +43 -0
  39. package/dist/components/data/Stat.svelte +92 -103
  40. package/dist/components/data/Table.svelte +443 -76
  41. package/dist/components/data/Table.svelte.d.ts +23 -2
  42. package/dist/components/data/Table.types.d.ts +14 -0
  43. package/dist/components/data/Table.types.js +1 -0
  44. package/dist/components/data/Tag.svelte +51 -53
  45. package/dist/components/data/Tag.svelte.d.ts +5 -1
  46. package/dist/components/data/index.d.ts +4 -0
  47. package/dist/components/data/index.js +2 -0
  48. package/dist/components/forms/Checkbox.svelte +39 -51
  49. package/dist/components/forms/Checkbox.svelte.d.ts +3 -1
  50. package/dist/components/forms/DatePicker.svelte +61 -0
  51. package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
  52. package/dist/components/forms/DateTimePicker.svelte +63 -0
  53. package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
  54. package/dist/components/forms/FileUpload.svelte +136 -164
  55. package/dist/components/forms/FileUpload.svelte.d.ts +1 -0
  56. package/dist/components/forms/Input.svelte +284 -57
  57. package/dist/components/forms/Input.svelte.d.ts +10 -3
  58. package/dist/components/forms/InputGroup.svelte +7 -7
  59. package/dist/components/forms/RadioGroup.svelte +77 -87
  60. package/dist/components/forms/RadioGroup.svelte.d.ts +3 -1
  61. package/dist/components/forms/RangeSlider.svelte +90 -116
  62. package/dist/components/forms/Select.svelte +106 -71
  63. package/dist/components/forms/Select.svelte.d.ts +3 -1
  64. package/dist/components/forms/Step.svelte +25 -0
  65. package/dist/components/forms/Step.svelte.d.ts +12 -0
  66. package/dist/components/forms/StepContext.d.ts +3 -0
  67. package/dist/components/forms/StepContext.js +5 -0
  68. package/dist/components/forms/Stepper.types.d.ts +37 -0
  69. package/dist/components/forms/Stepper.types.js +1 -0
  70. package/dist/components/forms/StepperForm.svelte +183 -0
  71. package/dist/components/forms/StepperForm.svelte.d.ts +17 -0
  72. package/dist/components/forms/Switch.svelte +44 -56
  73. package/dist/components/forms/Switch.svelte.d.ts +3 -1
  74. package/dist/components/forms/Textarea.svelte +52 -57
  75. package/dist/components/forms/Textarea.svelte.d.ts +3 -1
  76. package/dist/components/forms/TimePicker.svelte +63 -0
  77. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  78. package/dist/components/forms/formClasses.d.ts +3 -0
  79. package/dist/components/forms/formClasses.js +3 -0
  80. package/dist/components/forms/index.d.ts +6 -0
  81. package/dist/components/forms/index.js +5 -0
  82. package/dist/components/navigation/Breadcrumbs.svelte +56 -59
  83. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +1 -0
  84. package/dist/components/navigation/ContextMenu.svelte +133 -83
  85. package/dist/components/navigation/ContextMenu.svelte.d.ts +8 -1
  86. package/dist/components/navigation/DropdownMenu.svelte +139 -80
  87. package/dist/components/navigation/DropdownMenu.svelte.d.ts +8 -1
  88. package/dist/components/navigation/Menu.svelte +72 -48
  89. package/dist/components/navigation/Navbar.svelte +111 -32
  90. package/dist/components/navigation/Navbar.svelte.d.ts +6 -0
  91. package/dist/components/navigation/Sidebar.svelte +236 -35
  92. package/dist/components/navigation/Sidebar.svelte.d.ts +2 -0
  93. package/dist/components/navigation/Stepper.svelte +204 -0
  94. package/dist/components/navigation/Stepper.svelte.d.ts +34 -0
  95. package/dist/components/navigation/Tabs.svelte +86 -54
  96. package/dist/components/navigation/Tabs.svelte.d.ts +5 -1
  97. package/dist/components/navigation/index.d.ts +1 -0
  98. package/dist/components/navigation/index.js +1 -0
  99. package/dist/components/overlays/Alert.svelte +81 -99
  100. package/dist/components/overlays/Alert.svelte.d.ts +5 -1
  101. package/dist/components/overlays/CommandPalette.svelte +182 -217
  102. package/dist/components/overlays/Drawer.svelte +158 -167
  103. package/dist/components/overlays/Drawer.svelte.d.ts +3 -1
  104. package/dist/components/overlays/Dropdown.svelte +62 -30
  105. package/dist/components/overlays/Dropdown.svelte.d.ts +2 -0
  106. package/dist/components/overlays/Modal.svelte +125 -130
  107. package/dist/components/overlays/Modal.svelte.d.ts +3 -1
  108. package/dist/components/overlays/Popover.svelte +106 -131
  109. package/dist/components/overlays/ProgressBar.svelte +29 -45
  110. package/dist/components/overlays/SkeletonLoader.svelte +66 -82
  111. package/dist/components/overlays/Spinner.svelte +33 -43
  112. package/dist/components/overlays/Toast.svelte +111 -140
  113. package/dist/components/overlays/Toast.svelte.d.ts +3 -0
  114. package/dist/components/overlays/Tooltip.svelte +94 -115
  115. package/dist/components/overlays/Tooltip.svelte.d.ts +3 -1
  116. package/dist/components/typography/Code.svelte +10 -14
  117. package/dist/components/typography/Heading.svelte +15 -22
  118. package/dist/components/typography/Heading.svelte.d.ts +1 -0
  119. package/dist/components/typography/Text.svelte +21 -24
  120. package/dist/components/typography/Text.svelte.d.ts +2 -1
  121. package/dist/components/utilities/Accordion.svelte +54 -67
  122. package/dist/components/utilities/Accordion.svelte.d.ts +4 -1
  123. package/dist/components/utilities/Carousel.svelte +124 -152
  124. package/dist/components/utilities/Collapse.svelte +46 -60
  125. package/dist/components/utilities/Hero.svelte +42 -0
  126. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  127. package/dist/components/utilities/Portal.svelte +47 -72
  128. package/dist/components/utilities/ScrollArea.svelte +33 -41
  129. package/dist/components/utilities/SystemConsole.svelte +310 -0
  130. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  131. package/dist/components/utilities/SystemInterface.svelte +726 -0
  132. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  133. package/dist/components/utilities/index.d.ts +4 -0
  134. package/dist/components/utilities/index.js +3 -0
  135. package/dist/components/utilities/utilities.types.d.ts +46 -0
  136. package/dist/components/utilities/utilities.types.js +4 -0
  137. package/dist/index.d.ts +57 -5
  138. package/dist/index.js +5 -5
  139. package/dist/theme.css +2889 -218
  140. package/package.json +83 -76
@@ -1,82 +1,66 @@
1
- <script lang="ts">
2
- interface Props {
3
- variant?: 'text' | 'circular' | 'rectangular';
4
- width?: string;
5
- height?: string;
6
- count?: number;
7
- }
8
-
9
- let {
10
- variant = 'rectangular',
11
- width,
12
- height,
13
- count = 1
14
- }: Props = $props();
15
-
16
- const defaultDimensions = {
17
- text: { width: '100%', height: '1rem' },
18
- circular: { width: '3rem', height: '3rem' },
19
- rectangular: { width: '100%', height: '8rem' }
20
- };
21
-
22
- const roundingClasses = {
23
- text: 'rounded-[var(--radius-sm)]',
24
- circular: 'rounded-[var(--radius-pill)]',
25
- rectangular: 'rounded-[var(--radius-lg)]'
26
- };
27
-
28
- const finalWidth = width || defaultDimensions[variant].width;
29
- const finalHeight = height || defaultDimensions[variant].height;
30
- </script>
31
-
32
- <div
33
- aria-busy="true"
34
- aria-live="polite"
35
- aria-label="Loading content"
36
- >
37
- {#if variant === 'text'}
38
- {#each Array(count) as _, i}
39
- <div
40
- class="bg-[var(--color-base-3)] border border-[var(--color-border)] {roundingClasses[variant]} mb-2 skeleton-shimmer"
41
- style="width: {i === count - 1 ? '80%' : finalWidth}; height: {finalHeight};"
42
- />
43
- {/each}
44
- {:else}
45
- <div
46
- class="bg-[var(--color-base-3)] border border-[var(--color-border)] {roundingClasses[variant]} skeleton-shimmer"
47
- style="width: {finalWidth}; height: {finalHeight};"
48
- />
49
- {/if}
50
- </div>
51
-
52
- <style>
53
- .skeleton-shimmer {
54
- position: relative;
55
- overflow: hidden;
56
- }
57
-
58
- .skeleton-shimmer::before {
59
- content: '';
60
- position: absolute;
61
- top: 0;
62
- left: 0;
63
- width: 200%;
64
- height: 100%;
65
- background: linear-gradient(
66
- 90deg,
67
- transparent,
68
- rgba(127, 61, 255, 0.1),
69
- transparent
70
- );
71
- animation: shimmer 1.5s ease-in-out infinite;
72
- }
73
-
74
- @keyframes shimmer {
75
- 0% {
76
- transform: translateX(-100%);
77
- }
78
- 100% {
79
- transform: translateX(100%);
80
- }
81
- }
82
- </style>
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>
@@ -1,43 +1,33 @@
1
- <script lang="ts">
2
- interface Props {
3
- size?: 'sm' | 'md' | 'lg';
4
- label?: string;
5
- }
6
-
7
- let {
8
- size = 'md',
9
- label
10
- }: Props = $props();
11
-
12
- const sizeClasses = {
13
- sm: 'w-4 h-4 border-2',
14
- md: 'w-8 h-8 border-4',
15
- lg: 'w-12 h-12 border-4'
16
- };
17
- </script>
18
-
19
- <div
20
- class="inline-flex items-center justify-center"
21
- role="status"
22
- aria-live="polite"
23
- aria-label={label || 'Loading'}
24
- >
25
- <div
26
- class="{sizeClasses[size]} border-[var(--color-base-3)] border-t-[var(--color-accent)] rounded-[var(--radius-pill)] accent-glow animate-spin"
27
- />
28
- <span class="sr-only">{label || 'Loading'}</span>
29
- </div>
30
-
31
- <style>
32
- .sr-only {
33
- position: absolute;
34
- width: 1px;
35
- height: 1px;
36
- padding: 0;
37
- margin: -1px;
38
- overflow: hidden;
39
- clip: rect(0, 0, 0, 0);
40
- white-space: nowrap;
41
- border-width: 0;
42
- }
43
- </style>
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>
@@ -1,140 +1,111 @@
1
- <script lang="ts">
2
- interface Props {
3
- message: string;
4
- variant?: 'info' | 'success' | 'warning' | 'error';
5
- duration?: number;
6
- position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
7
- dismissible?: boolean;
8
- onclose?: () => void;
9
- /** Index in toast stack for vertical offset. Pass the toast's position in a list to enable stacking. */
10
- index?: number;
11
- }
12
-
13
- let {
14
- message,
15
- variant = 'info',
16
- duration = 3000,
17
- position = 'bottom-right',
18
- dismissible = true,
19
- onclose,
20
- index = 0
21
- }: Props = $props();
22
-
23
- let visible = $state(true);
24
- let isExiting = $state(false);
25
-
26
- const positionClasses = {
27
- 'top-left': 'top-4 left-4',
28
- 'top-center': 'top-4 left-1/2 -translate-x-1/2',
29
- 'top-right': 'top-4 right-4',
30
- 'bottom-left': 'bottom-4 left-4',
31
- 'bottom-center': 'bottom-4 left-1/2 -translate-x-1/2',
32
- 'bottom-right': 'bottom-4 right-4'
33
- };
34
-
35
- const variantBorderClasses = {
36
- info: 'border-l-4 border-[var(--color-accent)]',
37
- success: 'border-l-4 border-[var(--color-success)]',
38
- warning: 'border-l-4 border-[var(--color-warning)]',
39
- error: 'border-l-4 border-[var(--color-error)]'
40
- };
41
-
42
- const variantIconClasses = {
43
- info: 'text-[var(--color-accent-soft)]',
44
- success: 'text-[var(--color-success)]',
45
- warning: 'text-[var(--color-warning)]',
46
- error: 'text-[var(--color-error)]'
47
- };
48
-
49
- const variantIcons = {
50
- info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
51
- success: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
52
- 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',
53
- error: 'M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z'
54
- };
55
-
56
- const roleMap = {
57
- info: 'status',
58
- success: 'status',
59
- warning: 'alert',
60
- error: 'alert'
61
- };
62
-
63
- const ariaLiveMap = {
64
- info: 'polite',
65
- success: 'polite',
66
- warning: 'polite',
67
- error: 'assertive'
68
- } satisfies Record<'info' | 'success' | 'warning' | 'error', 'polite' | 'assertive'>;
69
-
70
- const animationClasses = $derived(
71
- position.startsWith('bottom')
72
- ? 'animate-[fade-up_0.3s_var(--ease-luxe)]'
73
- : 'animate-[fade-in_0.3s_var(--ease-luxe)]'
74
- );
75
-
76
- const stackOffset = $derived(
77
- position.startsWith('bottom')
78
- ? `translateY(-${index * 5}rem)`
79
- : `translateY(${index * 5}rem)`
80
- );
81
-
82
- function handleClose() {
83
- if (isExiting || !visible) return; // Prevent double-close
84
- isExiting = true;
85
- setTimeout(() => {
86
- visible = false;
87
- onclose?.();
88
- }, 200); // Match fade-out animation duration
89
- }
90
-
91
- $effect(() => {
92
- // Only auto-dismiss if toast is visible and duration is set
93
- if (visible && !isExiting && duration > 0) {
94
- const timer = setTimeout(() => {
95
- handleClose();
96
- }, duration);
97
-
98
- return () => {
99
- clearTimeout(timer);
100
- };
101
- }
102
- });
103
- </script>
104
-
105
- {#if visible}
106
- <div
107
- class="fixed z-[100] {positionClasses[position]} {isExiting ? 'animate-[fade-out_0.2s_var(--ease-sharp)]' : animationClasses}"
108
- style="transform: {stackOffset}"
109
- >
110
- <div
111
- class="glass-panel rounded-[var(--radius-lg)] p-4 min-w-[20rem] max-w-md flex gap-3 items-start {variantBorderClasses[variant]}"
112
- role={roleMap[variant]}
113
- aria-live={ariaLiveMap[variant]}
114
- aria-atomic="true"
115
- >
116
- <div class="flex-shrink-0 {variantIconClasses[variant]}">
117
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
118
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d={variantIcons[variant]} />
119
- </svg>
120
- </div>
121
-
122
- <div class="flex-1 text-sm text-[var(--color-text)]">
123
- {message}
124
- </div>
125
-
126
- {#if dismissible}
127
- <button
128
- type="button"
129
- onclick={handleClose}
130
- class="flex-shrink-0 text-[var(--color-text-soft)] hover:text-[var(--color-text)] transition-colors"
131
- aria-label="Dismiss notification"
132
- >
133
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
134
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
135
- </svg>
136
- </button>
137
- {/if}
138
- </div>
139
- </div>
140
- {/if}
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}
@@ -4,9 +4,12 @@ interface Props {
4
4
  duration?: number;
5
5
  position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
6
6
  dismissible?: boolean;
7
+ onClose?: () => void;
8
+ /** @deprecated Use onClose */
7
9
  onclose?: () => void;
8
10
  /** Index in toast stack for vertical offset. Pass the toast's position in a list to enable stacking. */
9
11
  index?: number;
12
+ icon?: import('svelte').Snippet;
10
13
  }
11
14
  declare const Toast: import("svelte").Component<Props, {}, "">;
12
15
  type Toast = ReturnType<typeof Toast>;