ui-svelte 0.2.10 → 0.2.12

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 (206) hide show
  1. package/dist/charts/ArcChart.svelte +9 -13
  2. package/dist/charts/ArcChart.svelte.d.ts +3 -3
  3. package/dist/charts/AreaChart.svelte +347 -118
  4. package/dist/charts/AreaChart.svelte.d.ts +33 -4
  5. package/dist/charts/BarChart.svelte +288 -66
  6. package/dist/charts/BarChart.svelte.d.ts +26 -1
  7. package/dist/charts/Candlestick.svelte +53 -50
  8. package/dist/charts/Candlestick.svelte.d.ts +8 -8
  9. package/dist/charts/LineChart.svelte +391 -91
  10. package/dist/charts/LineChart.svelte.d.ts +26 -3
  11. package/dist/charts/PieChart.svelte +333 -92
  12. package/dist/charts/PieChart.svelte.d.ts +33 -5
  13. package/dist/charts/css/arc-chart.css +3 -3
  14. package/dist/charts/css/area-chart.css +127 -29
  15. package/dist/charts/css/bar-chart.css +114 -8
  16. package/dist/charts/css/candlestick.css +2 -0
  17. package/dist/charts/css/line-chart.css +111 -13
  18. package/dist/charts/css/pie-chart.css +92 -20
  19. package/dist/control/Audio.svelte +86 -44
  20. package/dist/control/Audio.svelte.d.ts +4 -1
  21. package/dist/control/Button.svelte +18 -27
  22. package/dist/control/Button.svelte.d.ts +3 -2
  23. package/dist/control/IconButton.svelte +17 -27
  24. package/dist/control/IconButton.svelte.d.ts +3 -3
  25. package/dist/control/Image.svelte +123 -0
  26. package/dist/control/Image.svelte.d.ts +13 -0
  27. package/dist/control/Record.svelte +144 -98
  28. package/dist/control/Record.svelte.d.ts +2 -1
  29. package/dist/control/ToggleGroup.svelte +22 -8
  30. package/dist/control/ToggleGroup.svelte.d.ts +2 -1
  31. package/dist/control/ToggleTheme.svelte +13 -11
  32. package/dist/control/ToggleTheme.svelte.d.ts +3 -2
  33. package/dist/control/Video.svelte +55 -29
  34. package/dist/control/Video.svelte.d.ts +1 -0
  35. package/dist/control/css/btn.css +200 -152
  36. package/dist/control/css/image.css +56 -0
  37. package/dist/control/css/media.css +95 -30
  38. package/dist/control/css/toggle-group.css +269 -84
  39. package/dist/control/css/video.css +1 -14
  40. package/dist/css/animations.css +427 -2
  41. package/dist/css/base.css +13 -347
  42. package/dist/css/decorations.css +402 -0
  43. package/dist/css/rich-text.css +485 -0
  44. package/dist/css/transitions.css +158 -0
  45. package/dist/css/typography.css +291 -0
  46. package/dist/display/Accordion.svelte +28 -4
  47. package/dist/display/Accordion.svelte.d.ts +2 -1
  48. package/dist/display/Alert.svelte +32 -12
  49. package/dist/display/Alert.svelte.d.ts +2 -3
  50. package/dist/display/Avatar.svelte +54 -26
  51. package/dist/display/Avatar.svelte.d.ts +7 -1
  52. package/dist/display/AvatarGroup.svelte +26 -18
  53. package/dist/display/AvatarGroup.svelte.d.ts +9 -3
  54. package/dist/display/Badge.svelte +11 -4
  55. package/dist/display/Badge.svelte.d.ts +2 -1
  56. package/dist/display/Card.svelte +15 -14
  57. package/dist/display/Card.svelte.d.ts +2 -3
  58. package/dist/display/Carousel.svelte +130 -99
  59. package/dist/display/Carousel.svelte.d.ts +6 -4
  60. package/dist/display/ChatBox.svelte +245 -106
  61. package/dist/display/ChatBox.svelte.d.ts +32 -5
  62. package/dist/display/Chip.svelte +31 -17
  63. package/dist/display/Chip.svelte.d.ts +3 -2
  64. package/dist/display/Code.svelte +6 -3
  65. package/dist/display/Code.svelte.d.ts +1 -0
  66. package/dist/display/Collapsible.svelte +30 -4
  67. package/dist/display/Collapsible.svelte.d.ts +2 -1
  68. package/dist/display/Empty.svelte +37 -3
  69. package/dist/display/Empty.svelte.d.ts +3 -0
  70. package/dist/display/Item.svelte +31 -18
  71. package/dist/display/Item.svelte.d.ts +2 -2
  72. package/dist/display/Map.svelte +488 -0
  73. package/dist/display/Map.svelte.d.ts +44 -0
  74. package/dist/display/Section.svelte +14 -12
  75. package/dist/display/Section.svelte.d.ts +2 -3
  76. package/dist/display/Skeleton.svelte +32 -0
  77. package/dist/display/Skeleton.svelte.d.ts +10 -0
  78. package/dist/display/Table.svelte +94 -132
  79. package/dist/display/Table.svelte.d.ts +10 -1
  80. package/dist/display/css/accordion.css +349 -52
  81. package/dist/display/css/alert.css +18 -25
  82. package/dist/display/css/avatar-group.css +38 -44
  83. package/dist/display/css/avatar.css +152 -123
  84. package/dist/display/css/badge.css +50 -27
  85. package/dist/display/css/card.css +51 -71
  86. package/dist/display/css/carousel.css +25 -5
  87. package/dist/display/css/chat-box.css +158 -26
  88. package/dist/display/css/chip.css +142 -68
  89. package/dist/display/css/code.css +2 -6
  90. package/dist/display/css/collapsible.css +349 -45
  91. package/dist/display/css/divider.css +8 -6
  92. package/dist/display/css/empty.css +7 -0
  93. package/dist/display/css/item.css +311 -89
  94. package/dist/display/css/map.css +164 -0
  95. package/dist/display/css/section.css +78 -33
  96. package/dist/display/css/skeleton.css +58 -0
  97. package/dist/display/css/table.css +320 -189
  98. package/dist/form/Checkbox.svelte +11 -5
  99. package/dist/form/Checkbox.svelte.d.ts +2 -1
  100. package/dist/form/ColorField.svelte +543 -0
  101. package/dist/form/ColorField.svelte.d.ts +29 -0
  102. package/dist/form/ComboBox.svelte +24 -9
  103. package/dist/form/ComboBox.svelte.d.ts +2 -2
  104. package/dist/form/CsvField.svelte +62 -136
  105. package/dist/form/CsvField.svelte.d.ts +2 -2
  106. package/dist/form/DateField.svelte +33 -15
  107. package/dist/form/DateField.svelte.d.ts +2 -1
  108. package/dist/form/DateRange.svelte +436 -0
  109. package/dist/form/DateRange.svelte.d.ts +24 -0
  110. package/dist/form/DragDrop.svelte +348 -0
  111. package/dist/form/DragDrop.svelte.d.ts +32 -0
  112. package/dist/form/Dropzone.svelte +28 -8
  113. package/dist/form/Dropzone.svelte.d.ts +2 -2
  114. package/dist/form/Editor.svelte +626 -0
  115. package/dist/form/Editor.svelte.d.ts +50 -0
  116. package/dist/form/ImageCropper.svelte +291 -61
  117. package/dist/form/ImageCropper.svelte.d.ts +15 -1
  118. package/dist/form/PasswordField.svelte +120 -75
  119. package/dist/form/PasswordField.svelte.d.ts +9 -10
  120. package/dist/form/PhoneField.svelte +34 -16
  121. package/dist/form/PhoneField.svelte.d.ts +4 -3
  122. package/dist/form/PinField.svelte +39 -31
  123. package/dist/form/PinField.svelte.d.ts +3 -3
  124. package/dist/form/RadioGroup.svelte +4 -4
  125. package/dist/form/RadioGroup.svelte.d.ts +1 -1
  126. package/dist/form/Select.svelte +20 -19
  127. package/dist/form/Select.svelte.d.ts +2 -2
  128. package/dist/form/Slider.svelte +4 -2
  129. package/dist/form/Slider.svelte.d.ts +1 -0
  130. package/dist/form/TextField.svelte +16 -7
  131. package/dist/form/TextField.svelte.d.ts +2 -2
  132. package/dist/form/Textarea.svelte +15 -6
  133. package/dist/form/Textarea.svelte.d.ts +2 -2
  134. package/dist/form/Toggle.svelte +11 -1
  135. package/dist/form/Toggle.svelte.d.ts +2 -0
  136. package/dist/form/css/checkbox.css +18 -2
  137. package/dist/form/css/color-field.css +141 -0
  138. package/dist/form/css/control.css +193 -82
  139. package/dist/form/css/csv-field.css +226 -0
  140. package/dist/form/css/date-range.css +122 -0
  141. package/dist/form/css/date.css +24 -2
  142. package/dist/form/css/drag-drop.css +271 -0
  143. package/dist/form/css/dropzone.css +153 -34
  144. package/dist/form/css/editor.css +367 -0
  145. package/dist/form/css/field.css +4 -0
  146. package/dist/form/css/image-cropper.css +223 -22
  147. package/dist/form/css/password.css +1 -1
  148. package/dist/form/css/radio-group.css +1 -1
  149. package/dist/form/css/select.css +2 -2
  150. package/dist/form/css/slider.css +1 -0
  151. package/dist/form/css/textarea.css +178 -75
  152. package/dist/form/css/toggle.css +11 -2
  153. package/dist/hooks/use-table.svelte.d.ts +1 -0
  154. package/dist/hooks/use-table.svelte.js +6 -0
  155. package/dist/icons/index.d.ts +38 -2
  156. package/dist/icons/index.js +40 -4
  157. package/dist/index.css +16 -1
  158. package/dist/index.d.ts +11 -3
  159. package/dist/index.js +10 -2
  160. package/dist/layout/AppBar.svelte +22 -14
  161. package/dist/layout/AppBar.svelte.d.ts +2 -1
  162. package/dist/layout/Footer.svelte +19 -11
  163. package/dist/layout/Footer.svelte.d.ts +2 -1
  164. package/dist/layout/Provider.svelte +27 -4
  165. package/dist/layout/Provider.svelte.d.ts +3 -1
  166. package/dist/layout/css/app-bar.css +63 -66
  167. package/dist/layout/css/footer.css +62 -65
  168. package/dist/navigation/BottomNav.svelte +41 -13
  169. package/dist/navigation/FooterGroup.svelte +1 -1
  170. package/dist/navigation/NavMenu.svelte +47 -23
  171. package/dist/navigation/NavMenu.svelte.d.ts +29 -0
  172. package/dist/navigation/Pagination.svelte +158 -0
  173. package/dist/navigation/Pagination.svelte.d.ts +18 -0
  174. package/dist/navigation/SideNav.svelte +30 -25
  175. package/dist/navigation/SideNav.svelte.d.ts +2 -3
  176. package/dist/navigation/Tabs.svelte +17 -7
  177. package/dist/navigation/Tabs.svelte.d.ts +2 -2
  178. package/dist/navigation/css/bottom-nav.css +279 -257
  179. package/dist/navigation/css/footer-group.css +1 -1
  180. package/dist/navigation/css/footer-nav.css +1 -1
  181. package/dist/navigation/css/nav-menu.css +332 -106
  182. package/dist/navigation/css/pagination.css +74 -0
  183. package/dist/navigation/css/side-nav.css +515 -75
  184. package/dist/navigation/css/tabs.css +246 -52
  185. package/dist/overlay/Command.svelte +340 -0
  186. package/dist/overlay/Command.svelte.d.ts +24 -25
  187. package/dist/overlay/Drawer.svelte +49 -21
  188. package/dist/overlay/Drawer.svelte.d.ts +2 -2
  189. package/dist/overlay/Dropdown.svelte +4 -5
  190. package/dist/overlay/Modal.svelte +51 -16
  191. package/dist/overlay/Modal.svelte.d.ts +3 -3
  192. package/dist/overlay/Toast.svelte +41 -17
  193. package/dist/overlay/Toast.svelte.d.ts +1 -1
  194. package/dist/overlay/Tooltip.svelte +40 -26
  195. package/dist/overlay/Tooltip.svelte.d.ts +2 -2
  196. package/dist/overlay/css/command.css +80 -0
  197. package/dist/overlay/css/drawer.css +63 -24
  198. package/dist/overlay/css/dropdown.css +1 -1
  199. package/dist/overlay/css/hovercard.css +1 -1
  200. package/dist/overlay/css/modal.css +27 -27
  201. package/dist/overlay/css/toast.css +17 -29
  202. package/dist/overlay/css/tooltip.css +83 -66
  203. package/dist/stores/theme.svelte.js +26 -1
  204. package/dist/stores/toast.svelte.d.ts +4 -4
  205. package/dist/stores/toast.svelte.js +2 -2
  206. package/package.json +1 -1
