ui-svelte 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/dist/charts/ArcChart.svelte +320 -0
  4. package/dist/charts/ArcChart.svelte.d.ts +26 -0
  5. package/dist/charts/AreaChart.svelte +495 -0
  6. package/dist/charts/AreaChart.svelte.d.ts +32 -0
  7. package/dist/charts/BarChart.svelte +504 -0
  8. package/dist/charts/BarChart.svelte.d.ts +38 -0
  9. package/dist/charts/Candlestick.svelte +527 -0
  10. package/dist/charts/Candlestick.svelte.d.ts +38 -0
  11. package/dist/charts/LineChart.svelte +365 -0
  12. package/dist/charts/LineChart.svelte.d.ts +36 -0
  13. package/dist/charts/PieChart.svelte +311 -0
  14. package/dist/charts/PieChart.svelte.d.ts +28 -0
  15. package/dist/charts/css/arc-chart.css +237 -0
  16. package/dist/charts/css/area-chart.css +289 -0
  17. package/dist/charts/css/bar-chart.css +167 -0
  18. package/dist/charts/css/candlestick.css +197 -0
  19. package/dist/charts/css/line-chart.css +202 -0
  20. package/dist/charts/css/pie-chart.css +199 -0
  21. package/dist/control/Audio.svelte +212 -0
  22. package/dist/control/Audio.svelte.d.ts +8 -0
  23. package/dist/control/Button.svelte +116 -0
  24. package/dist/control/Button.svelte.d.ts +22 -0
  25. package/dist/control/IconButton.svelte +104 -0
  26. package/dist/control/IconButton.svelte.d.ts +17 -0
  27. package/dist/control/Record.svelte +430 -0
  28. package/dist/control/Record.svelte.d.ts +11 -0
  29. package/dist/control/ToggleTheme.svelte +21 -0
  30. package/dist/control/ToggleTheme.svelte.d.ts +8 -0
  31. package/dist/control/Video.svelte +222 -0
  32. package/dist/control/Video.svelte.d.ts +10 -0
  33. package/dist/control/css/btn.css +206 -0
  34. package/dist/control/css/media.css +78 -0
  35. package/dist/control/css/video.css +58 -0
  36. package/dist/css/animations.css +27 -0
  37. package/dist/css/base.css +192 -0
  38. package/dist/css/utilities.css +136 -0
  39. package/dist/display/Accordion.svelte +98 -0
  40. package/dist/display/Accordion.svelte.d.ts +20 -0
  41. package/dist/display/Alert.svelte +65 -0
  42. package/dist/display/Alert.svelte.d.ts +15 -0
  43. package/dist/display/Avatar.svelte +80 -0
  44. package/dist/display/Avatar.svelte.d.ts +13 -0
  45. package/dist/display/Badge.svelte +46 -0
  46. package/dist/display/Badge.svelte.d.ts +11 -0
  47. package/dist/display/Card.svelte +94 -0
  48. package/dist/display/Card.svelte.d.ts +21 -0
  49. package/dist/display/Carousel.svelte +359 -0
  50. package/dist/display/Carousel.svelte.d.ts +25 -0
  51. package/dist/display/ChatBox.svelte +249 -0
  52. package/dist/display/ChatBox.svelte.d.ts +18 -0
  53. package/dist/display/Chip.svelte +67 -0
  54. package/dist/display/Chip.svelte.d.ts +17 -0
  55. package/dist/display/Code.svelte +56 -0
  56. package/dist/display/Code.svelte.d.ts +9 -0
  57. package/dist/display/Collapsible.svelte +71 -0
  58. package/dist/display/Collapsible.svelte.d.ts +15 -0
  59. package/dist/display/Divider.svelte +32 -0
  60. package/dist/display/Divider.svelte.d.ts +10 -0
  61. package/dist/display/Empty.svelte +462 -0
  62. package/dist/display/Empty.svelte.d.ts +11 -0
  63. package/dist/display/Icon.svelte +20 -0
  64. package/dist/display/Icon.svelte.d.ts +11 -0
  65. package/dist/display/Item.svelte +119 -0
  66. package/dist/display/Item.svelte.d.ts +24 -0
  67. package/dist/display/Loading.svelte +8 -0
  68. package/dist/display/Loading.svelte.d.ts +26 -0
  69. package/dist/display/Marquee.svelte +164 -0
  70. package/dist/display/Marquee.svelte.d.ts +21 -0
  71. package/dist/display/Section.svelte +63 -0
  72. package/dist/display/Section.svelte.d.ts +16 -0
  73. package/dist/display/Table.svelte +407 -0
  74. package/dist/display/Table.svelte.d.ts +32 -0
  75. package/dist/display/TypeWriter.svelte +23 -0
  76. package/dist/display/TypeWriter.svelte.d.ts +11 -0
  77. package/dist/display/User.svelte +0 -0
  78. package/dist/display/User.svelte.d.ts +26 -0
  79. package/dist/display/css/accordion.css +98 -0
  80. package/dist/display/css/alert.css +51 -0
  81. package/dist/display/css/avatar.css +158 -0
  82. package/dist/display/css/badge.css +47 -0
  83. package/dist/display/css/card.css +231 -0
  84. package/dist/display/css/carousel.css +156 -0
  85. package/dist/display/css/chat-box.css +188 -0
  86. package/dist/display/css/chip.css +91 -0
  87. package/dist/display/css/code.css +19 -0
  88. package/dist/display/css/collapsible.css +86 -0
  89. package/dist/display/css/divider.css +54 -0
  90. package/dist/display/css/empty.css +8 -0
  91. package/dist/display/css/item.css +149 -0
  92. package/dist/display/css/listbox.css +24 -0
  93. package/dist/display/css/marquee.css +138 -0
  94. package/dist/display/css/section.css +85 -0
  95. package/dist/display/css/table.css +361 -0
  96. package/dist/form/Checkbox.svelte +45 -0
  97. package/dist/form/Checkbox.svelte.d.ts +13 -0
  98. package/dist/form/ComboBox.svelte +448 -0
  99. package/dist/form/ComboBox.svelte.d.ts +29 -0
  100. package/dist/form/CsvField.svelte +389 -0
  101. package/dist/form/CsvField.svelte.d.ts +21 -0
  102. package/dist/form/DateField.svelte +292 -0
  103. package/dist/form/DateField.svelte.d.ts +18 -0
  104. package/dist/form/Dropzone.svelte +196 -0
  105. package/dist/form/Dropzone.svelte.d.ts +30 -0
  106. package/dist/form/ImageCropper.svelte +254 -0
  107. package/dist/form/ImageCropper.svelte.d.ts +14 -0
  108. package/dist/form/PasswordField.svelte +170 -0
  109. package/dist/form/PasswordField.svelte.d.ts +28 -0
  110. package/dist/form/PhoneField.svelte +485 -0
  111. package/dist/form/PhoneField.svelte.d.ts +25 -0
  112. package/dist/form/PinField.svelte +139 -0
  113. package/dist/form/PinField.svelte.d.ts +17 -0
  114. package/dist/form/RadioGroup.svelte +70 -0
  115. package/dist/form/RadioGroup.svelte.d.ts +19 -0
  116. package/dist/form/Select.svelte +350 -0
  117. package/dist/form/Select.svelte.d.ts +26 -0
  118. package/dist/form/Slider.svelte +60 -0
  119. package/dist/form/Slider.svelte.d.ts +15 -0
  120. package/dist/form/TextField.svelte +154 -0
  121. package/dist/form/TextField.svelte.d.ts +31 -0
  122. package/dist/form/Textarea.svelte +137 -0
  123. package/dist/form/Textarea.svelte.d.ts +27 -0
  124. package/dist/form/Toggle.svelte +45 -0
  125. package/dist/form/Toggle.svelte.d.ts +13 -0
  126. package/dist/form/css/checkbox.css +46 -0
  127. package/dist/form/css/combo-box.css +69 -0
  128. package/dist/form/css/control.css +177 -0
  129. package/dist/form/css/csv-field.css +0 -0
  130. package/dist/form/css/date.css +56 -0
  131. package/dist/form/css/dropzone.css +133 -0
  132. package/dist/form/css/field.css +17 -0
  133. package/dist/form/css/image-cropper.css +155 -0
  134. package/dist/form/css/password.css +35 -0
  135. package/dist/form/css/radio-group.css +57 -0
  136. package/dist/form/css/select.css +18 -0
  137. package/dist/form/css/slider.css +80 -0
  138. package/dist/form/css/textarea.css +130 -0
  139. package/dist/form/css/toggle.css +27 -0
  140. package/dist/form/js/countries.d.ts +13 -0
  141. package/dist/form/js/countries.js +307 -0
  142. package/dist/form/js/phone-examples.d.ts +248 -0
  143. package/dist/form/js/phone-examples.js +247 -0
  144. package/dist/hooks/use-auth.svelte.d.ts +11 -0
  145. package/dist/hooks/use-auth.svelte.js +59 -0
  146. package/dist/hooks/use-chat.svelte.d.ts +40 -0
  147. package/dist/hooks/use-chat.svelte.js +265 -0
  148. package/dist/hooks/use-clipboard.svelte.d.ts +9 -0
  149. package/dist/hooks/use-clipboard.svelte.js +52 -0
  150. package/dist/hooks/use-fetch.svelte.d.ts +11 -0
  151. package/dist/hooks/use-fetch.svelte.js +38 -0
  152. package/dist/hooks/use-form.svelte.d.ts +31 -0
  153. package/dist/hooks/use-form.svelte.js +110 -0
  154. package/dist/hooks/use-localstorage.svelte.d.ts +3 -0
  155. package/dist/hooks/use-localstorage.svelte.js +26 -0
  156. package/dist/hooks/use-scroll.svelte.d.ts +6 -0
  157. package/dist/hooks/use-scroll.svelte.js +34 -0
  158. package/dist/hooks/use-search.svelte.d.ts +49 -0
  159. package/dist/hooks/use-search.svelte.js +229 -0
  160. package/dist/hooks/use-table.svelte.d.ts +85 -0
  161. package/dist/hooks/use-table.svelte.js +362 -0
  162. package/dist/hooks/use-websocket.svelte.d.ts +18 -0
  163. package/dist/hooks/use-websocket.svelte.js +79 -0
  164. package/dist/icons/index.d.ts +132 -0
  165. package/dist/icons/index.js +132 -0
  166. package/dist/index.css +115 -0
  167. package/dist/index.d.ts +76 -0
  168. package/dist/index.js +76 -0
  169. package/dist/layout/AppBar.svelte +94 -0
  170. package/dist/layout/AppBar.svelte.d.ts +17 -0
  171. package/dist/layout/Footer.svelte +94 -0
  172. package/dist/layout/Footer.svelte.d.ts +17 -0
  173. package/dist/layout/FooterLinks.svelte +28 -0
  174. package/dist/layout/FooterLinks.svelte.d.ts +11 -0
  175. package/dist/layout/Provider.svelte +52 -0
  176. package/dist/layout/Provider.svelte.d.ts +10 -0
  177. package/dist/layout/Scaffold.svelte +46 -0
  178. package/dist/layout/Scaffold.svelte.d.ts +15 -0
  179. package/dist/layout/Sidebar.svelte +40 -0
  180. package/dist/layout/Sidebar.svelte.d.ts +13 -0
  181. package/dist/layout/css/app-bar.css +35 -0
  182. package/dist/layout/css/bottom-bar.css +12 -0
  183. package/dist/layout/css/footer-links.css +17 -0
  184. package/dist/layout/css/footer.css +35 -0
  185. package/dist/layout/css/scaffold.css +15 -0
  186. package/dist/layout/css/sidebar.css +17 -0
  187. package/dist/navigation/BottomNav.svelte +0 -0
  188. package/dist/navigation/BottomNav.svelte.d.ts +26 -0
  189. package/dist/navigation/NavMenu.svelte +254 -0
  190. package/dist/navigation/SideNav.svelte +249 -0
  191. package/dist/navigation/Tabs.svelte +79 -0
  192. package/dist/navigation/Tabs.svelte.d.ts +19 -0
  193. package/dist/navigation/css/bottom-nav.css +0 -0
  194. package/dist/navigation/css/nav-menu.css +168 -0
  195. package/dist/navigation/css/side-nav.css +244 -0
  196. package/dist/navigation/css/tabs.css +118 -0
  197. package/dist/overlay/AlertDialog.svelte +0 -0
  198. package/dist/overlay/AlertDialog.svelte.d.ts +26 -0
  199. package/dist/overlay/Command.svelte +0 -0
  200. package/dist/overlay/Command.svelte.d.ts +26 -0
  201. package/dist/overlay/Drawer.svelte +129 -0
  202. package/dist/overlay/Drawer.svelte.d.ts +20 -0
  203. package/dist/overlay/Dropdown.svelte +140 -0
  204. package/dist/overlay/Modal.svelte +102 -0
  205. package/dist/overlay/Modal.svelte.d.ts +19 -0
  206. package/dist/overlay/PopoverStack.svelte +0 -0
  207. package/dist/overlay/PopoverStack.svelte.d.ts +26 -0
  208. package/dist/overlay/Toast.svelte +83 -0
  209. package/dist/overlay/Toast.svelte.d.ts +9 -0
  210. package/dist/overlay/Tooltip.svelte +140 -0
  211. package/dist/overlay/Tooltip.svelte.d.ts +12 -0
  212. package/dist/overlay/css/drawer.css +75 -0
  213. package/dist/overlay/css/dropdown.css +24 -0
  214. package/dist/overlay/css/hovercard.css +11 -0
  215. package/dist/overlay/css/modal.css +51 -0
  216. package/dist/overlay/css/toast.css +80 -0
  217. package/dist/overlay/css/tooltip.css +89 -0
  218. package/dist/stores/i18n.svelte.d.ts +16 -0
  219. package/dist/stores/i18n.svelte.js +137 -0
  220. package/dist/stores/theme.svelte.d.ts +5 -0
  221. package/dist/stores/theme.svelte.js +55 -0
  222. package/dist/stores/toast.svelte.d.ts +19 -0
  223. package/dist/stores/toast.svelte.js +38 -0
  224. package/dist/types.d.ts +75 -0
  225. package/dist/types.js +1 -0
  226. package/dist/utils/charts.d.ts +27 -0
  227. package/dist/utils/charts.js +140 -0
  228. package/dist/utils/class-names.d.ts +1 -0
  229. package/dist/utils/class-names.js +3 -0
  230. package/dist/utils/click-outside.d.ts +3 -0
  231. package/dist/utils/click-outside.js +9 -0
  232. package/dist/utils/popover.d.ts +3 -0
  233. package/dist/utils/popover.js +17 -0
  234. package/dist/utils/ulid.d.ts +1 -0
  235. package/dist/utils/ulid.js +22 -0
  236. package/dist/utils/validate-schema.d.ts +2 -0
  237. package/dist/utils/validate-schema.js +97 -0
  238. package/package.json +69 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 SappsJs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # SappsUI
