@eturnity/eturnity_reusable_components 8.13.3-EPDM-14458.0 → 8.13.3-EPDM-14657.0
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/package.json +1 -1
- package/src/DemoCharts.vue +424 -0
- package/src/TestChart.vue +241 -0
- package/src/assets/svgIcons/refresh.svg +3 -0
- package/src/assets/theme.js +16 -0
- package/src/components/barchart/BottomFields.vue +253 -0
- package/src/components/barchart/ChartControls.vue +113 -0
- package/src/components/barchart/SelectionBox.vue +150 -0
- package/src/components/barchart/composables/index.js +5 -0
- package/src/components/barchart/composables/useAxisCalculations.js +104 -0
- package/src/components/barchart/composables/useChartData.js +114 -0
- package/src/components/barchart/composables/useChartScroll.js +61 -0
- package/src/components/barchart/composables/useSelection.js +75 -0
- package/src/components/barchart/composables/useTooltip.js +100 -0
- package/src/components/barchart/index.vue +376 -0
- package/src/components/barchart/styles/bottomFields.js +66 -0
- package/src/components/barchart/styles/chart.js +259 -0
- package/src/components/barchart/styles/chartControls.js +59 -0
- package/src/components/buttons/splitButtons/index.vue +86 -0
- package/src/components/collapsableInfoText/index.vue +2 -2
- package/src/components/inputs/inputNumber/index.vue +14 -2
- package/src/components/modals/modal/index.vue +1 -5
- package/src/helpers/isObjectEqual.js +22 -0
- package/src/main.js +8 -0
- package/src/router/dynamicRoutes.js +12 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
import { computed } from 'vue'
|
2
|
+
import theme from '../../../assets/theme'
|
3
|
+
|
4
|
+
export function useChartData(props, paddedMaxValue) {
|
5
|
+
const GRADIENT_COLORS = theme.chartGradients
|
6
|
+
|
7
|
+
// Get stacked colors based on number of segments
|
8
|
+
const getStackedColors = computed(() => {
|
9
|
+
const colorMap = new Map()
|
10
|
+
|
11
|
+
return (totalSegments) => {
|
12
|
+
if (colorMap.has(totalSegments)) {
|
13
|
+
return colorMap.get(totalSegments)
|
14
|
+
}
|
15
|
+
|
16
|
+
let colors
|
17
|
+
switch (totalSegments) {
|
18
|
+
case 2:
|
19
|
+
colors = [GRADIENT_COLORS.stacked[3], GRADIENT_COLORS.stacked[0]]
|
20
|
+
break
|
21
|
+
case 3:
|
22
|
+
colors = [
|
23
|
+
GRADIENT_COLORS.stacked[5],
|
24
|
+
GRADIENT_COLORS.stacked[3],
|
25
|
+
GRADIENT_COLORS.stacked[1],
|
26
|
+
]
|
27
|
+
break
|
28
|
+
case 4:
|
29
|
+
colors = [
|
30
|
+
GRADIENT_COLORS.stacked[5],
|
31
|
+
GRADIENT_COLORS.stacked[3],
|
32
|
+
GRADIENT_COLORS.stacked[1],
|
33
|
+
GRADIENT_COLORS.stacked[0],
|
34
|
+
]
|
35
|
+
break
|
36
|
+
case 5:
|
37
|
+
colors = [
|
38
|
+
GRADIENT_COLORS.stacked[5],
|
39
|
+
GRADIENT_COLORS.stacked[3],
|
40
|
+
GRADIENT_COLORS.stacked[2],
|
41
|
+
GRADIENT_COLORS.stacked[1],
|
42
|
+
GRADIENT_COLORS.stacked[0],
|
43
|
+
]
|
44
|
+
break
|
45
|
+
case 6:
|
46
|
+
colors = [...GRADIENT_COLORS.stacked].reverse()
|
47
|
+
break
|
48
|
+
default:
|
49
|
+
colors = [GRADIENT_COLORS.stacked[0]]
|
50
|
+
}
|
51
|
+
|
52
|
+
colorMap.set(totalSegments, colors)
|
53
|
+
return colors
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
// Normalize data for rendering
|
58
|
+
const normalizedData = computed(() => {
|
59
|
+
if (!props.data?.length) return []
|
60
|
+
|
61
|
+
if (props.series.length) {
|
62
|
+
const stackedColors = [
|
63
|
+
...getStackedColors.value(props.series.length),
|
64
|
+
].reverse()
|
65
|
+
return props.data.map((item) => {
|
66
|
+
let accumulated = 0
|
67
|
+
return {
|
68
|
+
label: item.label,
|
69
|
+
segments: [...props.series].reverse().map((series, index) => {
|
70
|
+
const value =
|
71
|
+
series.data.find((d) => d.label === item.label)?.value || 0
|
72
|
+
accumulated += value
|
73
|
+
return {
|
74
|
+
value,
|
75
|
+
percentage: Number(
|
76
|
+
((accumulated / paddedMaxValue.value) * 100).toFixed(4)
|
77
|
+
),
|
78
|
+
gradientFrom: stackedColors[index].from,
|
79
|
+
gradientTo: stackedColors[index].to,
|
80
|
+
name: series.name,
|
81
|
+
}
|
82
|
+
}),
|
83
|
+
total: accumulated,
|
84
|
+
}
|
85
|
+
})
|
86
|
+
}
|
87
|
+
|
88
|
+
return props.data.map((item) => ({
|
89
|
+
label: item.label,
|
90
|
+
segments: [
|
91
|
+
{
|
92
|
+
value: item.value,
|
93
|
+
percentage: Number(
|
94
|
+
((item.value / paddedMaxValue.value) * 100).toFixed(4)
|
95
|
+
),
|
96
|
+
gradientFrom: GRADIENT_COLORS.simple.from,
|
97
|
+
gradientTo: GRADIENT_COLORS.simple.to,
|
98
|
+
name: 'Value',
|
99
|
+
},
|
100
|
+
],
|
101
|
+
}))
|
102
|
+
})
|
103
|
+
|
104
|
+
// Calculate total value of segments
|
105
|
+
const getTotalSegmentValue = (segments) => {
|
106
|
+
return segments.reduce((sum, segment) => sum + segment.value, 0)
|
107
|
+
}
|
108
|
+
|
109
|
+
return {
|
110
|
+
normalizedData,
|
111
|
+
getStackedColors,
|
112
|
+
getTotalSegmentValue,
|
113
|
+
}
|
114
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
2
|
+
|
3
|
+
export function useChartScroll(
|
4
|
+
chartId,
|
5
|
+
isInputFocused,
|
6
|
+
focusedBarData,
|
7
|
+
showTooltipFromInput
|
8
|
+
) {
|
9
|
+
const chartContent = ref(null)
|
10
|
+
const chartContentWidth = ref(0)
|
11
|
+
|
12
|
+
const updateChartContentWidth = () => {
|
13
|
+
if (chartContent.value) {
|
14
|
+
chartContentWidth.value = chartContent.value.$el.offsetWidth
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
// Handle scroll from chart
|
19
|
+
const handleChartScroll = (event) => {
|
20
|
+
const fieldsContainer = document.querySelector(
|
21
|
+
`.fields-container-${chartId}`
|
22
|
+
)
|
23
|
+
if (fieldsContainer) {
|
24
|
+
fieldsContainer.scrollLeft = event.target.scrollLeft
|
25
|
+
}
|
26
|
+
|
27
|
+
if (isInputFocused.value && focusedBarData.value) {
|
28
|
+
showTooltipFromInput({
|
29
|
+
seriesName: focusedBarData.value.segments[0].name,
|
30
|
+
label: focusedBarData.value.label,
|
31
|
+
})
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
// Handle scroll from bottom fields
|
36
|
+
const handleBottomFieldsScroll = (scrollLeft) => {
|
37
|
+
const chartContainer = document.querySelector(
|
38
|
+
`.chart-scroll-container-${chartId}`
|
39
|
+
)
|
40
|
+
if (chartContainer) {
|
41
|
+
chartContainer.scrollLeft = scrollLeft
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
onMounted(() => {
|
46
|
+
updateChartContentWidth()
|
47
|
+
window.addEventListener('resize', updateChartContentWidth)
|
48
|
+
})
|
49
|
+
|
50
|
+
onUnmounted(() => {
|
51
|
+
window.removeEventListener('resize', updateChartContentWidth)
|
52
|
+
})
|
53
|
+
|
54
|
+
return {
|
55
|
+
chartContent,
|
56
|
+
chartContentWidth,
|
57
|
+
updateChartContentWidth,
|
58
|
+
handleChartScroll,
|
59
|
+
handleBottomFieldsScroll,
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import { ref, computed, watch } from 'vue'
|
2
|
+
import theme from '../../../assets/theme'
|
3
|
+
|
4
|
+
export function useSelection(props, normalizedData, emit) {
|
5
|
+
const selectedIndices = ref({ start: 0, end: props.selectionSize })
|
6
|
+
const currentSelection = ref([])
|
7
|
+
|
8
|
+
watch(
|
9
|
+
() => [props.selectionSize, props.isSelectionEnabled],
|
10
|
+
([newSize, isEnabled]) => {
|
11
|
+
if (isEnabled) {
|
12
|
+
selectedIndices.value = {
|
13
|
+
start: 0,
|
14
|
+
end: newSize,
|
15
|
+
}
|
16
|
+
}
|
17
|
+
},
|
18
|
+
{ immediate: true }
|
19
|
+
)
|
20
|
+
|
21
|
+
// Update visual selection
|
22
|
+
const updateSelectedBars = ({ startIndex, endIndex }) => {
|
23
|
+
selectedIndices.value = { start: startIndex, end: endIndex }
|
24
|
+
currentSelection.value = normalizedData.value.slice(startIndex, endIndex)
|
25
|
+
}
|
26
|
+
|
27
|
+
const handleSelectionDragEnd = () => {
|
28
|
+
emit('selection-change', currentSelection.value)
|
29
|
+
}
|
30
|
+
|
31
|
+
// Check if bar is a selection boundary
|
32
|
+
const isSelectionBoundary = computed(() => {
|
33
|
+
return (index) => {
|
34
|
+
if (!props.isSelectionEnabled) return false
|
35
|
+
return (
|
36
|
+
index === selectedIndices.value.start ||
|
37
|
+
index === selectedIndices.value.end - 1
|
38
|
+
)
|
39
|
+
}
|
40
|
+
})
|
41
|
+
|
42
|
+
// Get segment gradient based on selection state
|
43
|
+
const getSegmentGradient = computed(() => {
|
44
|
+
return (index, segment) => {
|
45
|
+
if (!props.isSelectionEnabled) {
|
46
|
+
return {
|
47
|
+
from: segment.gradientFrom,
|
48
|
+
to: segment.gradientTo,
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
const isSelected =
|
53
|
+
index >= selectedIndices.value.start &&
|
54
|
+
index < selectedIndices.value.end
|
55
|
+
|
56
|
+
return isSelected
|
57
|
+
? {
|
58
|
+
from: segment.gradientFrom,
|
59
|
+
to: segment.gradientTo,
|
60
|
+
}
|
61
|
+
: {
|
62
|
+
from: theme.semanticColors.grey[500],
|
63
|
+
to: theme.semanticColors.grey[500],
|
64
|
+
}
|
65
|
+
}
|
66
|
+
})
|
67
|
+
|
68
|
+
return {
|
69
|
+
selectedIndices,
|
70
|
+
updateSelectedBars,
|
71
|
+
handleSelectionDragEnd,
|
72
|
+
isSelectionBoundary,
|
73
|
+
getSegmentGradient,
|
74
|
+
}
|
75
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import { ref, onUnmounted } from 'vue'
|
2
|
+
import isObjectEqual from '../../../helpers/isObjectEqual'
|
3
|
+
|
4
|
+
export function useTooltip(chartId, normalizedData) {
|
5
|
+
const showTooltipContent = ref(false)
|
6
|
+
const tooltipData = ref(null)
|
7
|
+
const tooltipStyle = ref({})
|
8
|
+
|
9
|
+
const isInputFocused = ref(false)
|
10
|
+
const focusedBarData = ref(null)
|
11
|
+
const tooltipTimeout = ref(null)
|
12
|
+
|
13
|
+
const showTooltip = (item, event, series) => {
|
14
|
+
if (isInputFocused.value) return
|
15
|
+
|
16
|
+
if (!showTooltipContent.value) {
|
17
|
+
showTooltipContent.value = true
|
18
|
+
}
|
19
|
+
if (isObjectEqual(item, tooltipData.value)) return
|
20
|
+
|
21
|
+
tooltipData.value = { ...item }
|
22
|
+
|
23
|
+
const targetElement = series.length
|
24
|
+
? event.target
|
25
|
+
.closest('.bar-group')
|
26
|
+
.querySelector('.bar-segment:last-child')
|
27
|
+
: event.target
|
28
|
+
const rect = targetElement.getBoundingClientRect()
|
29
|
+
|
30
|
+
tooltipStyle.value = {
|
31
|
+
left: `${rect.left + rect.width / 2}px`,
|
32
|
+
top: `${rect.top - 0}px`,
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
const showTooltipFromInput = ({ seriesName, label }) => {
|
37
|
+
if (tooltipTimeout.value) {
|
38
|
+
clearTimeout(tooltipTimeout.value)
|
39
|
+
tooltipTimeout.value = null
|
40
|
+
}
|
41
|
+
|
42
|
+
isInputFocused.value = true
|
43
|
+
const barData = normalizedData.value.find((item) => item.label === label)
|
44
|
+
|
45
|
+
if (!barData) return
|
46
|
+
focusedBarData.value = barData
|
47
|
+
const barElement = document.querySelector(
|
48
|
+
`.barchart-${chartId} .bar-group:nth-child(${
|
49
|
+
normalizedData.value.indexOf(barData) + 1
|
50
|
+
})`
|
51
|
+
)
|
52
|
+
|
53
|
+
if (!barElement) return
|
54
|
+
// Get the last bar segment, samee as hover behavior
|
55
|
+
const targetElement = barElement.querySelector('.bar-segment:last-child')
|
56
|
+
const rect = targetElement.getBoundingClientRect()
|
57
|
+
|
58
|
+
tooltipStyle.value = {
|
59
|
+
left: `${rect.left + rect.width / 2}px`,
|
60
|
+
top: `${rect.top - 0}px`,
|
61
|
+
}
|
62
|
+
showTooltipContent.value = true
|
63
|
+
tooltipData.value = barData
|
64
|
+
}
|
65
|
+
|
66
|
+
// Small delay to prevent tooltip flicker when switching between inputs
|
67
|
+
const handleInputBlurAll = () => {
|
68
|
+
tooltipTimeout.value = setTimeout(() => {
|
69
|
+
hideTooltip(true)
|
70
|
+
isInputFocused.value = false
|
71
|
+
tooltipTimeout.value = null
|
72
|
+
}, 50)
|
73
|
+
}
|
74
|
+
|
75
|
+
const hideTooltip = (isFromInputBlur = false) => {
|
76
|
+
if (typeof isFromInputBlur !== 'boolean' && isInputFocused.value) {
|
77
|
+
return
|
78
|
+
}
|
79
|
+
|
80
|
+
showTooltipContent.value = false
|
81
|
+
}
|
82
|
+
|
83
|
+
onUnmounted(() => {
|
84
|
+
if (!tooltipTimeout.value) return
|
85
|
+
clearTimeout(tooltipTimeout.value)
|
86
|
+
tooltipTimeout.value = null
|
87
|
+
})
|
88
|
+
|
89
|
+
return {
|
90
|
+
showTooltipContent,
|
91
|
+
tooltipData,
|
92
|
+
tooltipStyle,
|
93
|
+
showTooltip,
|
94
|
+
hideTooltip,
|
95
|
+
isInputFocused,
|
96
|
+
focusedBarData,
|
97
|
+
showTooltipFromInput,
|
98
|
+
handleInputBlurAll,
|
99
|
+
}
|
100
|
+
}
|