@@ -0,0 +1,123 @@
1
+ <script lang="ts">
2
+ import {
3
+ MaximizeSquareMinimalisticLinearIcon,
4
+ ArrowDownloadRegularIcon
5
+ } from '../icons/index.js';
6
+ import { IconButton } from '../index.js';
7
+ import { cn } from '../utils/class-names.js';
8
+
9
+ type Props = {
10
+ src: string;
11
+ alt?: string;
12
+ aspect?: 'horizontal' | 'vertical' | 'square' | 'auto';
13
+ class?: string;
14
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
15
+ hideControls?: boolean;
16
+ loading?: 'lazy' | 'eager';
17
+ objectFit?: 'cover' | 'contain' | 'fill' | 'none';
18
+ };
19
+
20
+ let {
21
+ src,
22
+ alt = '',
23
+ aspect = 'auto',
24
+ class: className,
25
+ color = 'primary',
26
+ hideControls = false,
27
+ loading = 'lazy',
28
+ objectFit = 'cover'
29
+ }: Props = $props();
30
+
31
+ let showControls = $state(false);
32
+ let isLoading = $state(true);
33
+ let hasError = $state(false);
34
+
35
+ const handleMouseEnter = () => {
36
+ showControls = true;
37
+ };
38
+
39
+ const handleMouseLeave = () => {
40
+ showControls = false;
41
+ };
42
+
43
+ const handleLoad = () => {
44
+ isLoading = false;
45
+ };
46
+
47
+ const handleError = () => {
48
+ isLoading = false;
49
+ hasError = true;
50
+ };
51
+
52
+ const handleToggleMaximize = () => {
53
+ const containerElement = document.querySelector('.image');
54
+
55
+ if (!document.fullscreenElement) {
56
+ if (containerElement?.requestFullscreen) {
57
+ containerElement.requestFullscreen();
58
+ }
59
+ } else {
60
+ if (document.exitFullscreen) {
61
+ document.exitFullscreen();
62
+ }
63
+ }
64
+ };
65
+
66
+ const handleDownload = async () => {
67
+ try {
68
+ const response = await fetch(src);
69
+ const blob = await response.blob();
70
+ const url = window.URL.createObjectURL(blob);
71
+ const link = document.createElement('a');
72
+ link.href = url;
73
+ link.download = alt || 'image';
74
+ document.body.appendChild(link);
75
+ link.click();
76
+ document.body.removeChild(link);
77
+ window.URL.revokeObjectURL(url);
78
+ } catch (error) {
79
+ console.error('Download error:', error);
80
+ }
81
+ };
82
+ </script>
83
+
84
+ <div
85
+ class={cn('image', aspect, `fit-${objectFit}`, className)}
86
+ onmouseenter={handleMouseEnter}
87
+ onmouseleave={handleMouseLeave}
88
+ role="img"
89
+ aria-label={alt}
90
+ >
91
+ {#if isLoading}
92
+ <div class="image-skeleton"></div>
93
+ {/if}
94
+
95
+ {#if hasError}
96
+ <div class="image-error">
97
+ <span>Failed to load image</span>
98
+ </div>
99
+ {:else}
100
+ <img {src} {alt} {loading} onload={handleLoad} onerror={handleError} class="image-element" />
101
+ {/if}
102
+
103
+ {#if !hideControls && !hasError}
104
+ <div class={cn('image-controls', showControls ? 'visible' : 'invisible')}>
105
+ <div class="image-actions">
106
+ <IconButton
107
+ onclick={handleDownload}
108
+ icon={ArrowDownloadRegularIcon}
109
+ {color}
110
+ variant="overlay"
111
+ size="sm"
112
+ />
113
+ <IconButton
114
+ onclick={handleToggleMaximize}
115
+ icon={MaximizeSquareMinimalisticLinearIcon}
116
+ {color}
117
+ variant="overlay"
118
+ size="sm"
119
+ />
120
+ </div>
121
+ </div>
122
+ {/if}
123
+ </div>
@@ -0,0 +1,13 @@
1
+ type Props = {
2
+ src: string;
3
+ alt?: string;
4
+ aspect?: 'horizontal' | 'vertical' | 'square' | 'auto';
5
+ class?: string;
6
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger';
7
+ hideControls?: boolean;
8
+ loading?: 'lazy' | 'eager';
9
+ objectFit?: 'cover' | 'contain' | 'fill' | 'none';
10
+ };
11
+ declare const Image: import("svelte").Component<Props, {}, "">;
12
+ type Image = ReturnType<typeof Image>;
13
+ export default Image;
@@ -1,37 +1,46 @@
1
1
  <script lang="ts">
2
2
  import {
3
- Checkmark24RegularIcon,
3
+ CheckmarkCircle24RegularIcon,
4
4
  Delete24RegularIcon,
5
- Pause24RegularIcon,
6
- Play24RegularIcon,
7
- Record24RegularIcon,
5
+ DismissCircle24RegularIcon,
6
+ Microphone2LinearIcon,
7
+ PauseFilledIcon,
8
+ PlayFilledIcon,
8
9
  RecordStop24RegularIcon
9
10
  } from '../icons/index.js';
10
- import { Button, Icon } from '../index.js';
11
+ import { IconButton } from '../index.js';
11
12
  import { cn } from '../utils/class-names.js';
12
13
 
13
14
  type Props = {
14
15
  class?: string;
15
16
  name: string;
16
- variant?:
17
- | 'primary'
18
- | 'secondary'
19
- | 'muted'
20
- | 'outlined'
21
- | 'ghost'
22
- | 'success'
23
- | 'info'
24
- | 'danger'
25
- | 'warning';
17
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
18
+ variant?: 'soft' | 'solid';
26
19
  url?: string;
27
20
  headers?: Record<string, string>;
28
21
  onRecordingComplete?: (blob: Blob, url: string) => void;
29
22
  };
30
23
 
24
+ const colors = {
25
+ primary: 'is-primary',
26
+ secondary: 'is-secondary',
27
+ muted: 'is-muted',
28
+ success: 'is-success',
29
+ info: 'is-info',
30
+ warning: 'is-warning',
31
+ danger: 'is-danger'
32
+ };
33
+
34
+ const variants = {
35
+ soft: 'is-soft',
36
+ solid: 'is-solid'
37
+ };
38
+
31
39
  let {
32
40
  class: className,
33
41
  name,
34
- variant = 'primary',
42
+ color = 'primary',
43
+ variant = 'soft',
35
44
  url,
36
45
  headers,
37
46
  onRecordingComplete
@@ -61,7 +70,7 @@
61
70
 
62
71
  const BAR_COUNT = 50;
63
72
 
64
- let baseClasses = $derived(cn('media', variant, className));
73
+ let baseClasses = $derived(cn('media', colors[color], variants[variant], className));
65
74
 
66
75
  async function startRecording() {
67
76
  try {
@@ -213,16 +222,6 @@
213
222
  return `${mins}:${secs.toString().padStart(2, '0')}`;
214
223
  }
215
224
 
216
- function handleToggleRecording() {
217
- if (!isRecording) {
218
- startRecording();
219
- } else if (isPaused) {
220
- resumeRecording();
221
- } else {
222
- pauseRecording();
223
- }
224
- }
225
-
226
225
  async function confirmRecording() {
227
226
  if (!reviewAudioUrl) return;
228
227
 
@@ -256,12 +255,6 @@
256
255
  cleanup();
257
256
  }
258
257
 
259
- function continueRecording() {
260
- isReviewing = false;
261
- isRecording = false;
262
- isPaused = false;
263
- }
264
-
265
258
  async function analyzeRecordedAudio(blob: Blob) {
266
259
  try {
267
260
  const context = new AudioContext();
@@ -273,16 +266,24 @@
273
266
  const blockSize = Math.floor(rawData.length / samples);
274
267
  const filteredData: number[] = [];
275
268
 
269
+ const samplesPerBlock = Math.min(blockSize, 500);
270
+ const stride = Math.max(1, Math.floor(blockSize / samplesPerBlock));
271
+
276
272
  for (let i = 0; i < samples; i++) {
277
273
  let sum = 0;
278
- for (let j = 0; j < blockSize; j++) {
279
- sum += Math.abs(rawData[i * blockSize + j]);
274
+ let count = 0;
275
+ const blockStart = i * blockSize;
276
+ const blockEnd = Math.min(blockStart + blockSize, rawData.length);
277
+
278
+ for (let j = blockStart; j < blockEnd; j += stride) {
279
+ sum += Math.abs(rawData[j]);
280
+ count++;
280
281
  }
281
- filteredData.push(sum / blockSize);
282
+ filteredData.push(count > 0 ? sum / count : 0);
282
283
  }
283
284
 
284
285
  const max = Math.max(...filteredData);
285
- playbackWaveform = filteredData.map((value) => (value / max) * 0.8 + 0.2);
286
+ playbackWaveform = filteredData.map((value) => (max > 0 ? (value / max) * 0.8 + 0.2 : 0.5));
286
287
 
287
288
  await context.close();
288
289
  } catch (error) {
@@ -350,77 +351,122 @@
350
351
 
351
352
  <div class={baseClasses}>
352
353
  {#if isReviewing}
353
- <Button onclick={togglePlayback} size="md" {variant}>
354
- {#if isPlaying}
355
- <Icon icon={Pause24RegularIcon} />
356
- {:else}
357
- <Icon icon={Play24RegularIcon} />
358
- {/if}
359
- </Button>
360
-
361
- <div class="media-waveform">
362
- <div class="media-bars">
363
- {#each playbackWaveform as height, i}
364
- {@const progress = playbackDuration > 0 ? playbackTime / playbackDuration : 0}
365
- {@const barPosition = (i + 0.5) / playbackWaveform.length}
366
- {@const isPlayed = barPosition <= progress}
367
- <div class="media-bar" class:active={isPlayed} style="height: {height * 100}%"></div>
368
- {/each}
354
+ <!-- REVIEW STATE: Play/Pause preview, waveform, time, confirm/discard -->
355
+ <IconButton
356
+ onclick={togglePlayback}
357
+ icon={isPlaying ? PauseFilledIcon : PlayFilledIcon}
358
+ {color}
359
+ variant={variant === 'solid' ? 'soft' : 'solid'}
360
+ size="md"
361
+ />
362
+
363
+ <div class="media-content">
364
+ <div class="media-waveform">
365
+ <div class="media-bars">
366
+ {#each playbackWaveform as height, i}
367
+ {@const progress = playbackDuration > 0 ? playbackTime / playbackDuration : 0}
368
+ {@const barPosition = (i + 0.5) / playbackWaveform.length}
369
+ {@const isPlayed = barPosition <= progress}
370
+ <div class="media-bar" class:active={isPlayed} style="height: {height * 100}%"></div>
371
+ {/each}
372
+ </div>
373
+ </div>
374
+ <div class="media-footer">
375
+ <span class="media-time">{formatTime(recordingTime)}</span>
369
376
  </div>
370
377
  </div>
371
378
 
372
- <span class="media-time">{formatTime(recordingTime)}</span>
373
-
374
- <div class="flex gap-2">
375
- <Button onclick={discardRecording} size="md" {variant}>
376
- <Icon icon={Delete24RegularIcon} />
377
- </Button>
378
- <Button onclick={continueRecording} size="md" {variant}>
379
- <Icon icon={Record24RegularIcon} />
380
- </Button>
381
- <Button onclick={confirmRecording} size="md" {variant}>
382
- <Icon icon={Checkmark24RegularIcon} />
383
- </Button>
379
+ <div class="flex gap-1">
380
+ <IconButton
381
+ onclick={discardRecording}
382
+ icon={DismissCircle24RegularIcon}
383
+ color="danger"
384
+ variant="ghost"
385
+ size="sm"
386
+ />
387
+ <IconButton
388
+ onclick={confirmRecording}
389
+ icon={CheckmarkCircle24RegularIcon}
390
+ color="success"
391
+ variant="ghost"
392
+ size="sm"
393
+ isLoading={isUploading}
394
+ />
384
395
  </div>
385
396
  {:else if !isRecording}
386
- <Button onclick={startRecording} size="md" {variant}>
387
- <Icon icon={Record24RegularIcon} />
388
- </Button>
389
-
390
- <div class="media-waveform">
391
- <div class="media-bars">
392
- {#each waveformBars as height}
393
- <div class="media-bar" style="height: {height * 100}%"></div>
394
- {/each}
397
+ <!-- IDLE STATE: Start recording button -->
398
+ <IconButton
399
+ onclick={startRecording}
400
+ icon={Microphone2LinearIcon}
401
+ {color}
402
+ variant={variant === 'solid' ? 'soft' : 'solid'}
403
+ size="md"
404
+ />
405
+
406
+ <div class="media-content">
407
+ <div class="media-waveform">
408
+ <div class="media-bars">
409
+ {#each waveformBars as height}
410
+ <div class="media-bar" style="height: {height * 100}%"></div>
411
+ {/each}
412
+ </div>
413
+ </div>
414
+ <div class="media-footer">
415
+ <span class="media-time">{formatTime(recordingTime)}</span>
395
416
  </div>
396
417
  </div>
397
-
398
- <span class="media-time">{formatTime(recordingTime)}</span>
399
418
  {:else}
400
- <Button onclick={handleToggleRecording} size="md" {variant}>
401
- {#if isPaused}
402
- <Icon icon={Play24RegularIcon} />
403
- {:else}
404
- <Icon icon={Pause24RegularIcon} />
405
- {/if}
406
- </Button>
407
-
408
- <div class="media-waveform">
409
- <div class="media-bars">
410
- {#each waveformBars as height}
411
- <div
412
- class="media-bar"
413
- class:recording={isRecording && !isPaused}
414
- style="height: {height * 100}%"
415
- ></div>
416
- {/each}
419
+ <!-- RECORDING STATE: Pause/Resume, waveform, time, discard, stop -->
420
+ {#if isPaused}
421
+ <IconButton
422
+ onclick={resumeRecording}
423
+ icon={PlayFilledIcon}
424
+ {color}
425
+ variant={variant === 'solid' ? 'soft' : 'solid'}
426
+ size="md"
427
+ />
428
+ {:else}
429
+ <IconButton
430
+ onclick={pauseRecording}
431
+ icon={PauseFilledIcon}
432
+ {color}
433
+ variant={variant === 'solid' ? 'soft' : 'solid'}
434
+ size="md"
435
+ />
436
+ {/if}
437
+
438
+ <div class="media-content">
439
+ <div class="media-waveform">
440
+ <div class="media-bars">
441
+ {#each waveformBars as height}
442
+ <div
443
+ class="media-bar"
444
+ class:recording={isRecording && !isPaused}
445
+ style="height: {height * 100}%"
446
+ ></div>
447
+ {/each}
448
+ </div>
449
+ </div>
450
+ <div class="media-footer">
451
+ <span class="media-time">{formatTime(recordingTime)}</span>
417
452
  </div>
418
453
  </div>
419
454
 
420
- <span class="media-time">{formatTime(recordingTime)}</span>
421
-
422
- <Button onclick={stopRecording} size="md" {variant} isLoading={isUploading}>
423
- <Icon icon={RecordStop24RegularIcon} />
424
- </Button>
455
+ <div class="flex gap-1">
456
+ <IconButton
457
+ onclick={discardRecording}
458
+ icon={Delete24RegularIcon}
459
+ color="danger"
460
+ variant="ghost"
461
+ size="sm"
462
+ />
463
+ <IconButton
464
+ onclick={stopRecording}
465
+ icon={RecordStop24RegularIcon}
466
+ {color}
467
+ variant="ghost"
468
+ size="sm"
469
+ />
470
+ </div>
425
471
  {/if}
426
472
  </div>
@@ -1,7 +1,8 @@
1
1
  type Props = {
2
2
  class?: string;
3
3
  name: string;
4
- variant?: 'primary' | 'secondary' | 'muted' | 'outlined' | 'ghost' | 'success' | 'info' | 'danger' | 'warning';
4
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
5
+ variant?: 'soft' | 'solid';
5
6
  url?: string;
6
7
  headers?: Record<string, string>;
7
8
  onRecordingComplete?: (blob: Blob, url: string) => void;
@@ -13,7 +13,8 @@
13
13
  items: ToggleGroupItem[];
14
14
  value?: string;
15
15
  onchange?: (value: string) => void;
16
- variant?: 'default' | 'primary' | 'secondary' | 'outlined';
16
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
17
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
17
18
  size?: 'xs' | 'sm' | 'md' | 'lg';
18
19
  class?: string;
19
20
  isWide?: boolean;
@@ -23,9 +24,11 @@
23
24
 
24
25
  let {
25
26
  items,
27
+ // svelte-ignore state_referenced_locally
26
28
  value = $bindable(items[0]?.id ?? ''),
27
29
  onchange,
28
- variant = 'default',
30
+ color = 'muted',
31
+ variant = 'soft',
29
32
  size = 'md',
30
33
  class: className,
31
34
  isWide,
@@ -33,14 +36,24 @@
33
36
  isDisabled
34
37
  }: Props = $props();
35
38
 
36
- const variantClasses: Record<string, string> = {
37
- default: '',
39
+ const colors = {
38
40
  primary: 'is-primary',
39
41
  secondary: 'is-secondary',
40
- outlined: 'is-outlined'
42
+ muted: 'is-muted',
43
+ success: 'is-success',
44
+ info: 'is-info',
45
+ danger: 'is-danger',
46
+ warning: 'is-warning'
41
47
  };
42
48
 
43
- const sizeClasses: Record<string, string> = {
49
+ const variants = {
50
+ solid: 'is-solid',
51
+ soft: 'is-soft',
52
+ outlined: 'is-outlined',
53
+ ghost: 'is-ghost'
54
+ };
55
+
56
+ const sizes = {
44
57
  xs: 'is-xs',
45
58
  sm: 'is-sm',
46
59
  md: 'is-md',
@@ -50,7 +63,8 @@
50
63
  let groupClasses = $derived(
51
64
  cn(
52
65
  'toggle-group',
53
- variantClasses[variant],
66
+ variants[variant],
67
+ colors[color],
54
68
  isWide && 'is-wide',
55
69
  isVertical && 'is-vertical',
56
70
  className
@@ -68,7 +82,7 @@
68
82
  {#each items as item}
69
83
  <button
70
84
  type="button"
71
- class={cn('toggle-group-item', sizeClasses[size], value === item.id && 'is-active')}
85
+ class={cn('toggle-group-item', sizes[size], value === item.id && 'is-active')}
72
86
  onclick={() => handleClick(item.id)}
73
87
  disabled={isDisabled}
74
88
  aria-pressed={value === item.id}
@@ -8,7 +8,8 @@ type Props = {
8
8
  items: ToggleGroupItem[];
9
9
  value?: string;
10
10
  onchange?: (value: string) => void;
11
- variant?: 'default' | 'primary' | 'secondary' | 'outlined';
11
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
12
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
12
13
  size?: 'xs' | 'sm' | 'md' | 'lg';
13
14
  class?: string;
14
15
  isWide?: boolean;
@@ -1,21 +1,23 @@
1
1
  <script lang="ts">
2
2
  import { MoonStarsLinearIcon, Sun2LinearIcon } from '../icons/index.js';
3
- import { Button, Icon } from '../index.js';
3
+ import { IconButton } from '../index.js';
4
4
  import { theme } from '../stores/theme.svelte.js';
5
5
 
6
6
  type Props = {
7
- variant?: 'primary' | 'secondary' | 'muted' | 'ghost';
8
- size?: 'sm' | 'md' | 'lg';
7
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
8
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
9
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
10
  class?: string;
10
11
  };
11
12
 
12
- let { variant = 'ghost', size = 'md', class: className }: Props = $props();
13
+ let { color = 'muted', variant = 'ghost', size = 'md', class: className }: Props = $props();
13
14
  </script>
14
15
 
15
- <Button onclick={theme.toggleTheme} {size} {variant} class={className} isIcon>
16
- {#if theme.isDark}
17
- <Icon icon={Sun2LinearIcon} />
18
- {:else}
19
- <Icon icon={MoonStarsLinearIcon} />
20
- {/if}
21
- </Button>
16
+ <IconButton
17
+ icon={theme.isDark ? Sun2LinearIcon : MoonStarsLinearIcon}
18
+ {size}
19
+ {color}
20
+ {variant}
21
+ class={className}
22
+ onclick={theme.toggleTheme}
23
+ />
@@ -1,6 +1,7 @@
1
1
  type Props = {
2
- variant?: 'primary' | 'secondary' | 'muted' | 'ghost';
3
- size?: 'sm' | 'md' | 'lg';
2
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
3
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
4
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
5
  class?: string;
5
6
  };
6
7
  declare const ToggleTheme: import("svelte").Component<Props, {}, "">;