ui-svelte 0.2.11 → 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 (204) 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 +5 -9
  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 +23 -18
  51. package/dist/display/Avatar.svelte.d.ts +4 -1
  52. package/dist/display/AvatarGroup.svelte +20 -18
  53. package/dist/display/AvatarGroup.svelte.d.ts +6 -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 +30 -11
  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 -75
  83. package/dist/display/css/avatar.css +139 -121
  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/{PasswordStrength.svelte → PasswordField.svelte} +58 -24
  119. package/dist/form/{PasswordStrength.svelte.d.ts → PasswordField.svelte.d.ts} +6 -5
  120. package/dist/form/PhoneField.svelte +26 -14
  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 +1 -1
  135. package/dist/form/css/checkbox.css +18 -2
  136. package/dist/form/css/color-field.css +141 -0
  137. package/dist/form/css/control.css +193 -82
  138. package/dist/form/css/csv-field.css +226 -0
  139. package/dist/form/css/date-range.css +122 -0
  140. package/dist/form/css/date.css +24 -2
  141. package/dist/form/css/drag-drop.css +271 -0
  142. package/dist/form/css/dropzone.css +153 -34
  143. package/dist/form/css/editor.css +367 -0
  144. package/dist/form/css/field.css +4 -0
  145. package/dist/form/css/image-cropper.css +223 -22
  146. package/dist/form/css/radio-group.css +1 -1
  147. package/dist/form/css/select.css +2 -2
  148. package/dist/form/css/slider.css +1 -0
  149. package/dist/form/css/textarea.css +178 -75
  150. package/dist/form/css/toggle.css +3 -3
  151. package/dist/hooks/use-table.svelte.d.ts +1 -0
  152. package/dist/hooks/use-table.svelte.js +6 -0
  153. package/dist/icons/index.d.ts +30 -2
  154. package/dist/icons/index.js +32 -4
  155. package/dist/index.css +16 -1
  156. package/dist/index.d.ts +12 -4
  157. package/dist/index.js +11 -3
  158. package/dist/layout/AppBar.svelte +22 -14
  159. package/dist/layout/AppBar.svelte.d.ts +2 -1
  160. package/dist/layout/Footer.svelte +19 -11
  161. package/dist/layout/Footer.svelte.d.ts +2 -1
  162. package/dist/layout/Provider.svelte +27 -4
  163. package/dist/layout/Provider.svelte.d.ts +3 -1
  164. package/dist/layout/css/app-bar.css +63 -66
  165. package/dist/layout/css/footer.css +62 -65
  166. package/dist/navigation/BottomNav.svelte +41 -13
  167. package/dist/navigation/FooterGroup.svelte +1 -1
  168. package/dist/navigation/NavMenu.svelte +47 -23
  169. package/dist/navigation/NavMenu.svelte.d.ts +29 -0
  170. package/dist/navigation/Pagination.svelte +158 -0
  171. package/dist/navigation/Pagination.svelte.d.ts +18 -0
  172. package/dist/navigation/SideNav.svelte +30 -25
  173. package/dist/navigation/SideNav.svelte.d.ts +2 -3
  174. package/dist/navigation/Tabs.svelte +17 -7
  175. package/dist/navigation/Tabs.svelte.d.ts +2 -2
  176. package/dist/navigation/css/bottom-nav.css +279 -257
  177. package/dist/navigation/css/footer-group.css +1 -1
  178. package/dist/navigation/css/footer-nav.css +1 -1
  179. package/dist/navigation/css/nav-menu.css +332 -106
  180. package/dist/navigation/css/pagination.css +74 -0
  181. package/dist/navigation/css/side-nav.css +515 -75
  182. package/dist/navigation/css/tabs.css +246 -52
  183. package/dist/overlay/Command.svelte +340 -0
  184. package/dist/overlay/Command.svelte.d.ts +24 -25
  185. package/dist/overlay/Drawer.svelte +49 -21
  186. package/dist/overlay/Drawer.svelte.d.ts +2 -2
  187. package/dist/overlay/Dropdown.svelte +3 -3
  188. package/dist/overlay/Modal.svelte +51 -16
  189. package/dist/overlay/Modal.svelte.d.ts +3 -3
  190. package/dist/overlay/Toast.svelte +41 -17
  191. package/dist/overlay/Toast.svelte.d.ts +1 -1
  192. package/dist/overlay/Tooltip.svelte +40 -26
  193. package/dist/overlay/Tooltip.svelte.d.ts +2 -2
  194. package/dist/overlay/css/command.css +80 -0
  195. package/dist/overlay/css/drawer.css +63 -24
  196. package/dist/overlay/css/dropdown.css +1 -1
  197. package/dist/overlay/css/hovercard.css +1 -1
  198. package/dist/overlay/css/modal.css +27 -27
  199. package/dist/overlay/css/toast.css +17 -29
  200. package/dist/overlay/css/tooltip.css +83 -66
  201. package/dist/stores/theme.svelte.js +26 -1
  202. package/dist/stores/toast.svelte.d.ts +4 -4
  203. package/dist/stores/toast.svelte.js +2 -2
  204. package/package.json +1 -1
