@eturnity/eturnity_reusable_components 8.7.5-EPIC-8593.2 → 8.7.5-EPIC-8593.4

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 (108) hide show
  1. package/package.json +1 -1
  2. package/src/assets/icons/collapse_arrow_icon_white.svg +1 -0
  3. package/src/assets/svgIcons/ac_power.svg +4 -0
  4. package/src/assets/svgIcons/arrow_long_left.svg +3 -0
  5. package/src/assets/svgIcons/arrow_long_right.svg +3 -0
  6. package/src/assets/svgIcons/chassis_ground_symbol.svg +27 -0
  7. package/src/assets/svgIcons/dc_power.svg +8 -0
  8. package/src/assets/svgIcons/double_arrow_long.svg +4 -0
  9. package/src/assets/svgIcons/ed_ac.svg +3 -0
  10. package/src/assets/svgIcons/ed_acgrid.svg +4 -0
  11. package/src/assets/svgIcons/ed_arrow_both.svg +7 -0
  12. package/src/assets/svgIcons/ed_arrow_left.svg +7 -0
  13. package/src/assets/svgIcons/ed_arrow_right.svg +7 -0
  14. package/src/assets/svgIcons/ed_battery.svg +10 -0
  15. package/src/assets/svgIcons/ed_batteryacinverter.svg +16 -0
  16. package/src/assets/svgIcons/ed_batteryintegratedinverter.svg +19 -0
  17. package/src/assets/svgIcons/ed_cirquitbreaker.svg +4 -0
  18. package/src/assets/svgIcons/ed_cirquitbreaker_magnetic.svg +6 -0
  19. package/src/assets/svgIcons/ed_cirquitbreaker_thermal.svg +4 -0
  20. package/src/assets/svgIcons/ed_cirquitbreaker_thermal_magnetic.svg +5 -0
  21. package/src/assets/svgIcons/ed_consumption.svg +3 -0
  22. package/src/assets/svgIcons/ed_dc.svg +6 -0
  23. package/src/assets/svgIcons/ed_disconnector.svg +4 -0
  24. package/src/assets/svgIcons/ed_disconnector_fuse.svg +4 -0
  25. package/src/assets/svgIcons/ed_disconnector_fuse_switch.svg +4 -0
  26. package/src/assets/svgIcons/ed_disconnector_loadbreak switch.svg +4 -0
  27. package/src/assets/svgIcons/ed_disconnector_switch.svg +4 -0
  28. package/src/assets/svgIcons/ed_disconnector_switch_auto_release.svg +5 -0
  29. package/src/assets/svgIcons/ed_energymanagement_rectangle.svg +3 -0
  30. package/src/assets/svgIcons/ed_evcharger.svg +19 -0
  31. package/src/assets/svgIcons/ed_flexiblecomponent_circle.svg +3 -0
  32. package/src/assets/svgIcons/ed_flexiblecomponent_square.svg +3 -0
  33. package/src/assets/svgIcons/ed_fuse.svg +3 -0
  34. package/src/assets/svgIcons/ed_ground.svg +5 -0
  35. package/src/assets/svgIcons/ed_heatpump.svg +4 -0
  36. package/src/assets/svgIcons/ed_icon_battery.svg +9 -0
  37. package/src/assets/svgIcons/ed_icon_circle.svg +3 -0
  38. package/src/assets/svgIcons/ed_icon_heatpump.svg +3 -0
  39. package/src/assets/svgIcons/ed_icon_inverter.svg +8 -0
  40. package/src/assets/svgIcons/ed_icon_optimizer.svg +11 -0
  41. package/src/assets/svgIcons/ed_integratedbatteryinverter.svg +10 -0
  42. package/src/assets/svgIcons/ed_inverter-blank.svg +3 -0
  43. package/src/assets/svgIcons/ed_mainsconnection.svg +3 -0
  44. package/src/assets/svgIcons/ed_meter_arrowleft.svg +4 -0
  45. package/src/assets/svgIcons/ed_meter_arrowright.svg +4 -0
  46. package/src/assets/svgIcons/ed_meter_bidirectional.svg +5 -0
  47. package/src/assets/svgIcons/ed_networkandsystemprotection_double.svg +14 -0
  48. package/src/assets/svgIcons/ed_networkandsystemprotection_single.svg +7 -0
  49. package/src/assets/svgIcons/ed_pvpanel.svg +7 -0
  50. package/src/assets/svgIcons/ed_rcd.svg +5 -0
  51. package/src/assets/svgIcons/ed_rcd_simple.svg +3 -0
  52. package/src/assets/svgIcons/ed_spd.svg +6 -0
  53. package/src/assets/svgIcons/ed_stringwithoptimizer.svg +33 -0
  54. package/src/assets/svgIcons/ed_stringwithoutoptimizer.svg +17 -0
  55. package/src/assets/svgIcons/ed_transformer.svg +3 -0
  56. package/src/assets/svgIcons/ground_symbol.svg +28 -0
  57. package/src/assets/svgIcons/house_sun.svg +3 -0
  58. package/src/assets/svgIcons/move_left.svg +3 -0
  59. package/src/assets/svgIcons/move_right.svg +3 -0
  60. package/src/assets/svgIcons/rectangle.svg +3 -0
  61. package/src/assets/svgIcons/refresh.svg +3 -0
  62. package/src/assets/svgIcons/text_icon.svg +3 -0
  63. package/src/assets/theme.js +18 -1
  64. package/src/components/banner/infoBanner/InfoBanner.spec.js +29 -42
  65. package/src/components/barchart/BottomFields.vue +253 -0
  66. package/src/components/barchart/ChartControls.vue +113 -0
  67. package/src/components/barchart/SelectionBox.vue +150 -0
  68. package/src/components/barchart/composables/index.js +5 -0
  69. package/src/components/barchart/composables/useAxisCalculations.js +104 -0
  70. package/src/components/barchart/composables/useChartData.js +114 -0
  71. package/src/components/barchart/composables/useChartScroll.js +61 -0
  72. package/src/components/barchart/composables/useSelection.js +75 -0
  73. package/src/components/barchart/composables/useTooltip.js +100 -0
  74. package/src/components/barchart/index.vue +385 -0
  75. package/src/components/barchart/styles/bottomFields.js +66 -0
  76. package/src/components/barchart/styles/chart.js +272 -0
  77. package/src/components/barchart/styles/chartControls.js +59 -0
  78. package/src/components/buttons/buttonIcon/index.vue +5 -0
  79. package/src/components/buttons/splitButtons/index.vue +86 -0
  80. package/src/components/collapsableInfoText/index.vue +2 -2
  81. package/src/components/draggableCard/defaultProps.js +16 -0
  82. package/src/components/draggableCard/draggableCard.spec.js +99 -0
  83. package/src/components/draggableCard/draggableCard.stories.js +79 -0
  84. package/src/components/draggableCard/index.vue +363 -0
  85. package/src/components/errorMessage/errorMessage.spec.js +34 -0
  86. package/src/components/errorMessage/errorMessage.stories.js +35 -0
  87. package/src/components/filter/filterSettings.vue +2 -0
  88. package/src/components/icon/index.vue +32 -9
  89. package/src/components/infoText/index.vue +2 -2
  90. package/src/components/infoText/infoText.spec.js +6 -1
  91. package/src/components/inputs/checkbox/index.vue +2 -2
  92. package/src/components/inputs/inputNumber/index.vue +14 -2
  93. package/src/components/inputs/searchInput/index.vue +18 -2
  94. package/src/components/inputs/select/index.vue +104 -13
  95. package/src/components/modals/actionModal/actionModal.spec.js +52 -0
  96. package/src/components/modals/actionModal/actionModal.stories.js +53 -0
  97. package/src/components/modals/actionModal/index.vue +6 -6
  98. package/src/components/modals/infoModal/index.vue +49 -19
  99. package/src/components/modals/infoModal/infoModal.spec.js +55 -0
  100. package/src/components/modals/infoModal/infoModal.stories.js +47 -0
  101. package/src/components/modals/modal/index.vue +16 -5
  102. package/src/components/pageSubtitle/PageSubtitle.stories.js +0 -1
  103. package/src/components/spinnerGif/index.vue +3 -3
  104. package/src/components/tabsHeader/index.vue +29 -1
  105. package/src/helpers/dateTimeFormatting.js +51 -0
  106. package/src/helpers/isObjectEqual.js +22 -0
  107. package/src/helpers/toLocaleNumber.js +11 -0
  108. package/src/main.js +1 -0
