@bagelink/vue 1.4.139 → 1.4.145

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 (73) hide show
  1. package/dist/components/Btn.vue.d.ts.map +1 -1
  2. package/dist/components/Carousel.vue.d.ts +1 -1
  3. package/dist/components/Modal.vue.d.ts +3 -0
  4. package/dist/components/Modal.vue.d.ts.map +1 -1
  5. package/dist/components/Slider.vue.d.ts +1 -1
  6. package/dist/components/Slider.vue.d.ts.map +1 -1
  7. package/dist/components/analytics/BarChart.vue.d.ts +11 -3
  8. package/dist/components/analytics/BarChart.vue.d.ts.map +1 -1
  9. package/dist/components/analytics/LineChart.vue.d.ts +9 -0
  10. package/dist/components/analytics/LineChart.vue.d.ts.map +1 -1
  11. package/dist/components/analytics/PieChart.vue.d.ts +30 -2
  12. package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
  13. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts +8 -0
  14. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts +9 -0
  16. package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts.map +1 -0
  17. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
  18. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +0 -14
  19. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
  20. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
  21. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
  22. package/dist/components/form/inputs/RichText/index.vue.d.ts +15 -15
  23. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  24. package/dist/components/form/inputs/RichText/richTextTypes.d.ts +1 -3
  25. package/dist/components/form/inputs/RichText/richTextTypes.d.ts.map +1 -1
  26. package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
  27. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
  28. package/dist/components/form/inputs/RichText/utils/media-clean.d.ts +2 -0
  29. package/dist/components/form/inputs/RichText/utils/media-clean.d.ts.map +1 -0
  30. package/dist/components/form/inputs/RichText/utils/media.d.ts +4 -4
  31. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
  32. package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
  33. package/dist/components/form/inputs/RichText/utils/table.d.ts +1 -1
  34. package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.d.ts.map +1 -1
  37. package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
  38. package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
  39. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
  40. package/dist/index.cjs +123 -22
  41. package/dist/index.mjs +123 -22
  42. package/dist/style.css +1 -1
  43. package/package.json +1 -1
  44. package/src/components/Btn.vue +50 -42
  45. package/src/components/Modal.vue +49 -50
  46. package/src/components/analytics/BarChart.vue +118 -7
  47. package/src/components/analytics/KpiCard.vue +2 -2
  48. package/src/components/analytics/LineChart.vue +189 -105
  49. package/src/components/analytics/PieChart.vue +392 -49
  50. package/src/components/dataTable/DataTable.vue +1 -1
  51. package/src/components/form/inputs/RichText/CheckList.md +23 -0
  52. package/src/components/form/inputs/RichText/components/EditorToolbar.vue +243 -27
  53. package/src/components/form/inputs/RichText/components/TableGridSelector.vue +94 -0
  54. package/src/components/form/inputs/RichText/composables/useCommands.ts +45 -0
  55. package/src/components/form/inputs/RichText/composables/useEditor.ts +13 -10
  56. package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +3 -128
  57. package/src/components/form/inputs/RichText/config.ts +33 -10
  58. package/src/components/form/inputs/RichText/editor.css +300 -33
  59. package/src/components/form/inputs/RichText/index.vue +3271 -130
  60. package/src/components/form/inputs/RichText/richTextTypes.ts +7 -3
  61. package/src/components/form/inputs/RichText/utils/commands.ts +851 -90
  62. package/src/components/form/inputs/RichText/utils/formatting.ts +17 -15
  63. package/src/components/form/inputs/RichText/utils/media-clean.ts +0 -0
  64. package/src/components/form/inputs/RichText/utils/media.ts +133 -67
  65. package/src/components/form/inputs/RichText/utils/selection.ts +40 -11
  66. package/src/components/form/inputs/RichText/utils/table.ts +1 -1
  67. package/src/components/index.ts +1 -0
  68. package/src/components/layout/AppContent.vue +26 -26
  69. package/src/components/layout/AppLayout.vue +21 -3
  70. package/src/components/layout/AppSidebar.vue +5 -2
  71. package/src/styles/layout.css +267 -0
  72. package/src/styles/mobilLayout.css +266 -0
  73. package/src/styles/modal.css +3 -17
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
+ import { computed, ref, onMounted, onUnmounted } from 'vue'
2
3
  import { Icon } from '@bagelink/vue'