@@ -1,36 +1,80 @@
1
1
  <script lang="ts">
2
- import { Icon } from '../index.js';
2
+ import type { IconData } from '../display/Icon.svelte';
3
+ import {
4
+ ImageAddRegularIcon,
5
+ ArrowResetRegularIcon,
6
+ CropRegularIcon,
7
+ Dismiss24RegularIcon
8
+ } from '../icons/index.js';
9
+ import { Icon, IconButton, Modal, Button } from '../index.js';
3
10
  import { cn } from '../utils/class-names.js';
4
11
 
5
12
  type Props = {
6
- src: string;
13
+ src?: string;
7
14
  alt?: string;
8
15
  aspectRatio?: number;
9
16
  minWidth?: number;
10
17
  minHeight?: number;
11
18
  maxWidth?: number;
12
19
  maxHeight?: number;
20
+ accept?: string;
21
+ placeholder?: string;
22
+ icon?: IconData;
23
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
24
+ shape?: 'circle' | 'square';
25
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
26
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
27
+ disabled?: boolean;
13
28
  onCrop?: (croppedImage: string) => void;
29
+ onChange?: (file: File | null) => void;
14
30
  class?: string;
31
+ name?: string;
32
+ label?: string;
33
+ helpText?: string;
34
+ errorText?: string;
15
35
  };
16
36
 
17
37
  const {
18
- src,
19
- alt = 'Image to crop',
38
+ src: initialSrc,
39
+ alt = 'Avatar',
20
40
  aspectRatio,
21
41
  minWidth = 50,
22
42
  minHeight = 50,
23
43
  maxWidth,
24
44
  maxHeight,
45
+ accept = 'image/*',
46
+ placeholder = 'Upload',
47
+ icon = ImageAddRegularIcon,
48
+ size = 'md',
49
+ shape = 'circle',
50
+ color = 'muted',
51
+ variant = 'soft',
52
+ disabled = false,
25
53
  onCrop,
26
- class: className
54
+ onChange,
55
+ class: className,
56
+ name,
57
+ label,
58
+ helpText,
59
+ errorText
27
60
  }: Props = $props();
28
61
 
29
- let canvas: HTMLCanvasElement;
30
- let image: HTMLImageElement;
31
- let containerDiv: HTMLDivElement;
62
+ const uid = $props.id();
32
63
 
33
- let isDragging = $state(false);
64
+ let canvas = $state<HTMLCanvasElement>();
65
+ let image = $state<HTMLImageElement>();
66
+ let containerDiv = $state<HTMLDivElement>();
67
+ let fileInput = $state<HTMLInputElement>();
68
+
69
+ // svelte-ignore state_referenced_locally
70
+ let currentSrc = $state(initialSrc || '');
71
+ // svelte-ignore state_referenced_locally
72
+ let croppedSrc = $state(initialSrc || '');
73
+ let uploadedFile = $state<File | null>(null);
74
+
75
+ let showModal = $state(false);
76
+
77
+ let isCropDragging = $state(false);
34
78
  let isResizing = $state(false);
