@bagelink/vue 1.15.63 → 1.15.65

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 (136) hide show
  1. package/dist/components/AccordionItem.vue.d.ts.map +1 -1
  2. package/dist/components/Avatar.vue.d.ts +6 -1
  3. package/dist/components/Avatar.vue.d.ts.map +1 -1
  4. package/dist/components/Badge.vue.d.ts.map +1 -1
  5. package/dist/components/Card.vue.d.ts +7 -0
  6. package/dist/components/Card.vue.d.ts.map +1 -1
  7. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  8. package/dist/components/EmptyState.vue.d.ts +43 -0
  9. package/dist/components/EmptyState.vue.d.ts.map +1 -0
  10. package/dist/components/Icon/Icon.vue.d.ts +13 -0
  11. package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
  12. package/dist/components/Image.vue.d.ts +26 -1
  13. package/dist/components/Image.vue.d.ts.map +1 -1
  14. package/dist/components/ListItem.vue.d.ts +9 -9
  15. package/dist/components/ListItem.vue.d.ts.map +1 -1
  16. package/dist/components/Menu.vue.d.ts.map +1 -1
  17. package/dist/components/Swiper.vue.d.ts +3 -3
  18. package/dist/components/calendar/CalendarPopover.vue.d.ts +10 -0
  19. package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
  20. package/dist/components/charts/BarChart.vue.d.ts +34 -0
  21. package/dist/components/charts/BarChart.vue.d.ts.map +1 -0
  22. package/dist/components/charts/ChartTooltip.vue.d.ts +33 -0
  23. package/dist/components/charts/ChartTooltip.vue.d.ts.map +1 -0
  24. package/dist/components/charts/Donut.vue.d.ts +53 -0
  25. package/dist/components/charts/Donut.vue.d.ts.map +1 -0
  26. package/dist/components/charts/Funnel.vue.d.ts +53 -0
  27. package/dist/components/charts/Funnel.vue.d.ts.map +1 -0
  28. package/dist/components/charts/Gauge.vue.d.ts +28 -0
  29. package/dist/components/charts/Gauge.vue.d.ts.map +1 -0
  30. package/dist/components/charts/LineChart.vue.d.ts +37 -0
  31. package/dist/components/charts/LineChart.vue.d.ts.map +1 -0
  32. package/dist/components/charts/RadialBars.vue.d.ts +34 -0
  33. package/dist/components/charts/RadialBars.vue.d.ts.map +1 -0
  34. package/dist/components/charts/RankBars.vue.d.ts +27 -0
  35. package/dist/components/charts/RankBars.vue.d.ts.map +1 -0
  36. package/dist/components/charts/Sparkline.vue.d.ts +25 -0
  37. package/dist/components/charts/Sparkline.vue.d.ts.map +1 -0
  38. package/dist/components/charts/StatCard.vue.d.ts +28 -0
  39. package/dist/components/charts/StatCard.vue.d.ts.map +1 -0
  40. package/dist/components/charts/core/data.d.ts +46 -0
  41. package/dist/components/charts/core/data.d.ts.map +1 -0
  42. package/dist/components/charts/core/format.d.ts +13 -0
  43. package/dist/components/charts/core/format.d.ts.map +1 -0
  44. package/dist/components/charts/core/palette.d.ts +19 -0
  45. package/dist/components/charts/core/palette.d.ts.map +1 -0
  46. package/dist/components/charts/core/uid.d.ts +2 -0
  47. package/dist/components/charts/core/uid.d.ts.map +1 -0
  48. package/dist/components/charts/core/useChartAnim.d.ts +11 -0
  49. package/dist/components/charts/core/useChartAnim.d.ts.map +1 -0
  50. package/dist/components/charts/core/useChartFrame.d.ts +21 -0
  51. package/dist/components/charts/core/useChartFrame.d.ts.map +1 -0
  52. package/dist/components/charts/core/useScale.d.ts +16 -0
  53. package/dist/components/charts/core/useScale.d.ts.map +1 -0
  54. package/dist/components/charts/index.d.ts +12 -0
  55. package/dist/components/charts/index.d.ts.map +1 -0
  56. package/dist/components/form/inputs/RadioGroup.vue.d.ts +1 -0
  57. package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
  58. package/dist/components/form/inputs/RangeInput.vue.d.ts +13 -4
  59. package/dist/components/form/inputs/RangeInput.vue.d.ts.map +1 -1
  60. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  61. package/dist/components/index.d.ts +3 -1
  62. package/dist/components/index.d.ts.map +1 -1
  63. package/dist/components/layout/Layout.vue.d.ts +1 -1
  64. package/dist/components/layout/Layout.vue.d.ts.map +1 -1
  65. package/dist/components/layout/Panel.vue.d.ts +1 -1
  66. package/dist/components/layout/Panel.vue.d.ts.map +1 -1
  67. package/dist/components/layout/Timeline.types.d.ts +9 -0
  68. package/dist/components/layout/Timeline.types.d.ts.map +1 -0
  69. package/dist/components/layout/Timeline.vue.d.ts +42 -0
  70. package/dist/components/layout/Timeline.vue.d.ts.map +1 -0
  71. package/dist/components/layout/TimelineItem.vue.d.ts +37 -0
  72. package/dist/components/layout/TimelineItem.vue.d.ts.map +1 -0
  73. package/dist/components/layout/index.d.ts +3 -0
  74. package/dist/components/layout/index.d.ts.map +1 -1
  75. package/dist/dialog/Dialog.vue.d.ts +4 -0
  76. package/dist/dialog/Dialog.vue.d.ts.map +1 -1
  77. package/dist/index.cjs +110 -116
  78. package/dist/index.mjs +38059 -37009
  79. package/dist/style.css +1 -1
  80. package/package.json +2 -1
  81. package/src/components/AccordionItem.vue +24 -22
  82. package/src/components/Avatar.vue +49 -11
  83. package/src/components/Badge.vue +4 -7
  84. package/src/components/Card.vue +32 -2
  85. package/src/components/Dropdown.vue +14 -3
  86. package/src/components/EmptyState.vue +91 -0
  87. package/src/components/Icon/Icon.vue +118 -25
  88. package/src/components/Image.vue +70 -3
  89. package/src/components/ListItem.vue +43 -22
  90. package/src/components/Menu.vue +10 -2
  91. package/src/components/charts/BarChart.vue +197 -0
  92. package/src/components/charts/ChartTooltip.vue +74 -0
  93. package/src/components/charts/Donut.vue +219 -0
  94. package/src/components/charts/Funnel.vue +377 -0
  95. package/src/components/charts/Gauge.vue +90 -0
  96. package/src/components/charts/LineChart.vue +255 -0
  97. package/src/components/charts/RadialBars.vue +99 -0
  98. package/src/components/charts/RankBars.vue +72 -0
  99. package/src/components/charts/Sparkline.vue +90 -0
  100. package/src/components/charts/StatCard.vue +84 -0
  101. package/src/components/charts/core/data.ts +95 -0
  102. package/src/components/charts/core/format.ts +64 -0
  103. package/src/components/charts/core/palette.ts +52 -0
  104. package/src/components/charts/core/uid.ts +6 -0
  105. package/src/components/charts/core/useChartAnim.ts +60 -0
  106. package/src/components/charts/core/useChartFrame.ts +49 -0
  107. package/src/components/charts/core/useScale.ts +39 -0
  108. package/src/components/charts/index.ts +12 -0
  109. package/src/components/form/inputs/RadioGroup.vue +2 -1
  110. package/src/components/form/inputs/RangeInput.vue +43 -15
  111. package/src/components/form/inputs/SelectInput.vue +1 -19
  112. package/src/components/index.ts +3 -1
  113. package/src/components/layout/Timeline.types.ts +9 -0
  114. package/src/components/layout/Timeline.vue +54 -0
  115. package/src/components/layout/TimelineItem.vue +93 -0
  116. package/src/components/layout/index.ts +3 -0
  117. package/src/dialog/Dialog.vue +29 -1
  118. package/src/styles/bagel.css +1 -0
  119. package/src/styles/gradients.css +181 -0
  120. package/src/styles/layout.css +9 -0
  121. package/src/styles/theme.css +1 -1
  122. package/dist/components/analytics/BarChart.vue.d.ts +0 -47
  123. package/dist/components/analytics/BarChart.vue.d.ts.map +0 -1
  124. package/dist/components/analytics/KpiCard.vue.d.ts +0 -24
  125. package/dist/components/analytics/KpiCard.vue.d.ts.map +0 -1
  126. package/dist/components/analytics/LineChart.vue.d.ts +0 -35
  127. package/dist/components/analytics/LineChart.vue.d.ts.map +0 -1
  128. package/dist/components/analytics/PieChart.vue.d.ts +0 -53
  129. package/dist/components/analytics/PieChart.vue.d.ts.map +0 -1
  130. package/dist/components/analytics/index.d.ts +0 -5
  131. package/dist/components/analytics/index.d.ts.map +0 -1
  132. package/src/components/analytics/BarChart.vue +0 -262
  133. package/src/components/analytics/KpiCard.vue +0 -84
  134. package/src/components/analytics/LineChart.vue +0 -357
  135. package/src/components/analytics/PieChart.vue +0 -544
  136. package/src/components/analytics/index.ts +0 -4
