@bagelink/vue 1.4.109 → 1.4.115

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 (87) hide show
  1. package/bin/generateFormSchema.ts +12 -12
  2. package/dist/components/Card.vue.d.ts.map +1 -1
  3. package/dist/components/ImportData.vue.d.ts.map +1 -1
  4. package/dist/components/ListItem.vue.d.ts +6 -1
  5. package/dist/components/ListItem.vue.d.ts.map +1 -1
  6. package/dist/components/analytics/BarChart.vue.d.ts +39 -0
  7. package/dist/components/analytics/BarChart.vue.d.ts.map +1 -0
  8. package/dist/components/analytics/KpiCard.vue.d.ts +24 -0
  9. package/dist/components/analytics/KpiCard.vue.d.ts.map +1 -0
  10. package/dist/components/analytics/LineChart.vue.d.ts +26 -0
  11. package/dist/components/analytics/LineChart.vue.d.ts.map +1 -0
  12. package/dist/components/analytics/PieChart.vue.d.ts +24 -0
  13. package/dist/components/analytics/PieChart.vue.d.ts.map +1 -0
  14. package/dist/components/analytics/index.d.ts +5 -0
  15. package/dist/components/analytics/index.d.ts.map +1 -0
  16. package/dist/components/calendar/Index.vue.d.ts.map +1 -1
  17. package/dist/components/calendar/index.d.ts +2 -0
  18. package/dist/components/calendar/index.d.ts.map +1 -0
  19. package/dist/components/calendar/views/MonthView.vue.d.ts.map +1 -1
  20. package/dist/components/calendar/views/WeekView.vue.d.ts.map +1 -1
  21. package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
  22. package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
  23. package/dist/components/form/inputs/DatePicker.vue.d.ts +2 -0
  24. package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
  25. package/dist/components/form/inputs/RadioGroup.vue.d.ts +6 -10
  26. package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
  27. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
  28. package/dist/components/form/inputs/SelectInput.vue.d.ts +2 -2
  29. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  30. package/dist/components/layout/AppContent.vue.d.ts +34 -0
  31. package/dist/components/layout/AppContent.vue.d.ts.map +1 -0
  32. package/dist/components/layout/AppLayout.vue.d.ts +27 -0
  33. package/dist/components/layout/AppLayout.vue.d.ts.map +1 -0
  34. package/dist/components/layout/AppSidebar.vue.d.ts +44 -0
  35. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -0
  36. package/dist/components/layout/index.d.ts +3 -0
  37. package/dist/components/layout/index.d.ts.map +1 -1
  38. package/dist/composables/useFormField.d.ts.map +1 -1
  39. package/dist/composables/useSchemaField.d.ts +2 -2
  40. package/dist/composables/useSchemaField.d.ts.map +1 -1
  41. package/dist/index.cjs +19 -19
  42. package/dist/index.mjs +10 -10
  43. package/dist/style.css +1 -1
  44. package/dist/types/BagelForm.d.ts +25 -13
  45. package/dist/types/BagelForm.d.ts.map +1 -1
  46. package/dist/utils/BagelFormUtils.d.ts +11 -8
  47. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  48. package/dist/utils/calendar/dateUtils.d.ts +21 -0
  49. package/dist/utils/calendar/dateUtils.d.ts.map +1 -1
  50. package/dist/utils/elementUtils.d.ts +5 -0
  51. package/dist/utils/elementUtils.d.ts.map +1 -1
  52. package/dist/utils/useSearch.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/Card.vue +1 -2
  55. package/src/components/DataPreview.vue +1 -1
  56. package/src/components/ImportData.vue +94 -88
  57. package/src/components/ListItem.vue +32 -24
  58. package/src/components/analytics/BarChart.vue +153 -0
  59. package/src/components/analytics/KpiCard.vue +84 -0
  60. package/src/components/analytics/LineChart.vue +267 -0
  61. package/src/components/analytics/PieChart.vue +183 -0
  62. package/src/components/analytics/index.ts +4 -0
  63. package/src/components/calendar/Index.vue +15 -35
  64. package/src/components/calendar/views/MonthView.vue +84 -88
  65. package/src/components/calendar/views/WeekView.vue +143 -89
  66. package/src/components/dataTable/DataTable.vue +2 -3
  67. package/src/components/form/BagelForm.vue +27 -6
  68. package/src/components/form/inputs/DateInput.vue +2 -2
  69. package/src/components/form/inputs/DatePicker.vue +42 -48
  70. package/src/components/form/inputs/RadioGroup.vue +60 -35
  71. package/src/components/form/inputs/RichText/utils/media.ts +1 -2
  72. package/src/components/form/inputs/SelectInput.vue +94 -101
  73. package/src/components/form/inputs/Upload/upload.css +135 -138
  74. package/src/components/layout/AppContent.vue +125 -0
  75. package/src/components/layout/AppLayout.vue +124 -0
  76. package/src/components/layout/AppSidebar.vue +271 -0
  77. package/src/components/layout/index.ts +5 -0
  78. package/src/composables/useFormField.ts +6 -0
  79. package/src/composables/useSchemaField.ts +38 -10
  80. package/src/styles/inputs.css +9 -0
  81. package/src/styles/theme.css +2 -2
  82. package/src/types/BagelForm.ts +68 -13
  83. package/src/utils/BagelFormUtils.ts +49 -52
  84. package/src/utils/calendar/dateUtils.ts +71 -17
  85. package/src/utils/elementUtils.ts +23 -4
  86. package/src/utils/useSearch.ts +14 -7
  87. /package/src/components/{dialog → calendar}/index.ts +0 -0
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ import { Card, Icon } from '@bagelink/vue'
3
+ import { computed } from 'vue'
4
+
5
+ interface Props {
6
+ title: string
7
+ value: number | string
8
+ icon?: string
9
+ color?: string
10
+ percentageChange?: number
11
+ prefix?: string
12
+ suffix?: string
13
+ currency?: Currency
14
+ loading?: boolean
15
+ subtitle?: string
16
+ }
17
+
18
+ type Currency = 'ILS' | 'USD' | 'EUR'
19
+
20
+ const props = withDefaults(defineProps<Props>(), {
21
+ icon: 'trending_up',
22
+ color: 'var(--bgl-primary)',
23
+ percentageChange: 0,
24
+ prefix: '',
25
+ suffix: '',
26
+ loading: false,
27
+ subtitle: ''
28
+ })
29
+
30
+ const isIncreasing = computed(() => props.percentageChange >= 0)
31
+
32
+ const formattedValue = computed(() => {
33
+ if (typeof props.value === 'string') return props.value
34
+
35
+ if (props.currency) {
36
+ return new Intl.NumberFormat('he-IL', {
37
+ style: 'currency',
38
+ currency: props.currency,
39
+ minimumFractionDigits: 0
40
+ }).format(props.value)
41
+ }
42
+
43
+ return props.value.toLocaleString()
44
+ })
45
+
46
+ const trendColor = computed(() => isIncreasing.value ? 'var(--bgl-green)' : 'var(--bgl-red)'
47
+ )
48
+ </script>
49
+
50
+ <template>
51
+ <Card class=" flex column space-between align-items-start py-1 px-1-5 m_p-1 relative ">
52
+ <div class="mb-1 flex space-between align-items-start m_mb-05 w-100p">
53
+ <div class="flex gap-025 align-items-start">
54
+ <Icon :name="icon" size="1" :color="color" class="line-height-08" weight="300" />
55
+ <div>
56
+ <h3 class="txt14 m-0 line-height-12 light opacity-6">
57
+ {{ title }}
58
+ </h3>
59
+ <p v-if="subtitle" class="txt12 color-gray">
60
+ {{ subtitle }}
61
+ </p>
62
+ </div>
63
+ </div>
64
+ <div v-if="percentageChange !== 0" class="kpi-trend flex gap-025 txt12 bold ms-auto" :style="{ color: trendColor }">
65
+ <Icon :name="isIncreasing ? 'trending_up' : 'trending_down'" />
66
+ <span>{{ Math.abs(percentageChange).toFixed(1) }}%</span>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="flex">
71
+ <div class="flex align-items-baseline gap-025 w100p" :class="{ loading }">
72
+ <span v-if="prefix" class="kpi-prefix txt16 semi color-gray">{{ prefix }}</span>
73
+ <span class="kpi-main-value bold txt28 m_txt24 line-height-1">{{ loading ? '...' : formattedValue }}</span>
74
+ <span v-if="suffix" class="kpi-suffix txt16 semi color-gray">{{ suffix }}</span>
75
+ </div>
76
+ </div>
77
+ </Card>
78
+ </template>
79
+
80
+ <style scoped>
81
+ .loading .kpi-main-value {
82
+ color: var(--bgl-gray);
83
+ }
84
+ </style>
@@ -0,0 +1,267 @@
1
+ <script setup lang="ts">
2
+
3
+ import { computed, onMounted, ref } from 'vue'
4
+ import { Icon } from '@bagelink/vue'
5
+
6
+ interface DataPoint {
7
+ date: string
8
+ value: number
9
+ label?: string
10
+ }
11
+
12
+ interface Props {
13
+ data: DataPoint[]
14
+ title?: string
15
+ icon?: string
16
+ color?: string
17
+ height?: number
18
+ showPoints?: boolean
19
+ currency?: boolean
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ title: 'Line Chart',
24
+ icon: 'trending_up',
25
+ color: 'var(--bgl-primary)',
26
+ height: 200,
27
+ showPoints: true,
28
+ currency: false
29
+ })
30
+
31
+ const svgRef = ref<SVGElement>()
32
+ const width = ref(600)
33
+ const height = ref(props.height)
34
+
35
+ const padding = { top: 20, inline_end: 30, bottom: 25, inline_start: 60 }
36
+ const chartWidth = computed(() => width.value - padding.inline_start - padding.inline_end)
37
+ const chartHeight = computed(() => height.value - padding.top - padding.bottom)
38
+
39
+ // RTL-aware padding calculations
40
+ const paddingLeft = computed(() => {
41
+ const isRTL = document.documentElement.dir === 'rtl' ||
42
+ document.documentElement.getAttribute('lang') === 'he'
43
+ return isRTL ? padding.inline_end : padding.inline_start
44
+ })
45
+
46
+ // RTL-aware label positioning
47
+ const labelXPosition = computed(() => {
48
+ const isRTL = document.documentElement.dir === 'rtl' ||
49
+ document.documentElement.getAttribute('lang') === 'he'
50
+ return isRTL ? paddingLeft.value + chartWidth.value + 10 : paddingLeft.value - 10
51
+ })
52
+
53
+ const labelTextAnchor = computed(() => {
54
+ const isRTL = document.documentElement.dir === 'rtl' ||
55
+ document.documentElement.getAttribute('lang') === 'he'
56
+ return isRTL ? 'start' : 'end'
57
+ })
58
+
59
+ const maxValue = computed(() => Math.max(...props.data.map(d => d.value), 0))
60
+ const minValue = computed(() => Math.min(...props.data.map(d => d.value), 0))
61
+
62
+ const xScale = computed(() => {
63
+ const isRTL = document.documentElement.dir === 'rtl' ||
64
+ document.documentElement.getAttribute('lang') === 'he'
65
+ const domain = props.data.length - 1
66
+
67
+ return (index: number) => {
68
+ const position = (index / domain) * chartWidth.value
69
+ // In RTL, reverse the x position so the chart flows from right to left
70
+ return isRTL ? chartWidth.value - position : position
71
+ }
72
+ })
73
+
74
+ const yScale = computed(() => {
75
+ const range = maxValue.value - minValue.value || 1
76
+ return (value: number) => chartHeight.value - ((value - minValue.value) / range) * chartHeight.value
77
+ })
78
+
79
+ const pathData = computed(() => {
80
+ if (props.data.length === 0) return ''
81
+
82
+ const points = props.data.map((d, i) =>
83
+ `${i === 0 ? 'M' : 'L'} ${paddingLeft.value + xScale.value(i)} ${padding.top + yScale.value(d.value)}`
84
+ ).join(' ')
85
+
86
+ return points
87
+ })
88
+
89
+ const gridLines = computed(() => {
90
+ const lines = []
91
+ const step = chartHeight.value / 4
92
+ for (let i = 0; i <= 4; i++) {
93
+ const y = i * step
94
+ const value = maxValue.value - (i / 4) * (maxValue.value - minValue.value)
95
+ lines.push({ y, value })
96
+ }
97
+ return lines
98
+ })
99
+
100
+ function formatValue(value: number): string {
101
+ if (props.currency) {
102
+ return new Intl.NumberFormat('he-IL', {
103
+ style: 'currency',
104
+ currency: 'ILS',
105
+ minimumFractionDigits: 0
106
+ }).format(value)
107
+ }
108
+ return value.toLocaleString()
109
+ }
110
+
111
+ function formatDate(dateStr: string): string {
112
+ const date = new Date(dateStr)
113
+ return date.toLocaleDateString('he-IL', { month: 'short', day: 'numeric' })
114
+ }
115
+
116
+ onMounted(() => {
117
+ if (svgRef.value) {
118
+ width.value = svgRef.value.clientWidth || 600
119
+ }
120
+ })
121
+ </script>
122
+
123
+ <template>
124
+ <div class="line-chart h-100p flex column flex-stretch">
125
+ <div class="flex space-between">
126
+ <div class="flex align-center gap-05 pb-1">
127
+ <Icon :name="icon" size="1.2" :color="color" class="line-height-08" />
128
+ <p class="white-space light m_txt14">
129
+ {{ title }}
130
+ </p>
131
+ </div>
132
+ </div>
133
+ <div class="chart-container flex-grow w-100p relative">
134
+ <svg
135
+ ref="svgRef"
136
+ :width="width"
137
+ :height="height"
138
+ class="chart-svg h-100p w-100p"
139
+ >
140
+ <!-- Grid lines -->
141
+ <g class="grid">
142
+ <line
143
+ v-for="line in gridLines"
144
+ :key="line.y"
145
+ :x1="paddingLeft"
146
+ :y1="padding.top + line.y"
147
+ :x2="paddingLeft + chartWidth"
148
+ :y2="padding.top + line.y"
149
+ stroke="#e0e0e0"
150
+ stroke-width="1"
151
+ />
152
+ <!-- Y-axis labels -->
153
+ <text
154
+ v-for="line in gridLines"
155
+ :key="`label-${line.y}`"
156
+ :x="labelXPosition"
157
+ :y="padding.top + line.y + 4"
158
+ class="grid-label"
159
+ :text-anchor="labelTextAnchor"
160
+ >
161
+ {{ formatValue(line.value) }}
162
+ </text>
163
+ </g>
164
+
165
+ <!-- X-axis -->
166
+ <line
167
+ :x1="paddingLeft"
168
+ :y1="padding.top + chartHeight"
169
+ :x2="paddingLeft + chartWidth"
170
+ :y2="padding.top + chartHeight"
171
+ stroke="#ccc"
172
+ stroke-width="2"
173
+ />
174
+
175
+ <!-- Y-axis -->
176
+ <line
177
+ :x1="paddingLeft"
178
+ :y1="padding.top"
179
+ :x2="paddingLeft"
180
+ :y2="padding.top + chartHeight"
181
+ stroke="#ccc"
182
+ stroke-width="2"
183
+ />
184
+
185
+ <!-- Line path -->
186
+ <path
187
+ :d="pathData"
188
+ :stroke="color"
189
+ stroke-width="3"
190
+ fill="none"
191
+ stroke-linecap="round"
192
+ stroke-linejoin="round"
193
+ />
194
+
195
+ <!-- Data points -->
196
+ <g v-if="showPoints">
197
+ <circle
198
+ v-for="(point, index) in data"
199
+ :key="index"
200
+ :cx="paddingLeft + xScale(index)"
201
+ :cy="padding.top + yScale(point.value)"
202
+ r="4"
203
+ :fill="color"
204
+ class="data-point"
205
+ >
206
+ <title>{{ formatDate(point.date) }}: {{ formatValue(point.value) }}</title>
207
+ </circle>
208
+ </g>
209
+
210
+ <!-- X-axis labels -->
211
+ <g class="x-labels">
212
+ <text
213
+ v-for="(point, index) in data.filter((_, i) => i % Math.ceil(data.length / 6) === 0)"
214
+ :key="index"
215
+ :x="paddingLeft + xScale(data.findIndex(d => d === point))"
216
+ :y="padding.top + chartHeight + 15"
217
+ class="axis-label"
218
+ text-anchor="middle"
219
+ >
220
+ {{ formatDate(point.date) }}
221
+ </text>
222
+ </g>
223
+ </svg>
224
+ </div>
225
+ </div>
226
+ </template>
227
+
228
+ <style scoped>
229
+ .line-chart, .chart-container {
230
+ direction: inherit;
231
+ }
232
+
233
+ .chart-svg {
234
+ transform-origin: center;
235
+ display: block; /* Remove any inline spacing */
236
+ }
237
+
238
+ .chart-container {
239
+ overflow: hidden; /* Prevent any overflow spacing */
240
+ }
241
+
242
+ .grid-label,
243
+ .axis-label {
244
+ fill: var(--bgl-gray) !important;;
245
+ font-family: system-ui, -apple-system, sans-serif;
246
+ font-size: 12px;
247
+ margin: 1rem;
248
+ }
249
+
250
+ .axis-label {
251
+ text-anchor: middle;
252
+ }
253
+
254
+ .data-point {
255
+ cursor: pointer;
256
+ transition: r 0.2s ease;
257
+ }
258
+
259
+ .data-point:hover {
260
+ r: 6;
261
+ }
262
+
263
+ [dir="rtl"] .line-chart {
264
+ text-align: right;
265
+ }
266
+
267
+ </style>
@@ -0,0 +1,183 @@
1
+ <script setup lang="ts">
2
+ import { Icon } from '@bagelink/vue'
3
+ import { computed } 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
+ size?: number
17
+ showLegend?: boolean
18
+ donut?: boolean
19
+ }
20
+
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ title: 'Pie Chart',
23
+ icon: 'bx-pie-chart-alt',
24
+ color: '#3B82F6',
25
+ size: 200,
26
+ showLegend: true,
27
+ donut: false
28
+ })
29
+
30
+ // Generate colors with different opacity based on the main color
31
+ const colors = computed(() => {
32
+ const baseColor = props.color
33
+ const opacities = [1, 0.8, 0.6, 0.4, 0.85, 0.7, 0.5, 0.3, 0.9, 0.75]
34
+
35
+ return opacities.map((opacity) => {
36
+ // Convert hex to rgba with opacity
37
+ 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)
41
+
42
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`
43
+ })
44
+ })
45
+
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
+ // })
53
+
54
+ const totalValue = computed(() => props.data.reduce((sum, item) => sum + item.value, 0)
55
+ )
56
+
57
+ const pieSegments = computed(() => {
58
+ let cumulativeAngle = 0
59
+ return props.data.map((item, index) => {
60
+ const percentage = item.value / totalValue.value
61
+ const angle = percentage * 360
62
+ const startAngle = cumulativeAngle
63
+ const endAngle = cumulativeAngle + angle
64
+ cumulativeAngle += angle
65
+
66
+ const startAngleRad = (startAngle * Math.PI) / 180
67
+ const endAngleRad = (endAngle * Math.PI) / 180
68
+
69
+ const radius = props.size / 2 - 10
70
+ const innerRadius = props.donut ? radius * 0.6 : 0
71
+
72
+ const largeArcFlag = angle > 180 ? 1 : 0
73
+
74
+ const pathData = props.donut
75
+ ? createDonutPath(startAngleRad, endAngleRad, radius, innerRadius, largeArcFlag)
76
+ : createPiePath(startAngleRad, endAngleRad, radius, largeArcFlag)
77
+
78
+ return {
79
+ ...item,
80
+ percentage,
81
+ angle,
82
+ startAngle,
83
+ endAngle,
84
+ pathData,
85
+ color: item.color || colors.value[index % colors.value.length]
86
+ }
87
+ })
88
+ })
89
+
90
+ function createPiePath(startAngle: number, endAngle: number, radius: number, largeArcFlag: number): string {
91
+ const x1 = Math.cos(startAngle) * radius
92
+ const y1 = Math.sin(startAngle) * radius
93
+ const x2 = Math.cos(endAngle) * radius
94
+ const y2 = Math.sin(endAngle) * radius
95
+
96
+ return `M 0 0 L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2} Z`
97
+ }
98
+
99
+ function createDonutPath(startAngle: number, endAngle: number, outerRadius: number, innerRadius: number, largeArcFlag: number): string {
100
+ const x1Outer = Math.cos(startAngle) * outerRadius
101
+ const y1Outer = Math.sin(startAngle) * outerRadius
102
+ const x2Outer = Math.cos(endAngle) * outerRadius
103
+ const y2Outer = Math.sin(endAngle) * outerRadius
104
+
105
+ const x1Inner = Math.cos(startAngle) * innerRadius
106
+ const y1Inner = Math.sin(startAngle) * innerRadius
107
+ const x2Inner = Math.cos(endAngle) * innerRadius
108
+ const y2Inner = Math.sin(endAngle) * innerRadius
109
+
110
+ return `M ${x1Outer} ${y1Outer} A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${x2Outer} ${y2Outer} L ${x2Inner} ${y2Inner} A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${x1Inner} ${y1Inner} Z`
111
+ }
112
+
113
+ function formatValue(value: number): string {
114
+ return value.toLocaleString()
115
+ }
116
+
117
+ function formatPercentage(percentage: number): string {
118
+ return `${(percentage * 100).toFixed(1)}%`
119
+ }
120
+ </script>
121
+
122
+ <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" />
127
+ <p class="white-space light m_txt14">
128
+ {{ title }}
129
+ </p>
130
+ </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>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </template>
159
+
160
+ <style scoped>
161
+ [dir="rtl"] .chart-container.with-legend {
162
+ flex-direction: row-reverse;
163
+ }
164
+
165
+ .pie-segment {
166
+ cursor: pointer;
167
+ transition: opacity 0.2s ease;
168
+ }
169
+
170
+ .pie-segment:hover {
171
+ opacity: 0.8;
172
+ }
173
+
174
+ /* RTL support for legend */
175
+ [dir="rtl"] .legend-item {
176
+ flex-direction: row-reverse;
177
+ }
178
+
179
+ .legend-color {
180
+ width: 12px;
181
+ height: 12px;
182
+ }
183
+ </style>
@@ -0,0 +1,4 @@
1
+ export { default as BarChart } from './BarChart.vue'
2
+ export { default as LineChart } from './LineChart.vue'
3
+ export { default as KpiCard } from './KpiCard.vue'
4
+ export { default as PieChart } from './PieChart.vue'
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Component } from 'vue'
3
3
  import type { CalendarEvent, CalendarProps, CalendarView, CalendarViewState } from './CalendarTypes'
4
- import { timeDelta, Btn, ListItem, Dropdown, fmtDate } from '@bagelink/vue'
4
+ import { timeDelta, Btn, ListItem, Dropdown, formatDate } from '@bagelink/vue'
5
5
  import { ref, computed, onMounted } from 'vue'
6
6
  import CalendarPopover from './CalendarPopover.vue'
7
7
  import AgendaView from './views/AgendaView.vue'
@@ -124,44 +124,28 @@ onMounted(() => { emit('ready', state.value) })
124
124
  <div class="calendar">
125
125
  <div class="flex m_block m_pb-1">
126
126
  <h3 class="txt-light my-0">
127
- <b>{{ fmtDate(currentDate, { fmt: 'MMMM' }) }}</b>
128
- {{ fmtDate(currentDate, { fmt: 'YYYY' }) }}
127
+ <b>{{ formatDate(currentDate, 'MMMM') }}</b>
128
+ {{ formatDate(currentDate, 'YYYY') }}
129
129
  </h3>
130
130
  <div class="ms-auto flex gap-025">
131
131
  <Dropdown thin :value="currentView" iconEnd="keyboard_arrow_down" color="gray">
132
- <ListItem
133
- v-for="(_, key) in views"
134
- :key="key"
135
- :title="key"
136
- @click="handleViewChange(key)"
137
- />
132
+ <ListItem v-for="(_, key) in views" :key="key" thin :title="key" @click="handleViewChange(key)" />
138
133
  </Dropdown>
139
134
  <Btn icon="calendar" thin color="gray" value="Today" @click="handleDateChange(new Date())" />
140
135
  <Btn
141
- icon="chevron_left"
142
- color="gray"
143
- thin
136
+ icon="chevron_left" color="gray" thin
144
137
  @click="handleDateChange(timeDelta(currentDate, { [currentView]: -1 }))"
145
138
  />
146
139
  <Btn
147
- icon="chevron_right"
148
- color="gray"
149
- thin
140
+ icon="chevron_right" color="gray" thin
150
141
  @click="handleDateChange(timeDelta(currentDate, { [currentView]: 1 }))"
151
142
  />
152
143
  </div>
153
144
  </div>
154
145
  <component
155
- :is="views[currentView]"
156
- :events="events"
157
- :start-date="currentDate"
158
- :week-start="weekStart"
159
- @event-click="handleEventClick"
160
- @event-create="handleEventCreate"
161
- @event-update="handleEventUpdate"
162
- @event-delete="handleEventDelete"
163
- @date-change="handleDateChange"
164
- @open-popover="openPopover"
146
+ :is="views[currentView]" :events="events" :start-date="currentDate" :week-start="weekStart"
147
+ @event-click="handleEventClick" @event-create="handleEventCreate" @event-update="handleEventUpdate"
148
+ @event-delete="handleEventDelete" @date-change="handleDateChange" @open-popover="openPopover"
165
149
  >
166
150
  <template #eventContent="{ event }">
167
151
  <slot name="eventContent" :event="event" />
@@ -170,12 +154,8 @@ onMounted(() => { emit('ready', state.value) })
170
154
 
171
155
  <!-- Global Popover -->
172
156
  <CalendarPopover
173
- :event="activeEvent"
174
- :position="popoverPosition"
175
- :show="showPopover"
176
- :has-event-content-slot="!!$slots.eventContent"
177
- @close="closePopover"
178
- @event-click="handleEventClick"
157
+ :event="activeEvent" :position="popoverPosition" :show="showPopover"
158
+ :has-event-content-slot="!!$slots.eventContent" @close="closePopover" @event-click="handleEventClick"
179
159
  >
180
160
  <template #eventContent="{ event }">
181
161
  <slot name="eventContent" :event="event" />
@@ -186,9 +166,9 @@ onMounted(() => { emit('ready', state.value) })
186
166
 
187
167
  <style scoped>
188
168
  .calendar {
189
- display: flex;
190
- flex-direction: column;
191
- height: 100%;
192
- width: 100%;
169
+ display: flex;
170
+ flex-direction: column;
171
+ height: 100%;
172
+ width: 100%;
193
173
  }
194
174
  </style>