35
79
  let resizeHandle = $state<string>('');
36
80
 
@@ -51,6 +95,57 @@
51
95
  let imageHeight = $state(0);
52
96
  let scale = $state(1);
53
97
 
98
+ const sizeClasses = {
99
+ xs: 'is-xs',
100
+ sm: 'is-sm',
101
+ md: 'is-md',
102
+ lg: 'is-lg',
103
+ xl: 'is-xl'
104
+ };
105
+
106
+ const colorClasses = {
107
+ primary: 'is-primary',
108
+ secondary: 'is-secondary',
109
+ muted: 'is-muted',
110
+ success: 'is-success',
111
+ info: 'is-info',
112
+ danger: 'is-danger',
113
+ warning: 'is-warning'
114
+ };
115
+
116
+ const variantClasses = {
117
+ solid: 'is-solid',
118
+ soft: 'is-soft',
119
+ outlined: 'is-outlined',
120
+ ghost: 'is-ghost'
121
+ };
122
+
123
+ $effect(() => {
124
+ if (initialSrc && initialSrc !== currentSrc) {
125
+ currentSrc = initialSrc;
126
+ croppedSrc = initialSrc;
127
+ imageLoaded = false;
128
+ }
129
+ });
130
+
131
+ function handleFiles(fileList: FileList | null) {
132
+ if (!fileList || fileList.length === 0 || disabled) return;
133
+
134
+ const file = fileList[0];
135
+ if (!file.type.startsWith('image/')) return;
136
+
137
+ if (uploadedFile && currentSrc.startsWith('blob:')) {
138
+ URL.revokeObjectURL(currentSrc);
139
+ }
140
+
141
+ uploadedFile = file;
142
+ currentSrc = URL.createObjectURL(file);
143
+ imageLoaded = false;
144
+ onChange?.(file);
145
+
146
+ showModal = true;
147
+ }
148
+
54
149
  function handleImageLoad() {
55
150
  if (!image || !containerDiv) return;
56
151
 
@@ -67,7 +162,7 @@
67
162
  imageWidth = naturalWidth * scale;
68
163
  imageHeight = naturalHeight * scale;
69
164
 
70
- const initialSize = Math.min(imageWidth, imageHeight) * 0.6;
165
+ const initialSize = Math.min(imageWidth, imageHeight) * 0.8;
71
166
  cropWidth = aspectRatio ? initialSize : initialSize;
72
167
  cropHeight = aspectRatio ? initialSize / aspectRatio : initialSize;
73
168
  cropX = (imageWidth - cropWidth) / 2;
@@ -84,7 +179,7 @@
84
179
  isResizing = true;
85
180
  resizeHandle = handle;
86
181
  } else {
87
- isDragging = true;
182
+ isCropDragging = true;
88
183
  }
89
184
 
90
185
  startX = e.clientX;
@@ -102,7 +197,7 @@
102
197
  const deltaX = e.clientX - startX;
103
198
  const deltaY = e.clientY - startY;
104
199
 
105
- if (isDragging) {
200
+ if (isCropDragging) {
106
201
  cropX = Math.max(0, Math.min(imageWidth - cropWidth, startCropX + deltaX));
107
202
  cropY = Math.max(0, Math.min(imageHeight - cropHeight, startCropY + deltaY));
108
203
  } else if (isResizing) {
@@ -148,7 +243,7 @@
148
243
  }
149
244
 
150
245
  function handleMouseUp() {
151
- isDragging = false;
246
+ isCropDragging = false;
152
247
  isResizing = false;
153
248
  resizeHandle = '';
154
249
  document.removeEventListener('mousemove', handleMouseMove);
@@ -180,75 +275,210 @@
180
275
  );
181
276
 
182
277
  const croppedImageUrl = canvas.toDataURL('image/png');
278
+ croppedSrc = croppedImageUrl;
183
279
  onCrop?.(croppedImageUrl);
280
+ showModal = false;
184
281
  }