2
+
3
+ **A modern, accessible UI component library built for Svelte 5**
4
+
5
+ SappsUI is a comprehensive UI component library designed to leverage Svelte 5's latest features. Build beautiful, accessible interfaces with minimal configuration and maximum developer experience.
6
+
7
+ ---
8
+
9
+ ## ✨ Features
10
+
11
+ ### 🎨 Modern Design System
12
+
13
+ Beautiful, pre-styled components with multiple variants and customization options. Choose from primary, secondary, soft, outlined, and ghost variants to match your design needs.
14
+
15
+ ### 🎯 Built for Svelte 5
16
+
17
+ Fully optimized for Svelte 5's runes system with `$state`, `$derived`, and snippets. Experience the full power of Svelte's reactivity with a modern component API.
18
+
19
+ ### 🔧 Highly Customizable
20
+
21
+ Every component accepts custom classes and styling props. Customize individual sections with dedicated class props for headers, bodies, footers, and more.
22
+
23
+ ### 📱 Responsive Design
24
+
25
+ Mobile-first design approach with responsive sizing options (xs, sm, md, lg, xl). Components adapt seamlessly to different screen sizes and devices.
26
+
27
+ ### ♿ Accessibility First
28
+
29
+ Built with accessibility in mind, following WCAG guidelines. Proper ARIA attributes, keyboard navigation, and semantic HTML ensure your apps are usable by everyone.
30
+
31
+ ### 🎭 Rich Component Variants
32
+
33
+ Multiple style variants for every component including primary, secondary, soft, outlined, ghost, and semantic variants (success, info, warning, danger) for contextual feedback.
34
+
35
+ ### 🖼️ Icon Integration
36
+
37
+ Seamless integration with Iconify for thousands of icon options. Add icons to buttons, text fields, and other components with a simple prop.
38
+
39
+ ### 📝 Form Components
40
+
41
+ Comprehensive form component suite with built-in validation, error states, help text, and floating labels. Build complex forms with ease.
42
+
43
+ ### 🎪 Interactive Elements
44
+
45
+ Rich interactive components including tabs, cards, selects, and more. Create dynamic user interfaces with minimal code.
46
+
47
+ ### 🎨 Theming Support
48
+
49
+ Flexible theming system with support for custom color schemes. Adapt components to match your brand identity effortlessly.
50
+
51
+ ### 📦 TypeScript Support
52
+
53
+ Full TypeScript support with comprehensive type definitions. Enjoy autocomplete, type checking, and compile-time error detection throughout your development process.
54
+
55
+ ### 🚀 Lightweight & Fast
56
+
57
+ Optimized bundle size with tree-shaking support. Only import what you need for blazing-fast load times and minimal overhead.
58
+
59
+ ---
60
+
61
+ ## 📦 Installation
62
+
63
+ ```bash
64
+ # or
65
+ bun add sappsui
66
+ ```
67
+
68
+ ---
69
+
70
+ ## 🚀 Quick Start
71
+
72
+ ```svelte
73
+ <script lang="ts">
74
+ import { Button, Card, TextField } from 'sappsui';
75
+
76
+ let name = $state('');
77
+ </script>
78
+
79
+ <Card>
80
+ <h2>Welcome to SappsUI</h2>
81
+
82
+ <TextField name="username" label="Your Name" placeholder="Enter your name..." bind:value={name} />
83
+
84
+ <Button label="Submit" variant="primary" onclick={() => console.log('Hello', name)} />
85
+ </Card>
86
+ ```
87
+
88
+ ---
89
+
90
+ ## 📖 Documentation
91
+
92
+ For detailed documentation, examples, and interactive component previews, visit our [official documentation](https://ui.sappsdev.com).
93
+
94
+ ---
95
+
96
+ ## 🎨 Component Library
97
+
98
+ SappsUI includes a comprehensive set of components for building modern web applications:
99
+
100
+ - **Buttons & Actions**: Button, IconButton
101
+ - **Forms**: TextField, Select, Checkbox
102
+ - **Layout**: Card, Divider, Section
103
+ - **Navigation**: Tabs
104
+ - **Data Display**: Avatar, Code
105
+ - **Feedback**: Status indicators, Error states
106
+ - **And more...**
107
+
108
+ ---
109
+
110
+ ## 🌟 Support
111
+
112
+ If you find SappsUI helpful, please consider giving it a star on GitHub! Your support helps us continue improving the library.
113
+
114
+ For questions, issues, or feature requests, please visit our [GitHub Issues](https://github.com/sappsdev/sappsui_svelte/issues) page.
115
+
116
+ ---
117
+
118
+ **Built with ❤️ by the SappsDev Team**
@@ -0,0 +1,320 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils/class-names.js';
3
+ import { onMount, untrack } from 'svelte';
4
+
5
+ type Color = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'muted';
6
+
7
+ type ArcData = {
8
+ value: number;
9
+ max?: number;
10
+ color: Color;
11
+ label?: string;
12
+ };
13
+
14
+ type Props = {
15
+ data?: ArcData[];
16
+ centerText?: string;
17
+ centerValue?: string | number;
18
+ thickness?: number;
19
+ gap?: number;
20
+ animated?: boolean;
21
+ animationDuration?: number;
22
+ loading?: boolean;
23
+ empty?: boolean;
24
+ emptyText?: string;
25
+ showLegend?: boolean;
26
+ showValues?: boolean;
27
+ class?: string;
28
+ chartClass?: string;
29
+ };
30
+
31
+ let {
32
+ data = [],
33
+ centerText = '',
34
+ centerValue = '',
35
+ thickness = 16,
36
+ gap = 8,
37
+ animated = true,
38
+ animationDuration = 1000,
39
+ loading = false,
40
+ empty = false,
41
+ emptyText = 'No data',
42
+ showLegend = true,
43
+ showValues = true,
44
+ class: className,
45
+ chartClass
46
+ }: Props = $props();
47
+
48
+ const colorClass = {
49
+ primary: 'is-primary',
50
+ secondary: 'is-secondary',
51
+ success: 'is-success',
52
+ info: 'is-info',
53
+ warning: 'is-warning',
54
+ danger: 'is-danger',
55
+ muted: 'is-muted'
56
+ };
57
+
58
+ let containerEl: HTMLDivElement | undefined = $state();
59
+ let displayPercentages = $state<number[]>(data.map(() => 0));
60
+ let animationFrameId: number | null = null;
61
+
62
+ let tooltipData = $state<(ArcData & { percentage: number }) | null>(null);
63
+ let tooltipPosition = $state<{ x: number; y: number }>({ x: 0, y: 0 });
64
+ let isTooltipActive = $state(false);
65
+
66
+ let containerSize = $state({ width: 0, height: 0 });
67
+ let width = $derived(containerSize.width);
68
+ let height = $derived(containerSize.height);
69
+ let size = $derived(Math.min(containerSize.width, containerSize.height));
70
+ let viewBoxSize = $derived(size);
71
+ let center = $derived(viewBoxSize / 2);
72
+
73
+ let responsiveThickness = $derived(() => {
74
+ const baseSize = 300;
75
+ const scale = size / baseSize;
76
+ return Math.max(8, Math.min(thickness * scale, thickness));
77
+ });
78
+
79
+ let responsiveGap = $derived(() => {
80
+ const baseSize = 300;
81
+ const scale = size / baseSize;
82
+ return Math.max(4, Math.min(gap * scale, gap));
83
+ });
84
+
85
+ let maxRadius = $derived(center - 20);
86
+
87
+ let responsiveLabelFontSize = $derived(() => {
88
+ const baseSize = 300;
89
+ const scale = size / baseSize;
90
+ return Math.max(10, Math.min(14 * scale, 14));
91
+ });
92
+
93
+ let responsiveValueFontSize = $derived(() => {
94
+ const baseSize = 300;
95
+ const scale = size / baseSize;
96
+ return Math.max(16, Math.min(32 * scale, 32));
97
+ });
98
+
99
+ let shouldShowText = $derived(size >= 120);
100
+
101
+ let computedCenterValue = $derived(centerValue || data.reduce((sum, arc) => sum + arc.value, 0));
102
+
103
+ let totalMax = $derived(() => {
104
+ if (data.length === 0) return 0;
105
+ if (data.length === 1 && data[0].max !== undefined) {
106
+ return data[0].max;
107
+ }
108
+ return data.reduce((sum, arc) => sum + (arc.max || arc.value), 0);
109
+ });
110
+
111
+ onMount(() => {
112
+ const updateSize = () => {
113
+ if (containerEl) {
114
+ const rect = containerEl.getBoundingClientRect();
115
+ containerSize = { width: rect.width, height: rect.height };
116
+ }
117
+ };
118
+
119
+ updateSize();
120
+ const resizeObserver = new ResizeObserver(updateSize);
121
+ if (containerEl) {
122
+ resizeObserver.observe(containerEl);
123
+ }
124
+
125
+ return () => {
126
+ if (animationFrameId !== null) {
127
+ cancelAnimationFrame(animationFrameId);
128
+ }
129
+ resizeObserver.disconnect();
130
+ };
131
+ });
132
+
133
+ $effect(() => {
134
+ const total = totalMax();
135
+ const targetPercentages = data.map((arc) => {
136
+ const max = data.length === 1 && arc.max !== undefined ? arc.max : total;
137
+ return Math.min(Math.max(arc.value / max, 0), 1);
138
+ });
139
+
140
+ if (animationFrameId !== null) {
141
+ cancelAnimationFrame(animationFrameId);
142
+ animationFrameId = null;
143
+ }
144
+
145
+ if (animated) {
146
+ const startTime = Date.now();
147
+ const startPercentages = untrack(() => [...displayPercentages]);
148
+
149
+ const animate = () => {
150
+ const now = Date.now();
151
+ const progress = Math.min((now - startTime) / animationDuration, 1);
152
+ const easeProgress = 1 - Math.pow(1 - progress, 3);
153
+
154
+ const newPercentages = startPercentages.map(
155
+ (start, i) => start + (targetPercentages[i] - start) * easeProgress
156
+ );
157
+
158
+ displayPercentages = newPercentages;
159
+
160
+ if (progress < 1) {
161
+ animationFrameId = requestAnimationFrame(animate);
162
+ } else {
163
+ animationFrameId = null;
164
+ }
165
+ };
166
+
167
+ animationFrameId = requestAnimationFrame(animate);
168
+ } else {
169
+ displayPercentages = targetPercentages;
170
+ }
171
+ });
172
+
173
+ function handleArcHover(arc: ArcData, percentage: number, event: MouseEvent): void {
174
+ const target = event.target as SVGCircleElement;
175
+ const rect = target.getBoundingClientRect();
176
+
177
+ const max = data.length === 1 && arc.max !== undefined ? arc.max : totalMax();
178
+
179
+ tooltipData = { ...arc, max, percentage: percentage * 100 };
180
+ tooltipPosition = {
181
+ x: rect.left + rect.width / 2,
182
+ y: rect.top - 10
183
+ };
184
+ isTooltipActive = true;
185
+ }
186
+
187
+ function handleArcLeave(): void {
188
+ isTooltipActive = false;
189
+ setTimeout(() => {
190
+ if (!isTooltipActive) {
191
+ tooltipData = null;
192
+ }
193
+ }, 100);
194
+ }
195
+ </script>
196
+
197
+ <div class={cn('arc-chart-container', className)}>
198
+ {#if loading}
199
+ <div class="arc-chart-loading">
200
+ <svg class="arc-chart-loading-spinner" viewBox="0 0 24 24">
201
+ <circle
202
+ cx="12"
203
+ cy="12"
204
+ r="10"
205
+ stroke="currentColor"
206
+ stroke-width="4"
207
+ fill="none"
208
+ opacity="0.25"
209
+ />
210
+ <path
211
+ fill="currentColor"
212
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
213
+ />
214
+ </svg>
215
+ </div>
216
+ {:else if empty}
217
+ <div class="arc-chart-empty">
218
+ <span>{emptyText}</span>
219
+ </div>
220
+ {:else}
221
+ <div bind:this={containerEl} class={cn('arc-chart', chartClass)}>
222
+ <svg class="arc-chart-svg" viewBox="0 0 {viewBoxSize} {viewBoxSize}">
223
+ {#each data as arc, i}
224
+ {@const currentThickness = responsiveThickness()}
225
+ {@const currentGap = responsiveGap()}
226
+ {@const radius = maxRadius - i * (currentThickness + currentGap)}
227
+ {@const percentage = displayPercentages[i]}
228
+ {@const circumference = 2 * Math.PI * radius}
229
+ {@const strokeDashoffset = circumference * (1 - percentage)}
230
+
231
+ <circle
232
+ cx={center}
233
+ cy={center}
234
+ r={radius}
235
+ class="arc-chart-background"
236
+ stroke-width={currentThickness}
237
+ />
238
+
239
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
240
+ <circle
241
+ cx={center}
242
+ cy={center}
243
+ r={radius}
244
+ class="arc-chart-arc is-{arc.color}"
245
+ stroke-width={currentThickness}
246
+ style="
247
+ stroke-dasharray: {circumference};
248
+ stroke-dashoffset: {strokeDashoffset};
249
+ transform: rotate(-90deg);
250
+ transform-origin: center;
251
+ "
252
+ onmouseenter={(e) => handleArcHover(arc, percentage, e)}
253
+ onmouseleave={handleArcLeave}
254
+ />
255
+ {/each}
256
+
257
+ {#if (centerText || computedCenterValue) && shouldShowText}
258
+ <text
259
+ x={center}
260
+ y={center - 10}
261
+ class="arc-chart-label"
262
+ text-anchor="middle"
263
+ dominant-baseline="middle"
264
+ style="font-size: {responsiveLabelFontSize()}px; font-weight: 500;"
265
+ >
266
+ {centerText}
267
+ </text>
268
+ <text
269
+ x={center}
270
+ y={center + 15}
271
+ class="arc-chart-value"
272
+ text-anchor="middle"
273
+ dominant-baseline="middle"
274
+ style="font-size: {responsiveValueFontSize()}px;"
275
+ >
276
+ {computedCenterValue}
277
+ </text>
278
+ {/if}
279
+ </svg>
280
+ </div>
281
+
282
+ {#if tooltipData && isTooltipActive}
283
+ <div
284
+ class="arc-chart-tooltip"
285
+ style="left: {tooltipPosition.x}px; top: {tooltipPosition.y}px;"
286
+ >
287
+ <div class="arc-chart-tooltip-content">
288
+ {#if tooltipData.label}
289
+ <div class="arc-chart-tooltip-title">{tooltipData.label}</div>
290
+ {/if}
291
+ <div class="arc-chart-tooltip-row">
292
+ <div class="arc-chart-tooltip-color is-{tooltipData.color}"></div>
293
+ <span class="arc-chart-tooltip-value">
294
+ {tooltipData.value} / {tooltipData.max} ({tooltipData.percentage.toFixed(1)}%)
295
+ </span>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ {/if}
300
+
301
+ {#if showLegend}
302
+ <div class="arc-chart-legend">
303
+ {#each data as arc, i}
304
+ {@const percentage = displayPercentages[i]}
305
+ {@const max = data.length === 1 && arc.max !== undefined ? arc.max : totalMax()}
306
+ <div class="arc-chart-legend-item">
307
+ <div class={cn('arc-chart-legend-color', colorClass[arc.color])}></div>
308
+ <span>{arc.label || `Arc ${i + 1}`}</span>
309
+ {#if showValues}
310
+ <span class="arc-chart-legend-value">
311
+ ({arc.value}{#if max}
312
+ / {max}{/if})
313
+ </span>
314
+ {/if}
315
+ </div>
316
+ {/each}
317
+ </div>
318
+ {/if}
319
+ {/if}
320
+ </div>
@@ -0,0 +1,26 @@
1
+ type Color = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'muted';
2
+ type ArcData = {
3
+ value: number;
4
+ max?: number;
5
+ color: Color;
6
+ label?: string;
7
+ };
8
+ type Props = {
9
+ data?: ArcData[];
10
+ centerText?: string;
11
+ centerValue?: string | number;
12
+ thickness?: number;
13
+ gap?: number;
14
+ animated?: boolean;
15
+ animationDuration?: number;
16
+ loading?: boolean;
17
+ empty?: boolean;
18
+ emptyText?: string;
19
+ showLegend?: boolean;
20
+ showValues?: boolean;
21
+ class?: string;
22
+ chartClass?: string;
23
+ };
24
+ declare const ArcChart: import("svelte").Component<Props, {}, "">;
25
+ type ArcChart = ReturnType<typeof ArcChart>;
26
+ export default ArcChart;