@@ -0,0 +1,104 @@
1
+ import { computed } from 'vue'
2
+
3
+ export function useAxisCalculations(props, maxValue) {
4
+ const findNiceNumber = (value) => {
5
+ // Handle 0 or negative values
6
+ if (value <= 0) return 0
7
+
8
+ if (value < 1) {
9
+ return Math.ceil(value * 10) / 10
10
+ }
11
+
12
+ const exponent = Math.floor(Math.log10(value))
13
+ const factor = Math.pow(10, exponent)
14
+ const normalized = value / factor
15
+
16
+ const niceNumbers = [
17
+ 1.0, 1.2, 1.5, 1.6, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0,
18
+ ]
19
+ const niceNormalized = niceNumbers.find((n) => n >= normalized) || 10.0
20
+
21
+ return niceNormalized * factor
22
+ }
23
+
24
+ // Calculate max value with padding, rounded to nice numbers
25
+ const paddedMaxValue = computed(() => {
26
+ const rawMax = maxValue.value
27
+ const withPadding = rawMax * 1.1
28
+ const niceNumber = findNiceNumber(withPadding)
29
+
30
+ return niceNumber
31
+ })
32
+
33
+ const calculateStepSize = (max) => {
34
+ const preferredDivisions = [5, 6, 7, 8, 9, 10]
35
+
36
+ for (const divisions of preferredDivisions) {
37
+ const roughStep = max / divisions
38
+ const niceStep = findNiceNumber(roughStep)
39
+
40
+ const numCompleteSteps = Math.floor(max / niceStep)
41
+ const actualMax = niceStep * numCompleteSteps
42
+
43
+ // Only use this step size if it gets us close to our target max
44
+ // and gives us the right number of ticks
45
+ if (
46
+ actualMax >= max * 0.95 && // Within 5% of target max
47
+ numCompleteSteps + 1 >= 5 &&
48
+ numCompleteSteps + 1 <= 10
49
+ ) {
50
+ return niceStep
51
+ }
52
+ }
53
+
54
+ // If no perfect match found, use max/6
55
+ return findNiceNumber(max / 6)
56
+ }
57
+
58
+ const yAxisLabels = computed(() => {
59
+ const max = paddedMaxValue.value
60
+
61
+ if (max === 0) {
62
+ return [0]
63
+ }
64
+
65
+ const stepSize = calculateStepSize(max)
66
+ const labels = []
67
+
68
+ // Generate labels including 0 up to numSteps
69
+ const numSteps = Math.floor(max / stepSize)
70
+ for (let i = 0; i <= numSteps; i++) {
71
+ labels.push(i * stepSize)
72
+ }
73
+
74
+ // Ensure we always have at least 2 labels (0 and max)
75
+ if (labels.length < 2) {
76
+ labels.push(max)
77
+ }
78
+
79
+ return labels
80
+ })
81
+
82
+ const yAxisHeight = computed(() => {
83
+ return '100%'
84
+ })
85
+
86
+ const yAxisWidth = computed(() => {
87
+ return !!props.yAxisTitle || props.isBottomFieldsShown ? '70px' : '60px'
88
+ })
89
+
90
+ const isChartControlsShown = (position) => {
91
+ return (
92
+ props.chartControlsPosition === position &&
93
+ (props.isLegendShown || props.splitButtonOptions.length)
94
+ )
95
+ }
96
+
97
+ return {
98
+ yAxisLabels,
99
+ yAxisHeight,
100
+ yAxisWidth,
101
+ isChartControlsShown,
102
+ paddedMaxValue,
103
+ }
104
+ }
@@ -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
+ }