185
282
 
186
283
  function handleReset() {
187
284
  if (!imageLoaded) return;
188
- const initialSize = Math.min(imageWidth, imageHeight) * 0.6;
285
+ const initialSize = Math.min(imageWidth, imageHeight) * 0.8;
189
286
  cropWidth = aspectRatio ? initialSize : initialSize;
190
287
  cropHeight = aspectRatio ? initialSize / aspectRatio : initialSize;
191
288
  cropX = (imageWidth - cropWidth) / 2;
192
289
  cropY = (imageHeight - cropHeight) / 2;
193
290
  }
291
+
292
+ function handleRemoveImage() {
293
+ if (uploadedFile && currentSrc.startsWith('blob:')) {
294
+ URL.revokeObjectURL(currentSrc);
295
+ }
296
+ uploadedFile = null;
297
+ currentSrc = '';
298
+ croppedSrc = '';
299
+ imageLoaded = false;
300
+ onChange?.(null);
301
+ }
302
+
303
+ function openCropModal() {
304
+ if (currentSrc && !disabled) {
305
+ showModal = true;
306
+ }
307
+ }
308
+
309
+ function closeModal() {
310
+ showModal = false;
311
+ }
194
312
  </script>
195
313
 
196
- <div class={cn('image-cropper', className)}>
197
- <div class="image-cropper-container" bind:this={containerDiv}>
198
- <img
199
- bind:this={image}
200
- {src}
201
- {alt}
202
- class="image-cropper-image"
203
- onload={handleImageLoad}
204
- style="width: {imageWidth}px; height: {imageHeight}px;"
314
+ <div class={cn('field', className)}>
315
+ {#if label}
316
+ <span class="field-label">{label}</span>
317
+ {/if}
318
+
319
+ <div class="image-cropper-wrapper">
320
+ <input
321
+ bind:this={fileInput}
322
+ id={`${uid}-${name}`}
323
+ type="file"
324
+ hidden
325
+ {accept}
326
+ {disabled}
327
+ {name}
328
+ onchange={(e) => handleFiles((e.target as HTMLInputElement).files)}
205
329
  />
206
330
 
207
- {#if imageLoaded}
208
- <div class="image-cropper-overlay">
209
- <div class="overlay-top" style="height: {cropY}px;"></div>
210
- <div class="overlay-middle" style="top: {cropY}px; height: {cropHeight}px;">
211
- <div class="overlay-left" style="width: {cropX}px;"></div>
212
- <div
213
- class="overlay-right"
214
- style="left: {cropX + cropWidth}px; width: {imageWidth - cropX - cropWidth}px;"
215
- ></div>
331
+ <button
332
+ type="button"
333
+ class={cn(
334
+ 'image-cropper-avatar',
335
+ sizeClasses[size],
336
+ colorClasses[color],
337
+ variantClasses[variant],
338
+ shape === 'circle' && 'is-circle',
339
+ disabled && 'is-disabled'
340
+ )}
341
+ class:is-error={errorText}
342
+ onclick={() => (croppedSrc ? openCropModal() : fileInput?.click())}
343
+ {disabled}
344
+ >
345
+ {#if croppedSrc}
346
+ <img src={croppedSrc} {alt} class="image-cropper-preview" />
347
+ <div class="image-cropper-overlay-edit">
348
+ <Icon icon={CropRegularIcon} />
216
349
  </div>
217
- <div
218
- class="overlay-bottom"
219
- style="top: {cropY + cropHeight}px; height: {imageHeight - cropY - cropHeight}px;"
220
- ></div>
350
+ {:else}
351
+ <Icon {icon} class="image-cropper-icon" />
352
+ <span class="image-cropper-placeholder">{placeholder}</span>
353
+ {/if}
354
+ </button>
355
+
356
+ {#if croppedSrc}
357
+ <div class="image-cropper-actions">
358
+ <IconButton
359
+ icon={ImageAddRegularIcon}
360
+ variant="ghost"
361
+ {color}
362
+ size="sm"
363
+ onclick={() => fileInput?.click()}
364
+ />
365
+ {#if uploadedFile}
366
+ <IconButton
367
+ icon={Dismiss24RegularIcon}
368
+ variant="ghost"
369
+ color="danger"
370
+ size="sm"
371
+ onclick={handleRemoveImage}
372
+ />
373
+ {/if}
374
+ </div>
375
+ {/if}
376
+ </div>
221
377
 
222
- <!-- svelte-ignore a11y_no_static_element_interactions -->
378
+ {#if errorText || helpText}
379
+ <div class={cn('field-help', errorText && 'is-danger')}>
380
+ {errorText || helpText}
381
+ </div>
382
+ {/if}
383
+ </div>
384
+
385
+ <Modal bind:open={showModal} onclose={closeModal} color="surface" rootClass="image-cropper-modal">
386
+ {#snippet header()}
387
+ <h2 class="image-cropper-modal-title">Crop Image</h2>
388
+ {/snippet}
389
+
390
+ <div class="image-cropper-modal-body">
391
+ <div class="image-cropper-container" bind:this={containerDiv}>
392
+ <img
393
+ bind:this={image}
394
+ src={currentSrc}
395
+ {alt}
396
+ class="image-cropper-image"
397
+ onload={handleImageLoad}
398
+ style="width: {imageWidth}px; height: {imageHeight}px;"
399
+ />
400
+
401
+ {#if imageLoaded}
223
402
  <div
224
- class="crop-area"
225
- style="left: {cropX}px; top: {cropY}px; width: {cropWidth}px; height: {cropHeight}px;"
226
- onmousedown={(e) => handleMouseDown(e)}
403
+ class="image-cropper-crop-overlay"
404
+ style="width: {imageWidth}px; height: {imageHeight}px;"
227
405
  >
228
- <div class="crop-grid">
229
- <div class="grid-line grid-line-v" style="left: 33.33%;"></div>
230
- <div class="grid-line grid-line-v" style="left: 66.66%;"></div>
231
- <div class="grid-line grid-line-h" style="top: 33.33%;"></div>
232
- <div class="grid-line grid-line-h" style="top: 66.66%;"></div>
406
+ <div class="overlay-top" style="height: {cropY}px;"></div>
407
+ <div class="overlay-middle" style="top: {cropY}px; height: {cropHeight}px;">
408
+ <div class="overlay-left" style="width: {cropX}px;"></div>
409
+ <div
410
+ class="overlay-right"
411
+ style="left: {cropX + cropWidth}px; width: {imageWidth - cropX - cropWidth}px;"
412
+ ></div>
233
413
  </div>
414
+ <div
415
+ class="overlay-bottom"
416
+ style="top: {cropY + cropHeight}px; height: {imageHeight - cropY - cropHeight}px;"
417
+ ></div>
234
418
 
235
- <div class="resize-handle handle-nw" onmousedown={(e) => handleMouseDown(e, 'nw')}></div>
236
- <div class="resize-handle handle-n" onmousedown={(e) => handleMouseDown(e, 'n')}></div>
237
- <div class="resize-handle handle-ne" onmousedown={(e) => handleMouseDown(e, 'ne')}></div>
238
- <div class="resize-handle handle-e" onmousedown={(e) => handleMouseDown(e, 'e')}></div>
239
- <div class="resize-handle handle-se" onmousedown={(e) => handleMouseDown(e, 'se')}></div>
240
- <div class="resize-handle handle-s" onmousedown={(e) => handleMouseDown(e, 's')}></div>
241
- <div class="resize-handle handle-sw" onmousedown={(e) => handleMouseDown(e, 'sw')}></div>
242
- <div class="resize-handle handle-w" onmousedown={(e) => handleMouseDown(e, 'w')}></div>
419
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
420
+ <div
421
+ class="crop-area"
422
+ class:is-circle={shape === 'circle'}
423
+ style="left: {cropX}px; top: {cropY}px; width: {cropWidth}px; height: {cropHeight}px;"
424
+ onmousedown={(e) => handleMouseDown(e)}
425
+ >
426
+ <div class="crop-grid">
427
+ <div class="grid-line grid-line-v" style="left: 33.33%;"></div>
428
+ <div class="grid-line grid-line-v" style="left: 66.66%;"></div>
429
+ <div class="grid-line grid-line-h" style="top: 33.33%;"></div>
430
+ <div class="grid-line grid-line-h" style="top: 66.66%;"></div>
431
+ </div>
432
+
433
+ <div
434
+ class="resize-handle handle-nw"
435
+ onmousedown={(e) => handleMouseDown(e, 'nw')}
436
+ ></div>
437
+ <div class="resize-handle handle-n" onmousedown={(e) => handleMouseDown(e, 'n')}></div>
438
+ <div
439
+ class="resize-handle handle-ne"
440
+ onmousedown={(e) => handleMouseDown(e, 'ne')}
441
+ ></div>
442
+ <div class="resize-handle handle-e" onmousedown={(e) => handleMouseDown(e, 'e')}></div>
443
+ <div
444
+ class="resize-handle handle-se"
445
+ onmousedown={(e) => handleMouseDown(e, 'se')}
446
+ ></div>
447
+ <div class="resize-handle handle-s" onmousedown={(e) => handleMouseDown(e, 's')}></div>
448
+ <div
449
+ class="resize-handle handle-sw"
450
+ onmousedown={(e) => handleMouseDown(e, 'sw')}
451
+ ></div>
452
+ <div class="resize-handle handle-w" onmousedown={(e) => handleMouseDown(e, 'w')}></div>
453
+ </div>
243
454
  </div>
244
- </div>
245
- {/if}
246
- </div>
455
+ {/if}
456
+ </div>
247
457
 
248
- <div class="image-cropper-controls">
249
- <button class="btn is-md is-outlined" onclick={handleReset}> refresh icon </button>
250
- <button class="btn is-md is-primary is-solid" onclick={handleCrop}> crop icon </button>
458
+ <canvas bind:this={canvas} class="image-cropper-canvas"></canvas>
251
459
  </div>
252
460
 
253
- <canvas bind:this={canvas} class="image-cropper-canvas"></canvas>
254
- </div>
461
+ {#snippet footer()}
462
+ <div class="image-cropper-modal-footer">
463
+ <Button
464
+ color="muted"
465
+ variant="ghost"
466
+ size="sm"
467
+ onclick={handleReset}
468
+ startIcon={ArrowResetRegularIcon}
469
+ >
470
+ Reset
471
+ </Button>
472
+ <Button color="muted" variant="outlined" size="sm" onclick={closeModal}>Cancel</Button>
473
+ <Button
474
+ color="primary"
475
+ variant="solid"
476
+ size="sm"
477
+ onclick={handleCrop}
478
+ startIcon={CropRegularIcon}
479
+ >
480
+ Apply
481
+ </Button>
482
+ </div>
483
+ {/snippet}
484
+ </Modal>
@@ -1,13 +1,27 @@
1
+ import type { IconData } from '../display/Icon.svelte';
1
2
  type Props = {
2
- src: string;
3
+ src?: string;
3
4
  alt?: string;
4
5
  aspectRatio?: number;
5
6
  minWidth?: number;
6
7
  minHeight?: number;
7
8
  maxWidth?: number;
8
9
  maxHeight?: number;
10
+ accept?: string;
11
+ placeholder?: string;
12
+ icon?: IconData;
13
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
14
+ shape?: 'circle' | 'square';
15
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
16
+ variant?: 'solid' | 'soft' | 'outlined' | 'ghost';
17
+ disabled?: boolean;
9
18
  onCrop?: (croppedImage: string) => void;
19
+ onChange?: (file: File | null) => void;
10
20
  class?: string;
21
+ name?: string;
22
+ label?: string;
23
+ helpText?: string;
24
+ errorText?: string;
11
25
  };
12
26
  declare const ImageCropper: import("svelte").Component<Props, {}, "">;
13
27
  type ImageCropper = ReturnType<typeof ImageCropper>;
@@ -11,7 +11,8 @@
11
11
  controlClass?: string;
12
12
  onchange?: (value: string | number | undefined) => void;
13
13
  oninput?: (value: string | number | undefined) => void;
14
- variant?: 'primary' | 'secondary' | 'muted' | 'outlined' | 'line';
14
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
15
+ variant?: 'solid' | 'soft' | 'outlined' | 'line';
15
16
  size?: 'sm' | 'md' | 'lg';
16
17
  name?: string;
17
18
  label?: string;
@@ -24,7 +25,7 @@
24
25
  strong?: string;
25
26
  };
26
27
  isFloatLabel?: boolean;
27
- isSolid?: boolean;
28
+ hideStrength?: boolean;
28
29
  };
29
30
 
30
31
  let {
@@ -36,6 +37,7 @@
36
37
  onchange,
37
38
  oninput,
38
39
  variant = 'outlined',
40
+ color = 'muted',
39
41
  size = 'md',
40
42
  name,
41
43
  label,
@@ -48,23 +50,45 @@
48
50
  medium: 'Medium',
49
51
  strong: 'Strong'
50
52
  },
51
- isSolid
53
+ hideStrength
52
54
  }: Props = $props();
53
55
 
54
- const variantClasses = {
56
+ const colors = {
55
57
  primary: 'is-primary',
56
58
  secondary: 'is-secondary',
57
59
  muted: 'is-muted',
60
+ success: 'is-success',
61
+ info: 'is-info',
62
+ danger: 'is-danger',
63
+ warning: 'is-warning'
64
+ };
65
+
66
+ const variants = {
67
+ solid: 'is-solid',
68
+ soft: 'is-soft',
58
69
  outlined: 'is-outlined',
59
70
  line: 'is-line'
60
71
  };
61
72
 
73
+ const btnVariants = {
74
+ solid: 'solid',
75
+ soft: 'ghost',
76
+ outlined: 'ghost',
77
+ line: 'ghost'
78
+ };
79
+
62
80
  const sizeClasses = {
63
81
  sm: 'is-sm',
64
82
  md: 'is-md',
65
83
  lg: 'is-lg'
66
84
  };
67
85
 
86
+ const btnSizes = {
87
+ sm: 'xs',
88
+ md: 'sm',
89
+ lg: 'md'
90
+ };
91
+
68
92
  const uid = $props.id();
69
93
 
70
94
  let isActive = $state(false);
@@ -83,7 +107,8 @@
83
107
  label: string;
84
108
  color: string;
85
109
  } {
86
- if (!password) return { score: 0, label: '', color: 'bg-gray-300' };
110
+ if (hideStrength) return { score: 0, label: '', color: 'bg-muted' };
111
+ if (!password) return { score: 0, label: '', color: 'bg-muted' };
87
112
 
88
113
  let score = 0;
89
114
 
@@ -93,17 +118,17 @@
93
118
  if (/[A-Z]/.test(password)) score += 1;
94
119
  if (/\d/.test(password)) score += 1;
95
120
  if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score += 1;
96
- if (score <= 2) return { score, label: labels.weak || 'Weak', color: 'bg-red-500' };
121
+ if (score <= 2) return { score, label: labels.weak || 'Weak', color: 'bg-danger' };
97
122
  if (score <= 4)
98
123
  return {
99
124
  score,
100
125
  label: labels.medium || 'Medium',
101
- color: 'bg-yellow-500'
126
+ color: 'bg-warning'
102
127
  };
103
128
  return {
104
129
  score,
105
130
  label: labels.strong || 'Strong',
106
- color: 'bg-green-500'
131
+ color: 'bg-success'
107
132
  };
108
133
  }
109
134
  </script>
@@ -115,9 +140,9 @@
115
140
  <label
116
141
  class={cn(
117
142
  'control',
118
- variantClasses[variant],
143
+ colors[color],
144
+ variants[variant],
119
145
  sizeClasses[size],
120
- isSolid && 'is-solid',
121
146
  isFloatLabel && 'is-float',
122
147
  (isActive || isFocused) && 'is-active',
123
148
  controlClass
@@ -154,25 +179,34 @@
154
179
  onfocusout={() => (isFocused = false)}
155
180
  />
156
181
  <IconButton
157
- variant="ghost"
158
- size="sm"
182
+ size={btnSizes[size] as any}
159
183
  icon={show ? EyeShowRegularIcon : EyeOffRegularIcon}
184
+ {color}
185
+ variant={btnVariants[variant] as any}
160
186
  onclick={() => (show = !show)}
161
187
  />
162
188
  </label>
163
- <div class={cn('field-strength')}>
164
- <div class="field-strength-bars">
165
- {#each Array(4) as _, i}
166
- <div
167
- class="field-strength-bar-item"
168
- class:active={isBarActive(i, strength.score)}
169
- class:weak={strength.score <= 2 && isBarActive(i, strength.score)}
170
- class:medium={strength.score > 2 && strength.score <= 4 && isBarActive(i, strength.score)}
171
- class:strong={strength.score > 4 && isBarActive(i, strength.score)}
172
- ></div>
173
- {/each}
189
+ {#if !hideStrength}
190
+ <div class={cn('field-strength')}>
191
+ <div class="field-strength-bars">
192
+ {#each Array(4) as _, i}
193
+ <div
194
+ class="field-strength-bar-item"
195
+ class:active={isBarActive(i, strength.score)}
196
+ class:weak={strength.score <= 2 && isBarActive(i, strength.score)}
197
+ class:medium={strength.score > 2 &&
198
+ strength.score <= 4 &&
199
+ isBarActive(i, strength.score)}
200
+ class:strong={strength.score > 4 && isBarActive(i, strength.score)}
201
+ ></div>
202
+ {/each}
203
+ </div>
204
+ {#if strength.label}
205
+ <span class="field-strength-label">{strength.label}</span>
206
+ {/if}
174
207
  </div>
175
- </div>
208
+ {/if}
209
+
176
210
  {#if errorText || helpText}
177
211
  <div class={cn('field-help', errorText && 'is-danger')}>
178
212
  {errorText || helpText}
@@ -6,7 +6,8 @@ type Props = {
6
6
  controlClass?: string;
7
7
  onchange?: (value: string | number | undefined) => void;
8
8
  oninput?: (value: string | number | undefined) => void;
9
- variant?: 'primary' | 'secondary' | 'muted' | 'outlined' | 'line';
9
+ color?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'danger' | 'warning';
10
+ variant?: 'solid' | 'soft' | 'outlined' | 'line';
10
11
  size?: 'sm' | 'md' | 'lg';
11
12
  name?: string;
12
13
  label?: string;
@@ -19,8 +20,8 @@ type Props = {
19
20
  strong?: string;
20
21
  };
21
22
  isFloatLabel?: boolean;
22
- isSolid?: boolean;
23
+ hideStrength?: boolean;
23
24
  };
24
- declare const PasswordStrength: import("svelte").Component<Props, {}, "value">;
25
- type PasswordStrength = ReturnType<typeof PasswordStrength>;
26
- export default PasswordStrength;
25
+ declare const PasswordField: import("svelte").Component<Props, {}, "value">;
26
+ type PasswordField = ReturnType<typeof PasswordField>;
27
+ export default PasswordField;