3
- import { computed } from 'vue'
4
4
 
5
5
  interface PieData {
6
6
  label: string
@@ -13,9 +13,22 @@ interface Props {
13
13
  title?: string
14
14
  icon?: string
15
15
  color?: string
16
+ colors?: string[]
16
17
  size?: number
17
18
  showLegend?: boolean
18
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
19
32
  }
20
33
 
21
34
  const props = withDefaults(defineProps<Props>(), {
@@ -24,52 +37,269 @@ const props = withDefaults(defineProps<Props>(), {
24
37
  color: '#3B82F6',
25
38
  size: 200,
26
39
  showLegend: true,
27
- donut: false
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'
28
52
  })
29
53
 
30
- // Generate colors with different opacity based on the main color
54
+ // Generate colors with different opacity based on the main color OR use custom colors
31
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
32
62
  const baseColor = props.color
33
63
  const opacities = [1, 0.8, 0.6, 0.4, 0.85, 0.7, 0.5, 0.3, 0.9, 0.75]
34
64
 
35
- return opacities.map((opacity) => {
65
+ return opacities.map(opacity => {
36
66
  // Convert hex to rgba with opacity
37
67
  const hex = baseColor.replace('#', '')
38
- const r = Number.parseInt(hex.substr(0, 2), 16)
39
- const g = Number.parseInt(hex.substr(2, 2), 16)
40
- const b = Number.parseInt(hex.substr(4, 2), 16)
68
+ const r = parseInt(hex.substr(0, 2), 16)
69
+ const g = parseInt(hex.substr(2, 2), 16)
70
+ const b = parseInt(hex.substr(4, 2), 16)
41
71
 
42
72
  return `rgba(${r}, ${g}, ${b}, ${opacity})`
43
73
  })
44
74
  })
45
75
 
46
- // // RTL detection
47
- // const isRtl = computed(() => {
48
- // if (typeof document !== 'undefined') {
49
- // return document.documentElement.dir === 'rtl' || document.body.dir === 'rtl'
50
- // }
51
- // return false
52
- // })
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
+ onUnmounted(() => {
91
+ if (observer) {
92
+ observer.disconnect()
93
+ }
94
+ })
95
+
96
+ let observer: IntersectionObserver | null = null
97
+
98
+ const 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
+ const 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
+ const easeOutCubic = (t: number): number => {
159
+ return 1 - Math.pow(1 - t, 3)
160
+ }
161
+
162
+ // Expose animation control methods
163
+ const 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
+ })
53
186
 
54
- const totalValue = computed(() => props.data.reduce((sum, item) => sum + item.value, 0)
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(() =>
202
+ props.data.reduce((sum, item) => sum + item.value, 0)
55
203
  )
56
204
 
205
+ // Calculate label positions for segments
206
+ const getLabelPosition = (segment: any) => {
207
+ const midAngle = (segment.startAngle + segment.endAngle) / 2
208
+ const midAngleRad = (midAngle * Math.PI) / 180
209
+ const radius = props.size / 2 - 10
210
+ const labelRadius = props.donut ? radius * 0.8 : radius * 0.7
211
+
212
+ return {
213
+ x: Math.cos(midAngleRad) * labelRadius,
214
+ y: Math.sin(midAngleRad) * labelRadius
215
+ }
216
+ }
217
+
218
+ // Calculate connector line positions
219
+ const getConnectorLine = (segment: any) => {
220
+ const midAngle = (segment.startAngle + segment.endAngle) / 2
221
+ const midAngleRad = (midAngle * Math.PI) / 180
222
+ const radius = props.size / 2 - 10
223
+ const innerRadius = props.donut ? radius * props.thickness : radius * 0.5
224
+ const outerRadius = radius + 15 // Reduced from 20 to 15
225
+
226
+ const innerX = Math.cos(midAngleRad) * innerRadius
227
+ const innerY = Math.sin(midAngleRad) * innerRadius
228
+ const outerX = Math.cos(midAngleRad) * outerRadius
229
+ const outerY = Math.sin(midAngleRad) * outerRadius
230
+
231
+ // Extended line for label - reduced extension
232
+ const labelX = outerX + (outerX > 0 ? 8 : -8) // Reduced from 10 to 8
233
+ const labelY = outerY
234
+
235
+ return {
236
+ innerX,
237
+ innerY,
238
+ outerX,
239
+ outerY,
240
+ labelX,
241
+ labelY,
242
+ textAnchor: outerX > 0 ? 'start' : 'end'
243
+ }
244
+ }
245
+
246
+ const centerDisplayValue = computed(() => {
247
+ if (props.centerValue !== undefined) {
248
+ return props.centerValue
249
+ }
250
+ return props.showCenterTotal ? totalValue.value : 0
251
+ })
252
+
57
253
  const pieSegments = computed(() => {
58
254
  let cumulativeAngle = 0
59
255
  return props.data.map((item, index) => {
60
256
  const percentage = item.value / totalValue.value
61
257
  const angle = percentage * 360
258
+
62
259
  const startAngle = cumulativeAngle
63
260
  const endAngle = cumulativeAngle + angle
64
261
  cumulativeAngle += angle
65
262
 
263
+ // Calculate if this segment should be visible based on animation progress
264
+ // Add a small delay even for the first segment to avoid immediate appearance
265
+ const baseDelay = 100 // 100ms delay for the first segment
266
+ const segmentDelay = props.animationDelay > 0 ?
267
+ baseDelay + (index * props.animationDelay) :
268
+ baseDelay + (index * 50) // Small stagger even when animationDelay is 0
269
+ const totalElapsedTime = animatedProgress.value * props.animationDuration
270
+ const isVisible = !props.animated || totalElapsedTime >= segmentDelay
271
+
272
+ // Debug log for animation
273
+ if (props.animated && index === 0) {
274
+ console.log(`Animation progress: ${(animatedProgress.value * 100).toFixed(1)}%, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
275
+ }
276
+
277
+ if (!isVisible) {
278
+ if (index < 3) {
279
+ console.log(`Segment ${index} waiting - needs ${segmentDelay}ms, current: ${totalElapsedTime.toFixed(0)}ms`)
280
+ }
281
+ return {
282
+ ...item,
283
+ percentage,
284
+ angle: 0,
285
+ startAngle,
286
+ endAngle: startAngle,
287
+ path: '',
288
+ color: item.color || colors.value[index % colors.value.length],
289
+ visible: false
290
+ }
291
+ } else if (index < 3) {
292
+ console.log(`Segment ${index} visible! - delay: ${segmentDelay}ms, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
293
+ }
294
+
66
295
  const startAngleRad = (startAngle * Math.PI) / 180
67
296
  const endAngleRad = (endAngle * Math.PI) / 180
68
297
 
69
298
  const radius = props.size / 2 - 10
70
- const innerRadius = props.donut ? radius * 0.6 : 0
299
+ const innerRadius = props.donut ? radius * props.thickness : 0
71
300
 
72
- const largeArcFlag = angle > 180 ? 1 : 0
301
+ const segmentAngle = endAngle - startAngle
302
+ const largeArcFlag = segmentAngle > 180 ? 1 : 0
73
303
 
74
304
  const pathData = props.donut
75
305
  ? createDonutPath(startAngleRad, endAngleRad, radius, innerRadius, largeArcFlag)
@@ -78,11 +308,12 @@ const pieSegments = computed(() => {
78
308
  return {
79
309
  ...item,
80
310
  percentage,
81
- angle,
311
+ angle: segmentAngle,
82
312
  startAngle,
83
313
  endAngle,
84
- pathData,
85
- color: item.color || colors.value[index % colors.value.length]
314
+ path: pathData,
315
+ color: item.color || colors.value[index % colors.value.length],
316
+ visible: true
86
317
  }
87
318
  })
88
319
  })
@@ -117,47 +348,112 @@ function formatValue(value: number): string {
117
348
  function formatPercentage(percentage: number): string {
118
349
  return `${(percentage * 100).toFixed(1)}%`
119
350
  }
351
+
352
+ function formatTooltip(segment: any): string {
353
+ return `<div class="pieTooltip">
354
+ <div class="round w-10px h-10px w-100p inline-block" style="background-color: ${segment.color};"></div>
355
+ <p class="inline-block txt12">${segment.label}</p>
356
+ <div class="gap-025 flex">
357
+ <p class="bold txt18">${formatValue(segment.value)}</p>
358
+ <p>|</p>
359
+ <p class="bold txt18">${formatPercentage(segment.percentage)}</p></div>`
360
+ }
120
361
  </script>
121
362
 
122
363
  <template>
123
- <div class="h-100p flex column flex-stretch">
124
- <div class="flex space-between">
125
- <div class="flex align-center gap-05 pb-1">
126
- <Icon :name="icon" size="1.2" :color="color" class="line-height-08" />
364
+ <div ref="chartRef" class="h-100p flex column flex-stretch">
365
+ <div class="flex space-between pb-1">
366
+ <div class="flex align-center gap-05">
367
+ <Icon :name="icon" size="1.2" :color="color" class="line-height-0" />
127
368
  <p class="white-space light m_txt14">
128
369
  {{ title }}
129
370
  </p>
130
371
  </div>
131
- </div>
132
- <div class="chart-container flex space-between gap-1 flex-wrap" :class="{ 'with-legend': showLegend }">
133
- <svg :width="size" :height="size" class="chart-svg mx-auto flex-shrink-0">
134
- <g :transform="`translate(${size / 2}, ${size / 2})`">
135
- <path
136
- v-for="(segment, index) in pieSegments" :key="index" :d="segment.pathData"
137
- :fill="segment.color" class="pie-segment" stroke="white" stroke-width="2"
138
- >
139
- <title>{{ segment.label }}: {{ formatValue(segment.value) }} ({{
140
- formatPercentage(segment.percentage) }})</title>
141
- </path>
142
- </g>
143
- </svg>
144
-
145
- <div v-if="showLegend" class="legend mx-auto display-flex column gap-05 min-w-100px">
146
- <div
147
- v-for="(segment, index) in pieSegments" :key="index"
148
- class="gap-1 flex align-items-center txt14 justify-content-start"
149
- >
150
- <div class="legend-color flex-shrink-0 radius-05" :style="{ backgroundColor: segment.color }" />
151
- <span v-if="segment.label" class="flex-shrink-1 flex-grow-1">{{ segment.label }}</span>
152
- <span class="bold">{{ formatValue(segment.value) }}</span>
153
- <span class="opacity-6 txt12">({{ formatPercentage(segment.percentage) }})</span>
372
+ <div v-if="percentageChange !== 0" class="flex align-center gap-025">
373
+ <Icon class="line-height-1" :name="percentageChange > 0 ? 'trending_up' : 'trending_down'" size="1" :class="percentageChange > 0 ? 'color-success' : 'color-danger'" />
374
+ <span class="txt12 bold" :class="percentageChange > 0 ? 'color-success' : 'color-danger'">
375
+ {{ Math.abs(percentageChange) }}%
376
+ </span>
377
+ </div>
378
+ </div>
379
+ <div class="chart-container flex space-between gap-1 flex-wrap flex-grow" :class="{ 'with-legend': showLegend, 'with-connectors': showConnectorLines }">
380
+ <svg :width="svgSize.width" :height="svgSize.height" class="chart-svg mx-auto flex-shrink-0" :style="showConnectorLines ? 'overflow: visible;' : ''">
381
+ <g :transform="`translate(${centerOffset.x}, ${centerOffset.y})`">
382
+ <path 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
+ <!-- Center text for donut chart -->
386
+ <g v-if="donut && (showCenterTotal || centerValue !== undefined)" class="center-text">
387
+ <text x="0" y="-8" text-anchor="middle" dominant-baseline="middle" class="center-label txt12 opacity-7">
388
+ {{ centerLabel }}
389
+ </text>
390
+ <text x="0" y="12" text-anchor="middle" dominant-baseline="middle" class="center-value txt20 bold">
391
+ {{ formatValue(centerDisplayValue) }}
392
+ </text>
393
+ </g>
394
+
395
+ <!-- Connector lines -->
396
+ <g v-if="showConnectorLines" class="connector-lines">
397
+ <g v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`line-${index}`">
398
+ <polyline
399
+ :points="`${getConnectorLine(segment).innerX},${getConnectorLine(segment).innerY} ${getConnectorLine(segment).outerX},${getConnectorLine(segment).outerY} ${getConnectorLine(segment).labelX},${getConnectorLine(segment).labelY}`"
400
+ stroke="#333" stroke-width="1.5" fill="none" />
401
+ </g>
402
+ </g>
403
+
404
+ <!-- Labels on chart -->
405
+ <g v-if="showLabelsOnChart" class="chart-labels">
406
+ <!-- Labels without connector lines -->
407
+ <g v-if="!showConnectorLines">
408
+ <text v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`label-${index}`" :x="getLabelPosition(segment).x" :y="getLabelPosition(segment).y - 5"
409
+ text-anchor="middle" dominant-baseline="middle" class="chart-label txt12 pointer-events-none" :fill="labelColor">
410
+ {{ segment.label }}
411
+ </text>
412
+ <text v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`value-${index}`" :x="getLabelPosition(segment).x" :y="getLabelPosition(segment).y + 12"
413
+ text-anchor="middle" dominant-baseline="middle" class="chart-value txt16 mt-1 line-height-2 bold pointer-events-none" :fill="labelColor">
414
+ {{ formatValue(segment.value) }}
415
+ </text>
416
+ </g>
417
+
418
+ <!-- Labels with connector lines -->
419
+ <text v-if="showConnectorLines" v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`label-line-${index}`" :x="getConnectorLine(segment).labelX"
420
+ :y="getConnectorLine(segment).labelY - 8" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-label txt12 bold" :fill="labelColor">
421
+ {{ segment.label }}
422
+ </text>
423
+
424
+ <!-- Value labels with connector lines -->
425
+ <text v-if="showConnectorLines" v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`value-line-${index}`" :x="getConnectorLine(segment).labelX"
426
+ :y="getConnectorLine(segment).labelY + 2" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-value txt11" fill="#666">
427
+ {{ formatValue(segment.value) }}
428
+ </text>
429
+
430
+ <!-- Percentage labels with connector lines -->
431
+ <text v-if="showConnectorLines" v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`percentage-${index}`" :x="getConnectorLine(segment).labelX"
432
+ :y="getConnectorLine(segment).labelY + 15" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-percentage txt10 pointer-events-none"
433
+ fill="#999">
434
+ ({{ formatPercentage(segment.percentage) }})
435
+ </text>
436
+ </g>
437
+ </g>
438
+ </svg>
439
+
440
+ <div v-if="showLegend" class="legend mx-auto display-flex column gap-05 min-w-100px">
441
+ <div v-for="(segment, index) in pieSegments" :key="index" class="gap-1 flex align-items-center txt14 justify-content-start">
442
+ <div class="legend-color flex-shrink-0 radius-05" :style="{ backgroundColor: segment.color }" />
443
+ <span v-if="segment.label" class="flex-shrink-1 flex-grow-1">{{ segment.label }}</span>
444
+ <span class="bold">{{ formatValue(segment.value) }}</span>
445
+ <span class="opacity-6 txt12">({{ formatPercentage(segment.percentage) }})</span>
446
+ </div>
154
447
  </div>
155
448
  </div>
156
- </div>
157
- </div>
449
+ </div>
158
450
  </template>
159
451
 
160
452
  <style scoped>
453
+ .pie-segment {
454
+ transition: opacity 0.3s ease;
455
+ }
456
+
161
457
  [dir="rtl"] .chart-container.with-legend {
162
458
  flex-direction: row-reverse;
163
459
  }
@@ -165,6 +461,17 @@ function formatPercentage(percentage: number): string {
165
461
  .pie-segment {
166
462
  cursor: pointer;
167
463
  transition: opacity 0.2s ease;
464
+ animation: fadeIn 0.3s ease-in-out;
465
+ }
466
+
467
+ @keyframes fadeIn {
468
+ from {
469
+ opacity: 0;
470
+ }
471
+
472
+ to {
473
+ opacity: 1;
474
+ }
168
475
  }
169
476
 
170
477
  .pie-segment:hover {
@@ -180,4 +487,40 @@ function formatPercentage(percentage: number): string {
180
487
  width: 12px;
181
488
  height: 12px;
182
489
  }
490
+
491
+ .connector-lines polyline {
492
+ opacity: 0.8;
493
+ }
494
+
495
+ .chart-container.with-connectors {
496
+ overflow: visible;
497
+ padding: 10px;
498
+ }
499
+
500
+ .chart-container.with-connectors .chart-svg {
501
+ overflow: visible;
502
+ }
503
+
504
+ .chart-svg {
505
+ max-width: 100%;
506
+ max-height: 100%;
507
+ }
508
+
509
+ .chart-container {
510
+ min-height: 0;
511
+ align-items: center;
512
+ }
513
+
514
+ .chart-container.flex-grow {
515
+ flex: 1;
516
+ }
517
+ </style>
518
+
519
+ <style>
520
+ .v-popper--theme-tooltip .v-popper__inner:has(.pieTooltip) {
521
+ background-color: var(--bgl-black) !important;
522
+ color: var(--bgl-white) !important;
523
+ border-radius: 0.5rem !important;
524
+ padding: 0.25rem 0.5rem !important;
525
+ }
183
526
  </style>
@@ -127,7 +127,7 @@ const showLoading = computed(() => loading.value)
127
127
  @click="toggleSort(field?.id || '')"
128
128
  >
129
129
  <div class="flex">
130
- {{ field.label || keyToLabel(field?.id) }}
130
+ {{ field.label || field.attrs?.label || keyToLabel(field?.id) }}
131
131
  <div class="list-arrows" :class="{ sorted: sortField === field?.id }">
132
132
  <Icon :class="{ desc: sortDirection === 'DESC' }" icon="keyboard_arrow_up" />
133
133
  </div>
@@ -0,0 +1,23 @@
1
+ [] that h1-6 works
2
+ [] enter after h1-6 turns back to p (text)
3
+ [] bold/underline/italic works both in the toolbar, and in the mini tow bar
4
+ [] bold/underline/italic get removed after clicking again
5
+ [] add and remove bold/underline/italic when the cursor it is on the end of the word
6
+ [] list works properly meaning one enter create a new item two enters takes you out of the list
7
+ [] alignments still works
8
+ [] list change direction
9
+ [] undo/redo still works
10
+ [] clear formating removes all inline style
11
+ [] pop-ups work - insert link - insert image - insert video - insert embed
12
+ [] That link actually being inserted
13
+ [] That image is actually being inserted
14
+ [] That video is actually being inserted
15
+ [] That embed is actually being inserted
16
+ [] able to insert table
17
+ [] advance options in table opens popup
18
+ [] edit btn is visible clickable, especially if you have more than one table
19
+ [] the table can get alignment and direction, even if it's the only thing in the body
20
+ [] that you can insert a table to the table edit btn
21
+ [] you can change table dircation and alignment
22
+ [] make sure you can change direction does the first element in the table
23
+ [] that table get the Direct direction and alignment relative to the rest of the text