@@ -1,544 +0,0 @@
1
- <script setup lang="ts">
2
- import { Icon } from '@bagelink/vue'
3
- import { computed, ref, onMounted, onUnmounted } from 'vue'
4
-
5
- interface PieData {
6
- label: string
7
- value: number
8
- color?: string
9
- }
10
-
11
- interface Props {
12
- data: PieData[]
13
- title?: string
14
- icon?: string
15
- color?: string
16
- colors?: string[]
17
- size?: number
18
- showLegend?: boolean
19
- donut?: boolean
20
- thickness?: number
21
- centerLabel?: string
22
- animated?: boolean
23
- animationDuration?: number
24
- animationDelay?: number
25
- animationStartDelay?: number
26
- centerValue?: number
27
- showCenterTotal?: boolean
28
- percentageChange?: number
29
- showLabelsOnChart?: boolean
30
- showConnectorLines?: boolean
31
- labelColor?: string
32
- }
33
-
34
- const props = withDefaults(defineProps<Props>(), {
35
- title: 'Pie Chart',
36
- icon: 'bx-pie-chart-alt',
37
- color: '#3B82F6',
38
- size: 200,
39
- showLegend: true,
40
- donut: false,
41
- thickness: 0.6,
42
- animated: true,
43
- animationDuration: 1500,
44
- animationDelay: 200,
45
- animationStartDelay: 0,
46
- centerLabel: 'Total',
47
- showCenterTotal: true,
48
- percentageChange: 0,
49
- showLabelsOnChart: false,
50
- showConnectorLines: false,
51
- labelColor: '#333'
52
- })
53
-
54
- // Generate colors with different opacity based on the main color OR use custom colors
55
- const colors = computed(() => {
56
- // If custom colors array is provided, use it
57
- if (props.colors && props.colors.length > 0) {
58
- return props.colors
59
- }
60
-
61
- // Otherwise, use the existing logic with opacity variations
62
- const baseColor = props.color
63
- const opacities = [1, 0.8, 0.6, 0.4, 0.85, 0.7, 0.5, 0.3, 0.9, 0.75]
64
-
65
- return opacities.map((opacity) => {
66
- // Convert hex to rgba with opacity
67
- const hex = baseColor.replace('#', '')
68
- const r = Number.parseInt(hex.substr(0, 2), 16)
69
- const g = Number.parseInt(hex.substr(2, 2), 16)
70
- const b = Number.parseInt(hex.substr(4, 2), 16)
71
-
72
- return `rgba(${r}, ${g}, ${b}, ${opacity})`
73
- })
74
- })
75
-
76
- // Animation state
77
- const animatedProgress = ref(0)
78
- const isAnimating = ref(false)
79
- const chartRef = ref<HTMLElement>()
80
- const isInView = ref(false)
81
-
82
- onMounted(() => {
83
- if (props.animated) {
84
- setupIntersectionObserver()
85
- } else {
86
- animatedProgress.value = 1
87
- }
88
- })
89
-
90
- let observer: IntersectionObserver | null = null
91
-
92
- onUnmounted(() => {
93
- if (observer) {
94
- observer.disconnect()
95
- }
96
- })
97
-
98
- function setupIntersectionObserver() {
99
- if (typeof window === 'undefined') { return }
100
-
101
- observer = new IntersectionObserver(
102
- (entries) => {
103
- entries.forEach((entry) => {
104
- if (entry.isIntersecting && !isInView.value) {
105
- isInView.value = true
106
- // Add start delay + small delay to ensure the component is fully rendered
107
- setTimeout(() => {
108
- startAnimation()
109
- }, 100 + props.animationStartDelay)
110
- }
111
- })
112
- },
113
- {
114
- threshold: 0.3, // Start animation when 30% of the component is visible
115
- rootMargin: '50px'
116
- }
117
- )
118
-
119
- // Use nextTick to ensure chartRef is available
120
- if (chartRef.value) {
121
- observer.observe(chartRef.value)
122
- } else {
123
- // Retry after a short delay if ref is not ready
124
- setTimeout(() => {
125
- if (chartRef.value && observer) {
126
- observer.observe(chartRef.value)
127
- }
128
- }, 100)
129
- }
130
- }
131
-
132
- function startAnimation() {
133
- const delayType = props.animationDelay > 0 ? 'staggered with delay' : 'quick stagger'
134
- console.log(`Starting animation for "${props.title}" - ${delayType} mode (duration: ${props.animationDuration}ms, start delay: ${props.animationStartDelay}ms)`)
135
- isAnimating.value = true
136
- animatedProgress.value = 0
137
-
138
- const startTime = Date.now()
139
-
140
- const animate = () => {
141
- const elapsed = Date.now() - startTime
142
- const progress = Math.min(elapsed / props.animationDuration, 1)
143
-
144
- // Easing function for smooth animation
145
- animatedProgress.value = easeOutCubic(progress)
146
-
147
- if (progress < 1) {
148
- requestAnimationFrame(animate)
149
- } else {
150
- isAnimating.value = false
151
- console.log('Animation completed')
152
- }
153
- }
154
-
155
- requestAnimationFrame(animate)
156
- }
157
-
158
- function easeOutCubic(t: number): number {
159
- return 1 - (1 - t) ** 3
160
- }
161
-
162
- // Expose animation control methods
163
- function restartAnimation() {
164
- if (props.animated) {
165
- startAnimation()
166
- }
167
- }
168
-
169
- defineExpose({
170
- restartAnimation
171
- })
172
-
173
- // Calculate SVG size based on connector lines
174
- const svgSize = computed(() => {
175
- if (props.showConnectorLines) {
176
- return {
177
- width: props.size + 200, // Increased even more
178
- height: props.size + 100 // Increased even more
179
- }
180
- }
181
- return {
182
- width: props.size,
183
- height: props.size
184
- }
185
- })
186
-
187
- // Calculate center offset for connector lines
188
- const centerOffset = computed(() => {
189
- if (props.showConnectorLines) {
190
- return {
191
- x: svgSize.value.width / 2 - 10, // Less offset
192
- y: svgSize.value.height / 2
193
- }
194
- }
195
- return {
196
- x: props.size / 2,
197
- y: props.size / 2
198
- }
199
- })
200
-
201
- const totalValue = computed(() => props.data.reduce((sum, item) => sum + item.value, 0)
202
- )
203
-
204
- // Calculate label positions for segments
205
- function getLabelPosition(segment: any) {
206
- const midAngle = (segment.startAngle + segment.endAngle) / 2
207
- const midAngleRad = (midAngle * Math.PI) / 180
208
- const radius = props.size / 2 - 10
209
- const labelRadius = props.donut ? radius * 0.8 : radius * 0.7
210
-
211
- return {
212
- x: Math.cos(midAngleRad) * labelRadius,
213
- y: Math.sin(midAngleRad) * labelRadius
214
- }
215
- }
216
-
217
- // Calculate connector line positions
218
- function getConnectorLine(segment: any) {
219
- const midAngle = (segment.startAngle + segment.endAngle) / 2
220
- const midAngleRad = (midAngle * Math.PI) / 180
221
- const radius = props.size / 2 - 10
222
- const innerRadius = props.donut ? radius * props.thickness : radius * 0.5
223
- const outerRadius = radius + 15 // Reduced from 20 to 15
224
-
225
- const innerX = Math.cos(midAngleRad) * innerRadius
226
- const innerY = Math.sin(midAngleRad) * innerRadius
227
- const outerX = Math.cos(midAngleRad) * outerRadius
228
- const outerY = Math.sin(midAngleRad) * outerRadius
229
-
230
- // Extended line for label - reduced extension
231
- const labelX = outerX + (outerX > 0 ? 8 : -8) // Reduced from 10 to 8
232
- const labelY = outerY
233
-
234
- return {
235
- innerX,
236
- innerY,
237
- outerX,
238
- outerY,
239
- labelX,
240
- labelY,
241
- textAnchor: outerX > 0 ? 'start' : 'end'
242
- }
243
- }
244
-
245
- const centerDisplayValue = computed(() => {
246
- if (props.centerValue !== undefined) {
247
- return props.centerValue
248
- }
249
- return props.showCenterTotal ? totalValue.value : 0
250
- })
251
-
252
- const pieSegments = computed(() => {
253
- let cumulativeAngle = 0
254
- return props.data.map((item, index) => {
255
- const percentage = item.value / totalValue.value
256
- const angle = percentage * 360
257
-
258
- const startAngle = cumulativeAngle
259
- const endAngle = cumulativeAngle + angle
260
- cumulativeAngle += angle
261
-
262
- // Calculate if this segment should be visible based on animation progress
263
- // Add a small delay even for the first segment to avoid immediate appearance
264
- const baseDelay = 100 // 100ms delay for the first segment
265
- const segmentDelay = props.animationDelay > 0
266
- ? baseDelay + (index * props.animationDelay)
267
- : baseDelay + (index * 50) // Small stagger even when animationDelay is 0
268
- const totalElapsedTime = animatedProgress.value * props.animationDuration
269
- const isVisible = !props.animated || totalElapsedTime >= segmentDelay
270
-
271
- // Debug log for animation
272
- if (props.animated && index === 0) {
273
- console.log(`Animation progress: ${(animatedProgress.value * 100).toFixed(1)}%, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
274
- }
275
-
276
- if (!isVisible) {
277
- if (index < 3) {
278
- console.log(`Segment ${index} waiting - needs ${segmentDelay}ms, current: ${totalElapsedTime.toFixed(0)}ms`)
279
- }
280
- return {
281
- ...item,
282
- percentage,
283
- angle: 0,
284
- startAngle,
285
- endAngle: startAngle,
286
- path: '',
287
- color: item.color || colors.value[index % colors.value.length],
288
- visible: false
289
- }
290
- } if (index < 3) {
291
- console.log(`Segment ${index} visible! - delay: ${segmentDelay}ms, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
292
- }
293
-
294
- const startAngleRad = (startAngle * Math.PI) / 180
295
- const endAngleRad = (endAngle * Math.PI) / 180
296
-
297
- const radius = props.size / 2 - 10
298
- const innerRadius = props.donut ? radius * props.thickness : 0
299
-
300
- const segmentAngle = endAngle - startAngle
301
- const largeArcFlag = segmentAngle > 180 ? 1 : 0
302
-
303
- const pathData = props.donut
304
- ? createDonutPath(startAngleRad, endAngleRad, radius, innerRadius, largeArcFlag)
305
- : createPiePath(startAngleRad, endAngleRad, radius, largeArcFlag)
306
-
307
- return {
308
- ...item,
309
- percentage,
310
- angle: segmentAngle,
311
- startAngle,
312
- endAngle,
313
- path: pathData,
314
- color: item.color || colors.value[index % colors.value.length],
315
- visible: true
316
- }
317
- })
318
- })
319
-
320
- function createPiePath(startAngle: number, endAngle: number, radius: number, largeArcFlag: number): string {
321
- const x1 = Math.cos(startAngle) * radius
322
- const y1 = Math.sin(startAngle) * radius
323
- const x2 = Math.cos(endAngle) * radius
324
- const y2 = Math.sin(endAngle) * radius
325
-
326
- return `M 0 0 L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2} Z`
327
- }
328
-
329
- function createDonutPath(startAngle: number, endAngle: number, outerRadius: number, innerRadius: number, largeArcFlag: number): string {
330
- const x1Outer = Math.cos(startAngle) * outerRadius
331
- const y1Outer = Math.sin(startAngle) * outerRadius
332
- const x2Outer = Math.cos(endAngle) * outerRadius
333
- const y2Outer = Math.sin(endAngle) * outerRadius
334
-
335
- const x1Inner = Math.cos(startAngle) * innerRadius
336
- const y1Inner = Math.sin(startAngle) * innerRadius
337
- const x2Inner = Math.cos(endAngle) * innerRadius
338
- const y2Inner = Math.sin(endAngle) * innerRadius
339
-
340
- return `M ${x1Outer} ${y1Outer} A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${x2Outer} ${y2Outer} L ${x2Inner} ${y2Inner} A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${x1Inner} ${y1Inner} Z`
341
- }
342
-
343
- function formatValue(value: number): string {
344
- return value.toLocaleString()
345
- }
346
-
347
- function formatPercentage(percentage: number): string {
348
- return `${(percentage * 100).toFixed(1)}%`
349
- }
350
-
351
- function formatTooltip(segment: any): string {
352
- return `<div class="pieTooltip">
353
- <div class="round w-10px h-10px w-100p inline-block" style="background-color: ${segment.color};"></div>
354
- <p class="inline-block txt12">${segment.label}</p>
355
- <div class="gap-025 flex">
356
- <p class="bold txt18">${formatValue(segment.value)}</p>
357
- <p>|</p>
358
- <p class="bold txt18">${formatPercentage(segment.percentage)}</p></div>`
359
- }
360
- </script>
361
-
362
- <template>
363
- <div ref="chartRef" class="h-100p flex column flex-stretch">
364
- <div class="flex space-between pb-1">
365
- <div class="flex align-center gap-05">
366
- <Icon :name="icon" size="1.2" :color="color" class="line-height-0" />
367
- <p class="white-space light m_txt14">
368
- {{ title }}
369
- </p>
370
- </div>
371
- <div v-if="percentageChange !== 0" class="flex align-center gap-025">
372
- <Icon class="line-height-1" :name="percentageChange > 0 ? 'trending_up' : 'trending_down'" size="1" :class="percentageChange > 0 ? 'color-success' : 'color-danger'" />
373
- <span class="txt12 bold" :class="percentageChange > 0 ? 'color-success' : 'color-danger'">
374
- {{ Math.abs(percentageChange) }}%
375
- </span>
376
- </div>
377
- </div>
378
- <div class="chart-container flex space-between gap-1 flex-wrap flex-grow" :class="{ 'with-legend': showLegend, 'with-connectors': showConnectorLines }">
379
- <svg :width="svgSize.width" :height="svgSize.height" class="chart-svg mx-auto flex-shrink-0" :style="showConnectorLines ? 'overflow: visible;' : ''">
380
- <g :transform="`translate(${centerOffset.x}, ${centerOffset.y})`">
381
- <path
382
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="index" v-tooltip="{ content: formatTooltip(segment), html: true }" :d="segment.path" :fill="segment.color"
383
- class="pie-segment" stroke="white" stroke-width="2"
384
- />
385
-
386
- <!-- Center text for donut chart -->
387
- <g v-if="donut && (showCenterTotal || centerValue !== undefined)" class="center-text">
388
- <text x="0" y="-8" text-anchor="middle" dominant-baseline="middle" class="center-label txt12 opacity-7">
389
- {{ centerLabel }}
390
- </text>
391
- <text x="0" y="12" text-anchor="middle" dominant-baseline="middle" class="center-value txt20 bold">
392
- {{ formatValue(centerDisplayValue) }}
393
- </text>
394
- </g>
395
-
396
- <!-- Connector lines -->
397
- <g v-if="showConnectorLines" class="connector-lines">
398
- <g v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`line-${index}`">
399
- <polyline
400
- :points="`${getConnectorLine(segment).innerX},${getConnectorLine(segment).innerY} ${getConnectorLine(segment).outerX},${getConnectorLine(segment).outerY} ${getConnectorLine(segment).labelX},${getConnectorLine(segment).labelY}`"
401
- stroke="#333" stroke-width="1.5" fill="none"
402
- />
403
- </g>
404
- </g>
405
-
406
- <!-- Labels on chart -->
407
- <g v-if="showLabelsOnChart" class="chart-labels">
408
- <!-- Labels without connector lines -->
409
- <g v-if="!showConnectorLines">
410
- <text
411
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`label-${index}`" :x="getLabelPosition(segment).x" :y="getLabelPosition(segment).y - 5"
412
- text-anchor="middle" dominant-baseline="middle" class="chart-label txt12 pointer-events-none" :fill="labelColor"
413
- >
414
- {{ segment.label }}
415
- </text>
416
- <text
417
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`value-${index}`" :x="getLabelPosition(segment).x" :y="getLabelPosition(segment).y + 12"
418
- text-anchor="middle" dominant-baseline="middle" class="chart-value txt16 mt-1 line-height-2 bold pointer-events-none" :fill="labelColor"
419
- >
420
- {{ formatValue(segment.value) }}
421
- </text>
422
- </g>
423
-
424
- <!-- Labels with connector lines -->
425
- <template v-if="showConnectorLines">
426
- <text
427
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`label-line-${index}`" :x="getConnectorLine(segment).labelX"
428
- :y="getConnectorLine(segment).labelY - 8" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-label txt12 bold" :fill="labelColor"
429
- >
430
- {{ segment.label }}
431
- </text>
432
-
433
- <!-- Value labels with connector lines -->
434
- <text
435
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`value-line-${index}`" :x="getConnectorLine(segment).labelX"
436
- :y="getConnectorLine(segment).labelY + 2" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-value txt11" fill="#666"
437
- >
438
- {{ formatValue(segment.value) }}
439
- </text>
440
-
441
- <!-- Percentage labels with connector lines -->
442
- <text
443
- v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`percentage-${index}`" :x="getConnectorLine(segment).labelX"
444
- :y="getConnectorLine(segment).labelY + 15" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-percentage txt10 pointer-events-none"
445
- fill="#999"
446
- >
447
- ({{ formatPercentage(segment.percentage) }})
448
- </text>
449
- </template>
450
- </g>
451
- </g>
452
- </svg>
453
-
454
- <div v-if="showLegend" class="legend mx-auto display-flex column gap-05 min-w-100px">
455
- <div v-for="(segment, index) in pieSegments" :key="index" class="gap-1 flex align-items-center txt14 justify-content-start">
456
- <div class="legend-color flex-shrink-0 radius-05" :style="{ backgroundColor: segment.color }" />
457
- <span v-if="segment.label" class="flex-shrink-1 flex-grow-1">{{ segment.label }}</span>
458
- <span class="bold">{{ formatValue(segment.value) }}</span>
459
- <span class="opacity-6 txt12">({{ formatPercentage(segment.percentage) }})</span>
460
- </div>
461
- </div>
462
- </div>
463
- </div>
464
- </template>
465
-
466
- <style scoped>
467
-
468
- .pie-segment {
469
- transition: opacity 0.3s ease;
470
- }
471
-
472
- [dir="rtl"] .chart-container.with-legend {
473
- flex-direction: row-reverse;
474
- }
475
-
476
- .pie-segment {
477
- cursor: pointer;
478
- transition: opacity 0.2s ease;
479
- animation: fadeIn 0.3s ease-in-out;
480
- }
481
-
482
- @keyframes fadeIn {
483
- from {
484
- opacity: 0;
485
- }
486
-
487
- to {
488
- opacity: 1;
489
- }
490
- }
491
-
492
- .pie-segment:hover {
493
- opacity: 0.8;
494
- }
495
-
496
- /* RTL support for legend */
497
- [dir="rtl"] .legend-item {
498
- flex-direction: row-reverse;
499
- }
500
-
501
- .legend-color {
502
- width: 12px;
503
- height: 12px;
504
- }
505
-
506
- .connector-lines polyline {
507
- opacity: 0.8;
508
- }
509
-
510
- .chart-container.with-connectors {
511
- overflow: visible;
512
- padding: 10px;
513
- }
514
-
515
- .chart-container.with-connectors .chart-svg {
516
- overflow: visible;
517
- }
518
-
519
- .chart-svg {
520
- max-width: 100%;
521
- max-height: 100%;
522
- }
523
-
524
- .chart-container {
525
- min-height: 0;
526
- align-items: center;
527
- }
528
-
529
- .chart-container.flex-grow {
530
- flex: 1;
531
- }
532
-
533
- </style>
534
-
535
- <style>
536
-
537
- .v-popper--theme-tooltip .v-popper__inner:has(.pieTooltip) {
538
- background-color: var(--bgl-black) !important;
539
- color: var(--bgl-white) !important;
540
- border-radius: 0.5rem !important;
541
- padding: 0.25rem 0.5rem !important;
542
- }
543
-
544
- </style>
@@ -1,4 +0,0 @@
1
- export { default as BarChart } from './BarChart.vue'
2
- export { default as KpiCard } from './KpiCard.vue'
3
- export { default as LineChart } from './LineChart.vue'
4
- export { default as PieChart } from './PieChart.vue'