@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.
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Carousel.vue.d.ts +1 -1
- package/dist/components/Modal.vue.d.ts +3 -0
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/Slider.vue.d.ts +1 -1
- package/dist/components/Slider.vue.d.ts.map +1 -1
- package/dist/components/analytics/BarChart.vue.d.ts +11 -3
- package/dist/components/analytics/BarChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/LineChart.vue.d.ts +9 -0
- package/dist/components/analytics/LineChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/PieChart.vue.d.ts +30 -2
- package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts +8 -0
- 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 +9 -0
- package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts.map +1 -0
- package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +0 -14
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts +15 -15
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/richTextTypes.d.ts +1 -3
- package/dist/components/form/inputs/RichText/richTextTypes.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/media-clean.d.ts +2 -0
- package/dist/components/form/inputs/RichText/utils/media-clean.d.ts.map +1 -0
- package/dist/components/form/inputs/RichText/utils/media.d.ts +4 -4
- package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/table.d.ts +1 -1
- package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/index.cjs +123 -22
- package/dist/index.mjs +123 -22
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Btn.vue +50 -42
- package/src/components/Modal.vue +49 -50
- package/src/components/analytics/BarChart.vue +118 -7
- package/src/components/analytics/KpiCard.vue +2 -2
- package/src/components/analytics/LineChart.vue +189 -105
- package/src/components/analytics/PieChart.vue +392 -49
- package/src/components/dataTable/DataTable.vue +1 -1
- package/src/components/form/inputs/RichText/CheckList.md +23 -0
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +243 -27
- package/src/components/form/inputs/RichText/components/TableGridSelector.vue +94 -0
- package/src/components/form/inputs/RichText/composables/useCommands.ts +45 -0
- package/src/components/form/inputs/RichText/composables/useEditor.ts +13 -10
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +3 -128
- package/src/components/form/inputs/RichText/config.ts +33 -10
- package/src/components/form/inputs/RichText/editor.css +300 -33
- package/src/components/form/inputs/RichText/index.vue +3271 -130
- package/src/components/form/inputs/RichText/richTextTypes.ts +7 -3
- package/src/components/form/inputs/RichText/utils/commands.ts +851 -90
- package/src/components/form/inputs/RichText/utils/formatting.ts +17 -15
- package/src/components/form/inputs/RichText/utils/media-clean.ts +0 -0
- package/src/components/form/inputs/RichText/utils/media.ts +133 -67
- package/src/components/form/inputs/RichText/utils/selection.ts +40 -11
- package/src/components/form/inputs/RichText/utils/table.ts +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/AppContent.vue +26 -26
- package/src/components/layout/AppLayout.vue +21 -3
- package/src/components/layout/AppSidebar.vue +5 -2
- package/src/styles/layout.css +267 -0
- package/src/styles/mobilLayout.css +266 -0
- package/src/styles/modal.css +3 -17
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
|
|
3
|
-
import { computed, onMounted, ref } from 'vue'
|
|
3
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
4
4
|
import { Icon } from '@bagelink/vue'
|
|
5
5
|
|
|
6
6
|
interface DataPoint {
|
|
@@ -17,6 +17,10 @@ interface Props {
|
|
|
17
17
|
height?: number
|
|
18
18
|
showPoints?: boolean
|
|
19
19
|
currency?: boolean
|
|
20
|
+
animated?: boolean
|
|
21
|
+
animationDuration?: number
|
|
22
|
+
animationStartDelay?: number
|
|
23
|
+
percentageChange?: number
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -25,13 +29,25 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
25
29
|
color: 'var(--bgl-primary)',
|
|
26
30
|
height: 200,
|
|
27
31
|
showPoints: true,
|
|
28
|
-
currency: false
|
|
32
|
+
currency: false,
|
|
33
|
+
animated: true,
|
|
34
|
+
animationDuration: 1500,
|
|
35
|
+
animationStartDelay: 0,
|
|
36
|
+
percentageChange: 0
|
|
29
37
|
})
|
|
30
38
|
|
|
31
39
|
const svgRef = ref<SVGElement>()
|
|
40
|
+
const chartRef = ref<HTMLElement>()
|
|
32
41
|
const width = ref(600)
|
|
33
42
|
const height = ref(props.height)
|
|
34
43
|
|
|
44
|
+
// Animation state
|
|
45
|
+
const animatedProgress = ref(0)
|
|
46
|
+
const isAnimating = ref(false)
|
|
47
|
+
const isInView = ref(false)
|
|
48
|
+
|
|
49
|
+
let observer: IntersectionObserver | null = null
|
|
50
|
+
|
|
35
51
|
const padding = { top: 20, inline_end: 30, bottom: 25, inline_start: 60 }
|
|
36
52
|
const chartWidth = computed(() => width.value - padding.inline_start - padding.inline_end)
|
|
37
53
|
const chartHeight = computed(() => height.value - padding.top - padding.bottom)
|
|
@@ -79,13 +95,29 @@ const yScale = computed(() => {
|
|
|
79
95
|
const pathData = computed(() => {
|
|
80
96
|
if (props.data.length === 0) return ''
|
|
81
97
|
|
|
82
|
-
|
|
98
|
+
// Calculate how many points to show based on animation progress
|
|
99
|
+
const totalPoints = props.data.length
|
|
100
|
+
const visiblePoints = props.animated ?
|
|
101
|
+
Math.ceil(totalPoints * animatedProgress.value) :
|
|
102
|
+
totalPoints
|
|
103
|
+
|
|
104
|
+
const dataToShow = props.data.slice(0, Math.max(1, visiblePoints))
|
|
105
|
+
|
|
106
|
+
const points = dataToShow.map((d, i) =>
|
|
83
107
|
`${i === 0 ? 'M' : 'L'} ${paddingLeft.value + xScale.value(i)} ${padding.top + yScale.value(d.value)}`
|
|
84
108
|
).join(' ')
|
|
85
109
|
|
|
86
110
|
return points
|
|
87
111
|
})
|
|
88
112
|
|
|
113
|
+
const visiblePoints = computed(() => {
|
|
114
|
+
if (!props.animated) return props.data
|
|
115
|
+
|
|
116
|
+
const totalPoints = props.data.length
|
|
117
|
+
const pointsToShow = Math.ceil(totalPoints * animatedProgress.value)
|
|
118
|
+
return props.data.slice(0, Math.max(1, pointsToShow))
|
|
119
|
+
})
|
|
120
|
+
|
|
89
121
|
const gridLines = computed(() => {
|
|
90
122
|
const lines = []
|
|
91
123
|
const step = chartHeight.value / 4
|
|
@@ -113,135 +145,170 @@ function formatDate(dateStr: string): string {
|
|
|
113
145
|
return date.toLocaleDateString('he-IL', { month: 'short', day: 'numeric' })
|
|
114
146
|
}
|
|
115
147
|
|
|
148
|
+
function formatTooltip(point: any): string {
|
|
149
|
+
const tooltipLines = [
|
|
150
|
+
`${formatDate(point.date)}`,
|
|
151
|
+
`<b>${formatValue(point.value)}</b>`
|
|
152
|
+
]
|
|
153
|
+
if (point.label) {
|
|
154
|
+
tooltipLines.push(`<b>${point.label}</b>`)
|
|
155
|
+
}
|
|
156
|
+
return `<div class="lineTooltip">${tooltipLines.join('<p>')}</div>`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const setupIntersectionObserver = () => {
|
|
160
|
+
if (typeof window === 'undefined') return
|
|
161
|
+
|
|
162
|
+
observer = new IntersectionObserver(
|
|
163
|
+
(entries) => {
|
|
164
|
+
entries.forEach((entry) => {
|
|
165
|
+
if (entry.isIntersecting && !isInView.value) {
|
|
166
|
+
isInView.value = true
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
startAnimation()
|
|
169
|
+
}, 100 + props.animationStartDelay)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
threshold: 0.3,
|
|
175
|
+
rootMargin: '50px'
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if (chartRef.value) {
|
|
180
|
+
observer.observe(chartRef.value)
|
|
181
|
+
} else {
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
if (chartRef.value && observer) {
|
|
184
|
+
observer.observe(chartRef.value)
|
|
185
|
+
}
|
|
186
|
+
}, 100)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const startAnimation = () => {
|
|
191
|
+
console.log(`Starting LineChart animation for "${props.title}" (duration: ${props.animationDuration}ms, start delay: ${props.animationStartDelay}ms)`)
|
|
192
|
+
isAnimating.value = true
|
|
193
|
+
animatedProgress.value = 0
|
|
194
|
+
|
|
195
|
+
const startTime = Date.now()
|
|
196
|
+
|
|
197
|
+
const animate = () => {
|
|
198
|
+
const elapsed = Date.now() - startTime
|
|
199
|
+
const progress = Math.min(elapsed / props.animationDuration, 1)
|
|
200
|
+
|
|
201
|
+
animatedProgress.value = easeOutCubic(progress)
|
|
202
|
+
|
|
203
|
+
if (progress < 1) {
|
|
204
|
+
requestAnimationFrame(animate)
|
|
205
|
+
} else {
|
|
206
|
+
isAnimating.value = false
|
|
207
|
+
console.log(`LineChart animation completed for "${props.title}"`)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
requestAnimationFrame(animate)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const easeOutCubic = (t: number): number => {
|
|
215
|
+
return 1 - Math.pow(1 - t, 3)
|
|
216
|
+
}
|
|
217
|
+
|
|
116
218
|
onMounted(() => {
|
|
117
219
|
if (svgRef.value) {
|
|
118
220
|
width.value = svgRef.value.clientWidth || 600
|
|
119
221
|
}
|
|
222
|
+
|
|
223
|
+
if (props.animated) {
|
|
224
|
+
setupIntersectionObserver()
|
|
225
|
+
} else {
|
|
226
|
+
animatedProgress.value = 1
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
onUnmounted(() => {
|
|
231
|
+
if (observer) {
|
|
232
|
+
observer.disconnect()
|
|
233
|
+
}
|
|
120
234
|
})
|
|
121
235
|
</script>
|
|
122
236
|
|
|
123
237
|
<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
|
|
127
|
-
<Icon :name="icon" size="1.2" :color="color" class="line-height-
|
|
238
|
+
<div ref="chartRef" class="line-chart h-100p flex column flex-stretch">
|
|
239
|
+
<div class="flex space-between pb-1">
|
|
240
|
+
<div class="flex align-center gap-05">
|
|
241
|
+
<Icon :name="icon" size="1.2" :color="color" class="line-height-0" />
|
|
128
242
|
<p class="white-space light m_txt14">
|
|
129
243
|
{{ title }}
|
|
130
244
|
</p>
|
|
131
245
|
</div>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<
|
|
143
|
-
v-for="line in gridLines"
|
|
144
|
-
|
|
145
|
-
:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
>
|
|
161
|
-
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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>
|
|
246
|
+
<div v-if="percentageChange !== 0" class="flex align-center gap-025">
|
|
247
|
+
<Icon :name="percentageChange > 0 ? 'trending_up' : 'trending_down'" size="1" :class="percentageChange > 0 ? 'color-success' : 'color-danger'" />
|
|
248
|
+
<span class="txt12 bold" :class="percentageChange > 0 ? 'color-success' : 'color-danger'">
|
|
249
|
+
{{ Math.abs(percentageChange) }}%
|
|
250
|
+
</span>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
<div class="chart-container flex-grow w-100p relative">
|
|
254
|
+
<svg ref="svgRef" :width="width" :height="height" class="chart-svg h-100p w-100p">
|
|
255
|
+
<!-- Grid lines -->
|
|
256
|
+
<g class="grid">
|
|
257
|
+
<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" />
|
|
258
|
+
<!-- Y-axis labels -->
|
|
259
|
+
<text v-for="line in gridLines" :key="`label-${line.y}`" :x="labelXPosition" :y="padding.top + line.y + 4" class="grid-label" :text-anchor="labelTextAnchor">
|
|
260
|
+
{{ formatValue(line.value) }}
|
|
261
|
+
</text>
|
|
262
|
+
</g>
|
|
263
|
+
|
|
264
|
+
<!-- X-axis -->
|
|
265
|
+
<line :x1="paddingLeft" :y1="padding.top + chartHeight" :x2="paddingLeft + chartWidth" :y2="padding.top + chartHeight" stroke="#ccc" stroke-width="2" />
|
|
266
|
+
|
|
267
|
+
<!-- Y-axis -->
|
|
268
|
+
<line :x1="paddingLeft" :y1="padding.top" :x2="paddingLeft" :y2="padding.top + chartHeight" stroke="#ccc" stroke-width="2" />
|
|
269
|
+
|
|
270
|
+
<!-- Line path -->
|
|
271
|
+
<path :d="pathData" :stroke="color" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round" />
|
|
272
|
+
|
|
273
|
+
<!-- Data points -->
|
|
274
|
+
<g v-if="showPoints">
|
|
275
|
+
<circle v-for="(point, index) in visiblePoints" :key="index" v-tooltip="{ content: formatTooltip(point), html: true }" :cx="paddingLeft + xScale(index)"
|
|
276
|
+
:cy="padding.top + yScale(point.value)" r="4" :fill="color" class="data-point" />
|
|
277
|
+
</g>
|
|
278
|
+
|
|
279
|
+
<!-- X-axis labels -->
|
|
280
|
+
<g class="x-labels">
|
|
281
|
+
<text v-for="(point, index) in visiblePoints.filter((_, i) => i % Math.ceil(visiblePoints.length / 6) === 0)" :key="index"
|
|
282
|
+
:x="paddingLeft + xScale(visiblePoints.findIndex(d => d === point))" :y="padding.top + chartHeight + 15" class="axis-label" text-anchor="middle">
|
|
283
|
+
{{ formatDate(point.date) }}
|
|
284
|
+
</text>
|
|
285
|
+
</g>
|
|
286
|
+
</svg>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
226
289
|
</template>
|
|
227
290
|
|
|
228
291
|
<style scoped>
|
|
229
|
-
.line-chart,
|
|
292
|
+
.line-chart,
|
|
293
|
+
.chart-container {
|
|
230
294
|
direction: inherit;
|
|
231
295
|
}
|
|
232
296
|
|
|
233
297
|
.chart-svg {
|
|
234
298
|
transform-origin: center;
|
|
235
|
-
display: block;
|
|
299
|
+
display: block;
|
|
300
|
+
/* Remove any inline spacing */
|
|
236
301
|
}
|
|
237
302
|
|
|
238
303
|
.chart-container {
|
|
239
|
-
overflow: hidden;
|
|
304
|
+
overflow: hidden;
|
|
305
|
+
/* Prevent any overflow spacing */
|
|
240
306
|
}
|
|
241
307
|
|
|
242
308
|
.grid-label,
|
|
243
309
|
.axis-label {
|
|
244
|
-
fill: var(--bgl-gray) !important
|
|
310
|
+
fill: var(--bgl-gray) !important;
|
|
311
|
+
;
|
|
245
312
|
font-family: system-ui, -apple-system, sans-serif;
|
|
246
313
|
font-size: 12px;
|
|
247
314
|
margin: 1rem;
|
|
@@ -263,5 +330,22 @@ onMounted(() => {
|
|
|
263
330
|
[dir="rtl"] .line-chart {
|
|
264
331
|
text-align: right;
|
|
265
332
|
}
|
|
333
|
+
</style>
|
|
334
|
+
|
|
335
|
+
<style>
|
|
336
|
+
.lineTooltip p {
|
|
337
|
+
font-weight: 300 !important;
|
|
338
|
+
font-size: 12px;
|
|
339
|
+
}
|
|
266
340
|
|
|
341
|
+
.lineTooltip {
|
|
342
|
+
font-weight: 700 !important;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.v-popper--theme-tooltip .v-popper__inner:has(.lineTooltip) {
|
|
346
|
+
background-color: var(--bgl-black) !important;
|
|
347
|
+
color: var(--bgl-white) !important;
|
|
348
|
+
border-radius: 0.5rem !important;
|
|
349
|
+
padding: 0.25rem 0.5rem !important;
|
|
350
|
+
}
|
|
267
351
|
</style>
|