@bagelink/vue 1.6.43 → 1.6.49
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.
- package/bin/experimentalGenTypedRoutes.ts +18 -19
- package/bin/utils.ts +4 -4
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/BglVideo.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Carousel.vue.d.ts +2 -2
- package/dist/components/Carousel.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Flag.vue.d.ts.map +1 -1
- package/dist/components/IframeVue.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/Loading.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/NavBar.vue.d.ts +1 -1
- package/dist/components/Pill.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +12 -4
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Zoomer.vue.d.ts.map +1 -1
- package/dist/components/analytics/LineChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/PieChart.vue.d.ts +2 -1
- package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/index.d.ts +1 -1
- package/dist/components/analytics/index.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/form/BglMultiStepForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RangeInput.vue.d.ts +11 -11
- package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts +1 -0
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Layout.vue.d.ts.map +1 -1
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
- package/dist/components/layout/index.d.ts +3 -3
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +24 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1530 -1404
- package/dist/plugins/modalTypes.d.ts +1 -8
- package/dist/plugins/modalTypes.d.ts.map +1 -1
- package/dist/plugins/useModal.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -2
- package/src/components/AccordionItem.vue +13 -13
- package/src/components/AddToCalendar.vue +1 -1
- package/src/components/AddressSearch.vue +9 -8
- package/src/components/Alert.vue +2 -1
- package/src/components/Badge.vue +5 -5
- package/src/components/BglVideo.vue +44 -45
- package/src/components/Btn.vue +15 -15
- package/src/components/Card.vue +10 -8
- package/src/components/Carousel.vue +159 -162
- package/src/components/DataPreview.vue +1 -1
- package/src/components/DragOver.vue +6 -6
- package/src/components/Dropdown.vue +39 -38
- package/src/components/Flag.vue +7 -6
- package/src/components/Icon/Icon.vue +22 -22
- package/src/components/IframeVue.vue +5 -5
- package/src/components/Image.vue +17 -17
- package/src/components/ImportData.vue +79 -79
- package/src/components/ListItem.vue +12 -11
- package/src/components/Loading.vue +10 -9
- package/src/components/MapEmbed/Index.vue +24 -24
- package/src/components/Modal.vue +11 -9
- package/src/components/ModalForm.vue +15 -11
- package/src/components/NavBar.vue +6 -6
- package/src/components/Pagination.vue +27 -27
- package/src/components/Pill.vue +11 -12
- package/src/components/Rating.vue +2 -2
- package/src/components/Slider.vue +75 -75
- package/src/components/Spreadsheet/Index.vue +34 -34
- package/src/components/Spreadsheet/SpreadsheetTable.vue +3 -3
- package/src/components/Swiper.vue +4 -4
- package/src/components/Zoomer.vue +282 -182
- package/src/components/analytics/BarChart.vue +6 -6
- package/src/components/analytics/KpiCard.vue +2 -2
- package/src/components/analytics/LineChart.vue +63 -61
- package/src/components/analytics/PieChart.vue +104 -90
- package/src/components/analytics/index.ts +2 -2
- package/src/components/calendar/CalendarPopover.vue +1 -1
- package/src/components/calendar/Index.vue +1 -1
- package/src/components/calendar/views/AgendaView.vue +3 -3
- package/src/components/calendar/views/DayView.vue +6 -6
- package/src/components/calendar/views/MonthView.vue +2 -2
- package/src/components/calendar/views/WeekView.vue +18 -18
- package/src/components/dataTable/DataTable.vue +4 -4
- package/src/components/dataTable/useSorting.ts +1 -1
- package/src/components/dataTable/useTableData.ts +15 -15
- package/src/components/dataTable/useTableSelection.ts +15 -15
- package/src/components/dataTable/useTableVirtualization.ts +1 -1
- package/src/components/draggable/useDraggable.ts +42 -42
- package/src/components/form/BagelForm.vue +15 -15
- package/src/components/form/BglFieldSet.vue +5 -3
- package/src/components/form/BglMultiStepForm.vue +20 -21
- package/src/components/form/inputs/CheckInput.vue +2 -2
- package/src/components/form/inputs/CodeEditor/format.ts +7 -7
- package/src/components/form/inputs/CodeEditor/useHighlight.ts +6 -6
- package/src/components/form/inputs/ColorInput.vue +5 -4
- package/src/components/form/inputs/DateInput.vue +8 -9
- package/src/components/form/inputs/DatePicker.vue +24 -24
- package/src/components/form/inputs/EmailInput.vue +24 -24
- package/src/components/form/inputs/NumberInput.vue +26 -26
- package/src/components/form/inputs/OTP.vue +7 -7
- package/src/components/form/inputs/PasswordInput.vue +3 -2
- package/src/components/form/inputs/RadioGroup.vue +28 -25
- package/src/components/form/inputs/RadioPillsInput.vue +12 -12
- package/src/components/form/inputs/RangeInput.vue +21 -21
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +107 -92
- package/src/components/form/inputs/RichText/components/TableGridSelector.vue +64 -64
- package/src/components/form/inputs/RichText/components/gridBox.vue +10 -8
- package/src/components/form/inputs/RichText/composables/useCommands.ts +1 -1
- package/src/components/form/inputs/RichText/composables/useEditor.ts +12 -12
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +1 -1
- package/src/components/form/inputs/RichText/index.vue +138 -138
- package/src/components/form/inputs/RichText/utils/commands.ts +84 -85
- package/src/components/form/inputs/RichText/utils/debug.ts +1 -1
- package/src/components/form/inputs/RichText/utils/formatting.ts +39 -39
- package/src/components/form/inputs/RichText/utils/media.ts +7 -7
- package/src/components/form/inputs/RichText/utils/selection.ts +28 -28
- package/src/components/form/inputs/RichText/utils/table.ts +19 -19
- package/src/components/form/inputs/SelectBtn.vue +1 -1
- package/src/components/form/inputs/SelectInput.vue +54 -54
- package/src/components/form/inputs/SignaturePad.vue +40 -40
- package/src/components/form/inputs/TableField.vue +1 -1
- package/src/components/form/inputs/TelInput.vue +54 -53
- package/src/components/form/inputs/TextInput.vue +19 -19
- package/src/components/form/inputs/ToggleInput.vue +2 -2
- package/src/components/form/inputs/Upload/useFileUpload.ts +6 -6
- package/src/components/form/useBagelFormState.ts +5 -5
- package/src/components/layout/AppContent.vue +6 -3
- package/src/components/layout/AppLayout.vue +2 -2
- package/src/components/layout/AppSidebar.vue +83 -16
- package/src/components/layout/Layout.vue +12 -10
- package/src/components/layout/SidebarMenu.vue +4 -4
- package/src/components/layout/TabbedLayout.vue +17 -17
- package/src/components/layout/Tabs.vue +4 -5
- package/src/components/layout/TabsNav.vue +14 -14
- package/src/components/layout/index.ts +3 -5
- package/src/components/lightbox/Lightbox.vue +276 -126
- package/src/components/lightbox/index.ts +8 -8
- package/src/composables/index.ts +8 -8
- package/src/composables/useAddToCalendar.ts +13 -13
- package/src/composables/useDevice.ts +2 -2
- package/src/composables/useFormField.ts +4 -4
- package/src/composables/usePolling.ts +8 -8
- package/src/composables/useSchemaField.ts +38 -38
- package/src/composables/useTheme.ts +9 -9
- package/src/composables/useValidateFieldValue.ts +2 -2
- package/src/directives/pattern.ts +25 -25
- package/src/directives/ripple.ts +4 -4
- package/src/directives/vResize.ts +6 -6
- package/src/index.ts +1 -0
- package/src/plugins/bagel.ts +4 -4
- package/src/plugins/modalTypes.ts +1 -8
- package/src/plugins/useModal.ts +43 -18
- package/src/styles/layout.css +1 -1
- package/src/types/index.ts +1 -1
- package/src/utils/BagelFormUtils.ts +7 -7
- package/src/utils/calendar/Helpers.ts +8 -8
- package/src/utils/calendar/dateUtils.ts +22 -22
- package/src/utils/calendar/time.ts +25 -25
- package/src/utils/calendar/week.ts +25 -25
- package/src/utils/elementUtils.ts +27 -27
- package/src/utils/sizeParsing.ts +2 -2
- package/src/utils/strings.ts +5 -5
- package/src/utils/tapDetector.ts +11 -11
- package/src/utils/useSearch.ts +29 -29
- package/vite.config.ts +0 -2
|
@@ -57,7 +57,7 @@ const observer = ref<IntersectionObserver | null>(null)
|
|
|
57
57
|
const chartRef = ref<HTMLElement | null>(null)
|
|
58
58
|
|
|
59
59
|
const chartData = computed(() => {
|
|
60
|
-
if (!props.data ||
|
|
60
|
+
if (!props.data || props.data.length === 0) { return [] }
|
|
61
61
|
|
|
62
62
|
// Use all data without limiting to maxBars
|
|
63
63
|
const maxValue = Math.max(...props.data.map(d => d.value), 1)
|
|
@@ -71,8 +71,8 @@ const chartData = computed(() => {
|
|
|
71
71
|
// Animation computed properties
|
|
72
72
|
const getBarOpacity = computed(() => {
|
|
73
73
|
return (index: number) => {
|
|
74
|
-
if (!props.animated) {return 1}
|
|
75
|
-
if (!isInView.value) {return 0}
|
|
74
|
+
if (!props.animated) { return 1 }
|
|
75
|
+
if (!isInView.value) { return 0 }
|
|
76
76
|
|
|
77
77
|
const totalBars = chartData.value.length
|
|
78
78
|
|
|
@@ -90,7 +90,7 @@ function easeOutCubic(t: number): number {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function startAnimation() {
|
|
93
|
-
if (isAnimating.value || !props.animated) {return}
|
|
93
|
+
if (isAnimating.value || !props.animated) { return }
|
|
94
94
|
|
|
95
95
|
console.log(`🎯 TrendChart: Starting animation with ${props.animationDuration}ms duration`)
|
|
96
96
|
isAnimating.value = true
|
|
@@ -105,7 +105,7 @@ function startAnimation() {
|
|
|
105
105
|
|
|
106
106
|
animatedProgress.value = easedProgress
|
|
107
107
|
|
|
108
|
-
if (
|
|
108
|
+
if (progress < 1) {
|
|
109
109
|
requestAnimationFrame(animate)
|
|
110
110
|
} else {
|
|
111
111
|
isAnimating.value = false
|
|
@@ -117,7 +117,7 @@ function startAnimation() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
function setupIntersectionObserver() {
|
|
120
|
-
if (!chartRef.value || observer.value) {return}
|
|
120
|
+
if (!chartRef.value || observer.value) { return }
|
|
121
121
|
|
|
122
122
|
observer.value = new IntersectionObserver(
|
|
123
123
|
(entries) => {
|
|
@@ -27,10 +27,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
27
27
|
subtitle: ''
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
const isIncreasing = computed(() =>
|
|
30
|
+
const isIncreasing = computed(() => props.percentageChange >= 0)
|
|
31
31
|
|
|
32
32
|
const formattedValue = computed(() => {
|
|
33
|
-
if (
|
|
33
|
+
if (typeof props.value === 'string') { return props.value }
|
|
34
34
|
|
|
35
35
|
if (props.currency) {
|
|
36
36
|
return new Intl.NumberFormat('he-IL', {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
4
2
|
import { Icon } from '@bagelink/vue'
|
|
3
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
5
4
|
|
|
6
5
|
interface DataPoint {
|
|
7
6
|
date: string
|
|
@@ -54,21 +53,21 @@ const chartHeight = computed(() => height.value - padding.top - padding.bottom)
|
|
|
54
53
|
|
|
55
54
|
// RTL-aware padding calculations
|
|
56
55
|
const paddingLeft = computed(() => {
|
|
57
|
-
const isRTL =
|
|
58
|
-
|
|
56
|
+
const isRTL = document.documentElement.dir === 'rtl'
|
|
57
|
+
|| document.documentElement.getAttribute('lang') === 'he'
|
|
59
58
|
return isRTL ? padding.inline_end : padding.inline_start
|
|
60
59
|
})
|
|
61
60
|
|
|
62
61
|
// RTL-aware label positioning
|
|
63
62
|
const labelXPosition = computed(() => {
|
|
64
|
-
const isRTL =
|
|
65
|
-
|
|
63
|
+
const isRTL = document.documentElement.dir === 'rtl'
|
|
64
|
+
|| document.documentElement.getAttribute('lang') === 'he'
|
|
66
65
|
return isRTL ? paddingLeft.value + chartWidth.value + 10 : paddingLeft.value - 10
|
|
67
66
|
})
|
|
68
67
|
|
|
69
68
|
const labelTextAnchor = computed(() => {
|
|
70
|
-
const isRTL =
|
|
71
|
-
|
|
69
|
+
const isRTL = document.documentElement.dir === 'rtl'
|
|
70
|
+
|| document.documentElement.getAttribute('lang') === 'he'
|
|
72
71
|
return isRTL ? 'start' : 'end'
|
|
73
72
|
})
|
|
74
73
|
|
|
@@ -76,8 +75,8 @@ const maxValue = computed(() => Math.max(...props.data.map(d => d.value), 0))
|
|
|
76
75
|
const minValue = computed(() => Math.min(...props.data.map(d => d.value), 0))
|
|
77
76
|
|
|
78
77
|
const xScale = computed(() => {
|
|
79
|
-
const isRTL =
|
|
80
|
-
|
|
78
|
+
const isRTL = document.documentElement.dir === 'rtl'
|
|
79
|
+
|| document.documentElement.getAttribute('lang') === 'he'
|
|
81
80
|
const domain = props.data.length - 1
|
|
82
81
|
|
|
83
82
|
return (index: number) => {
|
|
@@ -93,25 +92,24 @@ const yScale = computed(() => {
|
|
|
93
92
|
})
|
|
94
93
|
|
|
95
94
|
const pathData = computed(() => {
|
|
96
|
-
if (
|
|
95
|
+
if (props.data.length === 0) { return '' }
|
|
97
96
|
|
|
98
97
|
// Calculate how many points to show based on animation progress
|
|
99
98
|
const totalPoints = props.data.length
|
|
100
|
-
const visiblePoints = props.animated
|
|
101
|
-
Math.ceil(totalPoints * animatedProgress.value)
|
|
102
|
-
totalPoints
|
|
99
|
+
const visiblePoints = props.animated
|
|
100
|
+
? Math.ceil(totalPoints * animatedProgress.value)
|
|
101
|
+
: totalPoints
|
|
103
102
|
|
|
104
103
|
const dataToShow = props.data.slice(0, Math.max(1, visiblePoints))
|
|
105
104
|
|
|
106
|
-
const points = dataToShow.map((d, i) =>
|
|
107
|
-
`${ 0 === i ? 'M' : 'L'} ${paddingLeft.value + xScale.value(i)} ${padding.top + yScale.value(d.value)}`
|
|
105
|
+
const points = dataToShow.map((d, i) => `${i === 0 ? 'M' : 'L'} ${paddingLeft.value + xScale.value(i)} ${padding.top + yScale.value(d.value)}`
|
|
108
106
|
).join(' ')
|
|
109
107
|
|
|
110
108
|
return points
|
|
111
109
|
})
|
|
112
110
|
|
|
113
111
|
const visiblePoints = computed(() => {
|
|
114
|
-
if (!props.animated) {return props.data}
|
|
112
|
+
if (!props.animated) { return props.data }
|
|
115
113
|
|
|
116
114
|
const totalPoints = props.data.length
|
|
117
115
|
const pointsToShow = Math.ceil(totalPoints * animatedProgress.value)
|
|
@@ -121,7 +119,7 @@ const visiblePoints = computed(() => {
|
|
|
121
119
|
const gridLines = computed(() => {
|
|
122
120
|
const lines = []
|
|
123
121
|
const step = chartHeight.value / 4
|
|
124
|
-
for (let i = 0;
|
|
122
|
+
for (let i = 0; i <= 4; i++) {
|
|
125
123
|
const y = i * step
|
|
126
124
|
const value = maxValue.value - (i / 4) * (maxValue.value - minValue.value)
|
|
127
125
|
lines.push({ y, value })
|
|
@@ -156,8 +154,8 @@ function formatTooltip(point: any): string {
|
|
|
156
154
|
return `<div class="lineTooltip">${tooltipLines.join('<p>')}</div>`
|
|
157
155
|
}
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
if ('undefined'
|
|
157
|
+
function setupIntersectionObserver() {
|
|
158
|
+
if (typeof window === 'undefined') { return }
|
|
161
159
|
|
|
162
160
|
observer = new IntersectionObserver(
|
|
163
161
|
(entries) => {
|
|
@@ -187,7 +185,7 @@ const setupIntersectionObserver = () => {
|
|
|
187
185
|
}
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
|
|
188
|
+
function startAnimation() {
|
|
191
189
|
console.log(`Starting LineChart animation for "${props.title}" (duration: ${props.animationDuration}ms, start delay: ${props.animationStartDelay}ms)`)
|
|
192
190
|
isAnimating.value = true
|
|
193
191
|
animatedProgress.value = 0
|
|
@@ -200,7 +198,7 @@ const startAnimation = () => {
|
|
|
200
198
|
|
|
201
199
|
animatedProgress.value = easeOutCubic(progress)
|
|
202
200
|
|
|
203
|
-
if (
|
|
201
|
+
if (progress < 1) {
|
|
204
202
|
requestAnimationFrame(animate)
|
|
205
203
|
} else {
|
|
206
204
|
isAnimating.value = false
|
|
@@ -211,8 +209,8 @@ const startAnimation = () => {
|
|
|
211
209
|
requestAnimationFrame(animate)
|
|
212
210
|
}
|
|
213
211
|
|
|
214
|
-
|
|
215
|
-
return 1 -
|
|
212
|
+
function easeOutCubic(t: number): number {
|
|
213
|
+
return 1 - (1 - t) ** 3
|
|
216
214
|
}
|
|
217
215
|
|
|
218
216
|
onMounted(() => {
|
|
@@ -249,43 +247,47 @@ onUnmounted(() => {
|
|
|
249
247
|
{{ Math.abs(percentageChange) }}%
|
|
250
248
|
</span>
|
|
251
249
|
</div>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
250
|
+
</div>
|
|
251
|
+
<div class="chart-container flex-grow w-100p relative">
|
|
252
|
+
<svg ref="svgRef" :width="width" :height="height" class="chart-svg h-100p w-100p">
|
|
253
|
+
<!-- Grid lines -->
|
|
254
|
+
<g class="grid">
|
|
255
|
+
<line v-for="line in gridLines" :key="line.y" :x1="paddingLeft" :y1="padding.top + line.y" :x2="paddingLeft + chartWidth" :y2="padding.top + line.y" stroke="#e0e0e0" stroke-width="1" />
|
|
256
|
+
<!-- Y-axis labels -->
|
|
257
|
+
<text v-for="line in gridLines" :key="`label-${line.y}`" :x="labelXPosition" :y="padding.top + line.y + 4" class="grid-label" :text-anchor="labelTextAnchor">
|
|
258
|
+
{{ formatValue(line.value) }}
|
|
259
|
+
</text>
|
|
260
|
+
</g>
|
|
261
|
+
|
|
262
|
+
<!-- X-axis -->
|
|
263
|
+
<line :x1="paddingLeft" :y1="padding.top + chartHeight" :x2="paddingLeft + chartWidth" :y2="padding.top + chartHeight" stroke="#ccc" stroke-width="2" />
|
|
264
|
+
|
|
265
|
+
<!-- Y-axis -->
|
|
266
|
+
<line :x1="paddingLeft" :y1="padding.top" :x2="paddingLeft" :y2="padding.top + chartHeight" stroke="#ccc" stroke-width="2" />
|
|
267
|
+
|
|
268
|
+
<!-- Line path -->
|
|
269
|
+
<path :d="pathData" :stroke="color" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round" />
|
|
270
|
+
|
|
271
|
+
<!-- Data points -->
|
|
272
|
+
<g v-if="showPoints">
|
|
273
|
+
<circle
|
|
274
|
+
v-for="(point, index) in visiblePoints" :key="index" v-tooltip="{ content: formatTooltip(point), html: true }" :cx="paddingLeft + xScale(index)"
|
|
275
|
+
:cy="padding.top + yScale(point.value)" r="4" :fill="color" class="data-point"
|
|
276
|
+
/>
|
|
277
|
+
</g>
|
|
278
|
+
|
|
279
|
+
<!-- X-axis labels -->
|
|
280
|
+
<g class="x-labels">
|
|
281
|
+
<text
|
|
282
|
+
v-for="(point, index) in visiblePoints.filter((_, i) => i % Math.ceil(visiblePoints.length / 6) === 0)" :key="index"
|
|
283
|
+
:x="paddingLeft + xScale(visiblePoints.findIndex(d => d === point))" :y="padding.top + chartHeight + 15" class="axis-label" text-anchor="middle"
|
|
284
|
+
>
|
|
285
|
+
{{ formatDate(point.date) }}
|
|
286
|
+
</text>
|
|
287
|
+
</g>
|
|
288
|
+
</svg>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
289
291
|
</template>
|
|
290
292
|
|
|
291
293
|
<style scoped>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
|
3
2
|
import { Icon } from '@bagelink/vue'
|
|
3
|
+
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
|
4
4
|
|
|
5
5
|
interface PieData {
|
|
6
6
|
label: string
|
|
@@ -54,7 +54,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
54
54
|
// Generate colors with different opacity based on the main color OR use custom colors
|
|
55
55
|
const colors = computed(() => {
|
|
56
56
|
// If custom colors array is provided, use it
|
|
57
|
-
if (props.colors &&
|
|
57
|
+
if (props.colors && props.colors.length > 0) {
|
|
58
58
|
return props.colors
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -62,12 +62,12 @@ const colors = computed(() => {
|
|
|
62
62
|
const baseColor = props.color
|
|
63
63
|
const opacities = [1, 0.8, 0.6, 0.4, 0.85, 0.7, 0.5, 0.3, 0.9, 0.75]
|
|
64
64
|
|
|
65
|
-
return opacities.map(opacity => {
|
|
65
|
+
return opacities.map((opacity) => {
|
|
66
66
|
// Convert hex to rgba with opacity
|
|
67
67
|
const hex = baseColor.replace('#', '')
|
|
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)
|
|
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
71
|
|
|
72
72
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`
|
|
73
73
|
})
|
|
@@ -87,16 +87,16 @@ onMounted(() => {
|
|
|
87
87
|
}
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
+
let observer: IntersectionObserver | null = null
|
|
91
|
+
|
|
90
92
|
onUnmounted(() => {
|
|
91
93
|
if (observer) {
|
|
92
94
|
observer.disconnect()
|
|
93
95
|
}
|
|
94
96
|
})
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const setupIntersectionObserver = () => {
|
|
99
|
-
if ('undefined' === typeof window) {return}
|
|
98
|
+
function setupIntersectionObserver() {
|
|
99
|
+
if (typeof window === 'undefined') { return }
|
|
100
100
|
|
|
101
101
|
observer = new IntersectionObserver(
|
|
102
102
|
(entries) => {
|
|
@@ -129,8 +129,8 @@ const setupIntersectionObserver = () => {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
const delayType =
|
|
132
|
+
function startAnimation() {
|
|
133
|
+
const delayType = props.animationDelay > 0 ? 'staggered with delay' : 'quick stagger'
|
|
134
134
|
console.log(`Starting animation for "${props.title}" - ${delayType} mode (duration: ${props.animationDuration}ms, start delay: ${props.animationStartDelay}ms)`)
|
|
135
135
|
isAnimating.value = true
|
|
136
136
|
animatedProgress.value = 0
|
|
@@ -144,7 +144,7 @@ const startAnimation = () => {
|
|
|
144
144
|
// Easing function for smooth animation
|
|
145
145
|
animatedProgress.value = easeOutCubic(progress)
|
|
146
146
|
|
|
147
|
-
if (
|
|
147
|
+
if (progress < 1) {
|
|
148
148
|
requestAnimationFrame(animate)
|
|
149
149
|
} else {
|
|
150
150
|
isAnimating.value = false
|
|
@@ -155,12 +155,12 @@ const startAnimation = () => {
|
|
|
155
155
|
requestAnimationFrame(animate)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
return 1 -
|
|
158
|
+
function easeOutCubic(t: number): number {
|
|
159
|
+
return 1 - (1 - t) ** 3
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
// Expose animation control methods
|
|
163
|
-
|
|
163
|
+
function restartAnimation() {
|
|
164
164
|
if (props.animated) {
|
|
165
165
|
startAnimation()
|
|
166
166
|
}
|
|
@@ -174,8 +174,8 @@ defineExpose({
|
|
|
174
174
|
const svgSize = computed(() => {
|
|
175
175
|
if (props.showConnectorLines) {
|
|
176
176
|
return {
|
|
177
|
-
width: props.size + 200,
|
|
178
|
-
height: props.size + 100
|
|
177
|
+
width: props.size + 200, // Increased even more
|
|
178
|
+
height: props.size + 100 // Increased even more
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
return {
|
|
@@ -188,7 +188,7 @@ const svgSize = computed(() => {
|
|
|
188
188
|
const centerOffset = computed(() => {
|
|
189
189
|
if (props.showConnectorLines) {
|
|
190
190
|
return {
|
|
191
|
-
x: svgSize.value.width / 2 - 10,
|
|
191
|
+
x: svgSize.value.width / 2 - 10, // Less offset
|
|
192
192
|
y: svgSize.value.height / 2
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -198,12 +198,11 @@ const centerOffset = computed(() => {
|
|
|
198
198
|
}
|
|
199
199
|
})
|
|
200
200
|
|
|
201
|
-
const totalValue = computed(() =>
|
|
202
|
-
props.data.reduce((sum, item) => sum + item.value, 0)
|
|
201
|
+
const totalValue = computed(() => props.data.reduce((sum, item) => sum + item.value, 0)
|
|
203
202
|
)
|
|
204
203
|
|
|
205
204
|
// Calculate label positions for segments
|
|
206
|
-
|
|
205
|
+
function getLabelPosition(segment: any) {
|
|
207
206
|
const midAngle = (segment.startAngle + segment.endAngle) / 2
|
|
208
207
|
const midAngleRad = (midAngle * Math.PI) / 180
|
|
209
208
|
const radius = props.size / 2 - 10
|
|
@@ -216,12 +215,12 @@ const getLabelPosition = (segment: any) => {
|
|
|
216
215
|
}
|
|
217
216
|
|
|
218
217
|
// Calculate connector line positions
|
|
219
|
-
|
|
218
|
+
function getConnectorLine(segment: any) {
|
|
220
219
|
const midAngle = (segment.startAngle + segment.endAngle) / 2
|
|
221
220
|
const midAngleRad = (midAngle * Math.PI) / 180
|
|
222
221
|
const radius = props.size / 2 - 10
|
|
223
222
|
const innerRadius = props.donut ? radius * props.thickness : radius * 0.5
|
|
224
|
-
const outerRadius = radius + 15
|
|
223
|
+
const outerRadius = radius + 15 // Reduced from 20 to 15
|
|
225
224
|
|
|
226
225
|
const innerX = Math.cos(midAngleRad) * innerRadius
|
|
227
226
|
const innerY = Math.sin(midAngleRad) * innerRadius
|
|
@@ -229,7 +228,7 @@ const getConnectorLine = (segment: any) => {
|
|
|
229
228
|
const outerY = Math.sin(midAngleRad) * outerRadius
|
|
230
229
|
|
|
231
230
|
// Extended line for label - reduced extension
|
|
232
|
-
const labelX = outerX + (
|
|
231
|
+
const labelX = outerX + (outerX > 0 ? 8 : -8) // Reduced from 10 to 8
|
|
233
232
|
const labelY = outerY
|
|
234
233
|
|
|
235
234
|
return {
|
|
@@ -239,7 +238,7 @@ const getConnectorLine = (segment: any) => {
|
|
|
239
238
|
outerY,
|
|
240
239
|
labelX,
|
|
241
240
|
labelY,
|
|
242
|
-
textAnchor:
|
|
241
|
+
textAnchor: outerX > 0 ? 'start' : 'end'
|
|
243
242
|
}
|
|
244
243
|
}
|
|
245
244
|
|
|
@@ -263,19 +262,19 @@ const pieSegments = computed(() => {
|
|
|
263
262
|
// Calculate if this segment should be visible based on animation progress
|
|
264
263
|
// Add a small delay even for the first segment to avoid immediate appearance
|
|
265
264
|
const baseDelay = 100 // 100ms delay for the first segment
|
|
266
|
-
const segmentDelay =
|
|
267
|
-
baseDelay + (index * props.animationDelay)
|
|
268
|
-
baseDelay + (index * 50) // Small stagger even when animationDelay is 0
|
|
265
|
+
const segmentDelay = props.animationDelay > 0
|
|
266
|
+
? baseDelay + (index * props.animationDelay)
|
|
267
|
+
: baseDelay + (index * 50) // Small stagger even when animationDelay is 0
|
|
269
268
|
const totalElapsedTime = animatedProgress.value * props.animationDuration
|
|
270
269
|
const isVisible = !props.animated || totalElapsedTime >= segmentDelay
|
|
271
270
|
|
|
272
271
|
// Debug log for animation
|
|
273
|
-
if (props.animated &&
|
|
272
|
+
if (props.animated && index === 0) {
|
|
274
273
|
console.log(`Animation progress: ${(animatedProgress.value * 100).toFixed(1)}%, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
|
|
275
274
|
}
|
|
276
275
|
|
|
277
276
|
if (!isVisible) {
|
|
278
|
-
if (
|
|
277
|
+
if (index < 3) {
|
|
279
278
|
console.log(`Segment ${index} waiting - needs ${segmentDelay}ms, current: ${totalElapsedTime.toFixed(0)}ms`)
|
|
280
279
|
}
|
|
281
280
|
return {
|
|
@@ -288,7 +287,7 @@ const pieSegments = computed(() => {
|
|
|
288
287
|
color: item.color || colors.value[index % colors.value.length],
|
|
289
288
|
visible: false
|
|
290
289
|
}
|
|
291
|
-
}if (index < 3) {
|
|
290
|
+
} if (index < 3) {
|
|
292
291
|
console.log(`Segment ${index} visible! - delay: ${segmentDelay}ms, elapsed: ${totalElapsedTime.toFixed(0)}ms`)
|
|
293
292
|
}
|
|
294
293
|
|
|
@@ -299,7 +298,7 @@ const pieSegments = computed(() => {
|
|
|
299
298
|
const innerRadius = props.donut ? radius * props.thickness : 0
|
|
300
299
|
|
|
301
300
|
const segmentAngle = endAngle - startAngle
|
|
302
|
-
const largeArcFlag =
|
|
301
|
+
const largeArcFlag = segmentAngle > 180 ? 1 : 0
|
|
303
302
|
|
|
304
303
|
const pathData = props.donut
|
|
305
304
|
? createDonutPath(startAngleRad, endAngleRad, radius, innerRadius, largeArcFlag)
|
|
@@ -375,78 +374,93 @@ function formatTooltip(segment: any): string {
|
|
|
375
374
|
{{ Math.abs(percentageChange) }}%
|
|
376
375
|
</span>
|
|
377
376
|
</div>
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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 }}
|
|
389
415
|
</text>
|
|
390
|
-
<text
|
|
391
|
-
{
|
|
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) }}
|
|
392
421
|
</text>
|
|
393
422
|
</g>
|
|
394
|
-
|
|
395
|
-
<!--
|
|
396
|
-
<
|
|
397
|
-
<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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">
|
|
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
|
+
>
|
|
421
430
|
{{ segment.label }}
|
|
422
431
|
</text>
|
|
423
|
-
|
|
432
|
+
|
|
424
433
|
<!-- Value labels with connector lines -->
|
|
425
|
-
<text
|
|
426
|
-
|
|
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
|
+
>
|
|
427
438
|
{{ formatValue(segment.value) }}
|
|
428
439
|
</text>
|
|
429
|
-
|
|
440
|
+
|
|
430
441
|
<!-- Percentage labels with connector lines -->
|
|
431
|
-
<text
|
|
442
|
+
<text
|
|
443
|
+
v-for="(segment, index) in pieSegments.filter(s => s.visible)" :key="`percentage-${index}`" :x="getConnectorLine(segment).labelX"
|
|
432
444
|
:y="getConnectorLine(segment).labelY + 15" :text-anchor="getConnectorLine(segment).textAnchor" dominant-baseline="middle" class="chart-percentage txt10 pointer-events-none"
|
|
433
|
-
fill="#999"
|
|
445
|
+
fill="#999"
|
|
446
|
+
>
|
|
434
447
|
({{ formatPercentage(segment.percentage) }})
|
|
435
448
|
</text>
|
|
436
|
-
</
|
|
449
|
+
</template>
|
|
437
450
|
</g>
|
|
438
|
-
</
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
</
|
|
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>
|
|
447
460
|
</div>
|
|
448
461
|
</div>
|
|
449
|
-
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
450
464
|
</template>
|
|
451
465
|
|
|
452
466
|
<style scoped>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as BarChart } from './BarChart.vue'
|
|
2
|
-
export { default as LineChart } from './LineChart.vue'
|
|
3
2
|
export { default as KpiCard } from './KpiCard.vue'
|
|
4
|
-
export { default as
|
|
3
|
+
export { default as LineChart } from './LineChart.vue'
|
|
4
|
+
export { default as PieChart } from './PieChart.vue'
|
|
@@ -31,7 +31,7 @@ const popoverPosition = computed(() => {
|
|
|
31
31
|
left = Math.max(scrollX.value + margin, Math.min(left, innerWidth.value + scrollX.value - popoverWidth - margin))
|
|
32
32
|
|
|
33
33
|
return { top, left, width: popoverWidth, height: 150 }
|
|
34
|
-
}if (props.targetElement && props.show) {
|
|
34
|
+
} if (props.targetElement && props.show) {
|
|
35
35
|
// Position relative to target element
|
|
36
36
|
const target = props.targetElement
|
|
37
37
|
const rect = target.getBoundingClientRect()
|
|
@@ -42,7 +42,7 @@ const views: Record<CalendarView, Component> = {
|
|
|
42
42
|
const visibleDateRange = computed(() => {
|
|
43
43
|
const start = new Date(currentDate.value)
|
|
44
44
|
let end = new Date(currentDate.value)
|
|
45
|
-
const addSunday = 'Sunday'
|
|
45
|
+
const addSunday = props.weekStart === 'Sunday' ? 0 : 1
|
|
46
46
|
switch (currentView.value) {
|
|
47
47
|
case 'Week':
|
|
48
48
|
start.setDate(start.getDate() - start.getDay() + addSunday)
|