@eturnity/eturnity_reusable_components 7.51.3-EPDM-12810.1 → 7.51.3-EPDM-12810.2

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 (34) hide show
  1. package/package.json +2 -3
  2. package/src/assets/gifs/spinner.gif +0 -0
  3. package/src/assets/svgIcons/checkmark.svg +3 -0
  4. package/src/assets/svgIcons/collapse_all.svg +4 -0
  5. package/src/assets/svgIcons/expand_all.svg +4 -0
  6. package/src/assets/svgIcons/export_document.svg +3 -0
  7. package/src/assets/svgIcons/hybrid.svg +4 -0
  8. package/src/assets/svgIcons/module.svg +3 -0
  9. package/src/assets/svgIcons/move_down.svg +3 -0
  10. package/src/assets/svgIcons/move_up.svg +3 -0
  11. package/src/assets/svgIcons/optimizer.svg +6 -0
  12. package/src/assets/svgIcons/string_design.svg +5 -0
  13. package/src/assets/svgIcons/string_directions.svg +10 -0
  14. package/src/components/banner/notificationBanner/index.vue +131 -0
  15. package/src/components/buttons/buttonIcon/index.vue +4 -2
  16. package/src/components/buttons/mainButton/index.vue +16 -2
  17. package/src/components/card/index.vue +1 -1
  18. package/src/components/icon/Icons.stories.js +9 -19
  19. package/src/components/icon/icon.spec.js +66 -0
  20. package/src/components/icon/iconCollection.vue +4 -3
  21. package/src/components/icon/index.vue +9 -2
  22. package/src/components/infoText/index.vue +210 -74
  23. package/src/components/inputs/checkbox/index.vue +19 -8
  24. package/src/components/inputs/inputNumber/index.vue +202 -12
  25. package/src/components/inputs/inputText/index.vue +36 -2
  26. package/src/components/inputs/radioButton/index.vue +27 -6
  27. package/src/components/inputs/select/index.vue +68 -24
  28. package/src/components/inputs/select/option/index.vue +11 -2
  29. package/src/components/label/index.vue +1 -2
  30. package/src/components/markerItem/index.vue +8 -1
  31. package/src/components/spinner/index.vue +11 -0
  32. package/src/components/spinnerGif/index.vue +98 -0
  33. package/src/components/stringDesign/DropdownMenu/index.vue +983 -0
  34. package/src/components/tableDropdown/index.vue +51 -20
@@ -2,20 +2,27 @@
2
2
  <PageContainer ref="container">
3
3
  <div
4
4
  ref="icon"
5
- @click="openTrigger === 'onClick' && toggleInfo()"
6
- @mouseenter="openTrigger === 'onHover' && showInfo()"
7
- @mouseleave="openTrigger === 'onHover' && hideInfo()"
5
+ data-test-id="infoText_trigger"
6
+ @click=";(isMobile || openTrigger === 'onClick') && toggleInfo()"
7
+ @mouseenter="!isMobile && openTrigger === 'onHover' && showInfo()"
8
+ @mouseleave="!isMobile && openTrigger === 'onHover' && hideInfo()"
8
9
  >
9
10
  <IconComponent
10
- :color="iconColor"
11
- cursor="pointer"
12
- name="info"
11
+ :color="iconColor || computedIconColor"
12
+ :cursor="isDisabled ? 'not-allowed' : 'pointer'"
13
+ :disabled="isDisabled"
14
+ :name="iconName"
13
15
  :size="size"
14
16
  />
15
17
  </div>
16
18
  <Teleport v-if="isVisible" to="body">
17
19
  <TextWrapper :style="wrapperStyle">
18
- <TextOverlay ref="infoBox" :image="image" :style="boxStyle">
20
+ <TextOverlay
21
+ ref="infoBox"
22
+ :image="image"
23
+ :style="boxStyle"
24
+ :width="infoBoxWidth"
25
+ >
19
26
  <OverlayImage
20
27
  v-if="image"
21
28
  ref="infoImage"
@@ -23,7 +30,11 @@
23
30
  :src="image"
24
31
  @load="onImageLoad"
25
32
  />
26
- <span ref="textContent" :style="textStyle" v-html="text"></span>
33
+ <span ref="textContent" :style="textStyle">
34
+ <slot>
35
+ <span v-html="text"></span>
36
+ </slot>
37
+ </span>
27
38
  </TextOverlay>
28
39
  <Arrow :image="image" :style="arrowStyle" />
29
40
  </TextWrapper>
@@ -57,7 +68,7 @@
57
68
  word-wrap: break-word;
58
69
  overflow-wrap: break-word;
59
70
  white-space: normal;
60
- width: 100%; // Ensure the TextOverlay takes full width of its parent
71
+ width: ${(props) => (props.width ? `${props.width}px` : '100%')};
61
72
  box-shadow: ${(props) =>
62
73
  props.image ? '0 2px 10px rgba(0, 0, 0, 0.1)' : 'none'};
63
74
 
@@ -75,11 +86,9 @@
75
86
  position: absolute;
76
87
  width: 0;
77
88
  height: 0;
78
- border-left: 8px solid transparent;
79
- border-right: 8px solid transparent;
80
- border-top: 8px solid
81
- ${(props) =>
82
- props.image ? props.theme.colors.white : props.theme.colors.black};
89
+ border: 8px solid transparent;
90
+ border-top-color: ${(props) =>
91
+ props.image ? props.theme.colors.white : props.theme.colors.black};
83
92
  filter: ${(props) =>
84
93
  props.image ? 'drop-shadow(0 2px 2px rgba(0, 0, 0, 0.1))' : 'none'};
85
94
  `
@@ -111,13 +120,13 @@
111
120
  },
112
121
  props: {
113
122
  text: {
114
- type: String,
123
+ required: false,
115
124
  default: '',
125
+ type: String,
116
126
  },
117
127
  size: {
118
128
  type: String,
119
129
  default: '14px',
120
- type: String,
121
130
  },
122
131
  infoPosition: {
123
132
  required: false,
@@ -142,6 +151,20 @@
142
151
  type: String,
143
152
  default: '',
144
153
  },
154
+ iconName: {
155
+ type: String,
156
+ default: 'info',
157
+ },
158
+ iconColor: {
159
+ type: String,
160
+ default: null,
161
+ required: false,
162
+ },
163
+ isDisabled: {
164
+ type: Boolean,
165
+ default: false,
166
+ required: false,
167
+ },
145
168
  },
146
169
  setup(props) {
147
170
  const isVisible = ref(false)
@@ -155,6 +178,7 @@
155
178
  const boxStyle = ref({})
156
179
  const arrowStyle = ref({})
157
180
  const wrapperStyle = ref({})
181
+ const isMobile = ref(window.innerWidth <= 768)
158
182
 
159
183
  const textStyle = computed(() => ({
160
184
  fontSize: props.image ? '12px' : '13px',
@@ -163,57 +187,127 @@
163
187
  }))
164
188
 
165
189
  const calculatePosition = (width) => {
166
- if (!icon.value) return {}
190
+ if (!icon.value || !width) return {}
167
191
 
168
192
  const iconRect = icon.value.getBoundingClientRect()
169
193
  const windowHeight = window.innerHeight
170
194
  const windowWidth = window.innerWidth
171
195
 
172
- let top = iconRect.bottom + 10
173
- let left = iconRect.left
174
-
175
- // Adjust left based on the infoBoxWidth
176
- if (left + width > windowWidth) {
177
- left = windowWidth - width - 10
178
- }
179
-
180
- // Ensure there's enough space below or place above if not
181
- const isAbove = top + infoBoxHeight.value > windowHeight
182
- if (isAbove) {
183
- top = iconRect.top - infoBoxHeight.value - 10
184
- }
185
-
186
- // Calculate arrow position
187
- let arrowLeft = iconRect.left - left + iconRect.width / 2 - 8
188
-
189
- // Adjust arrow position if it's too close to the edges
190
- const edgeThreshold = 2 // pixels from the edge to start adjusting
191
- if (arrowLeft < edgeThreshold) {
192
- arrowLeft = edgeThreshold
193
- } else if (arrowLeft > width - 16 - edgeThreshold) {
194
- arrowLeft = width - 16 - edgeThreshold
196
+ // Calculate available space in all directions
197
+ const spaceBelow = windowHeight - iconRect.bottom - 10
198
+ const spaceAbove = iconRect.top - 10
199
+ const spaceRight = windowWidth - iconRect.right - 10
200
+ const spaceLeft = iconRect.left - 10
201
+
202
+ // Determine the best position based on available space
203
+ const positions = [
204
+ { position: 'bottom', space: spaceBelow },
205
+ { position: 'top', space: spaceAbove },
206
+ { position: 'right', space: spaceRight },
207
+ { position: 'left', space: spaceLeft },
208
+ ].sort((a, b) => b.space - a.space)
209
+
210
+ const bestPosition = positions[0].position
211
+
212
+ let top, left, arrowPosition
213
+
214
+ switch (bestPosition) {
215
+ case 'bottom':
216
+ top = Math.round(iconRect.bottom + 10)
217
+ left = Math.round(Math.min(iconRect.left, windowWidth - width - 10))
218
+ arrowPosition = {
219
+ left: `${Math.round(
220
+ Math.min(
221
+ Math.max(iconRect.left - left + iconRect.width / 2 - 8, 2),
222
+ width - 18
223
+ )
224
+ )}px`,
225
+ top: '-7px',
226
+ bottom: 'auto',
227
+ transform: 'rotate(180deg)',
228
+ }
229
+ break
230
+
231
+ case 'top':
232
+ top = Math.round(iconRect.top - infoBoxHeight.value - 10)
233
+ left = Math.round(Math.min(iconRect.left, windowWidth - width - 10))
234
+ arrowPosition = {
235
+ left: `${Math.round(
236
+ Math.min(
237
+ Math.max(iconRect.left - left + iconRect.width / 2 - 8, 2),
238
+ width - 18
239
+ )
240
+ )}px`,
241
+ top: 'auto',
242
+ bottom: '-7px',
243
+ transform: 'none',
244
+ }
245
+ break
246
+
247
+ case 'right':
248
+ top = Math.round(
249
+ Math.max(
250
+ Math.min(
251
+ iconRect.top - (infoBoxHeight.value - iconRect.height) / 2,
252
+ windowHeight - infoBoxHeight.value - 10
253
+ ),
254
+ 10
255
+ )
256
+ )
257
+ left = Math.round(iconRect.right + 10)
258
+ arrowPosition = {
259
+ left: '-7px',
260
+ top: `${Math.round(
261
+ Math.min(
262
+ Math.max(iconRect.top - top + iconRect.height / 2 - 8, 2),
263
+ infoBoxHeight.value - 18
264
+ )
265
+ )}px`,
266
+ bottom: 'auto',
267
+ transform: 'rotate(90deg)',
268
+ }
269
+ break
270
+
271
+ case 'left':
272
+ top = Math.round(
273
+ Math.max(
274
+ Math.min(
275
+ iconRect.top - (infoBoxHeight.value - iconRect.height) / 2,
276
+ windowHeight - infoBoxHeight.value - 10
277
+ ),
278
+ 10
279
+ )
280
+ )
281
+ left = Math.round(iconRect.left - width - 10)
282
+ arrowPosition = {
283
+ left: 'auto',
284
+ right: '-7px',
285
+ top: `${Math.round(
286
+ Math.min(
287
+ Math.max(iconRect.top - top + iconRect.height / 2 - 8, 2),
288
+ infoBoxHeight.value - 18
289
+ )
290
+ )}px`,
291
+ bottom: 'auto',
292
+ transform: 'rotate(-90deg)',
293
+ }
294
+ break
195
295
  }
196
296
 
197
- const arrowTop = isAbove ? 'auto' : '-7px'
198
- const arrowBottom = isAbove ? '-7px' : 'auto'
199
-
200
- arrowStyle.value = {
201
- left: `${arrowLeft}px`,
202
- top: arrowTop,
203
- bottom: arrowBottom,
204
- transform: isAbove ? 'none' : 'rotate(180deg)',
205
- }
297
+ // Set arrow styles
298
+ arrowStyle.value = arrowPosition
206
299
 
300
+ // Set wrapper styles
207
301
  wrapperStyle.value = {
208
302
  position: 'fixed',
209
303
  top: `${top}px`,
210
304
  left: `${left}px`,
211
305
  transform: 'none',
212
- width: `${width}px`, // Set a fixed width for the wrapper
306
+ width: `${width}px`,
213
307
  }
214
308
 
215
309
  return {
216
- width: '100%', // Always use 100% width for the TextOverlay
310
+ width: '100%',
217
311
  maxWidth: props.maxWidth,
218
312
  overflowY: 'auto',
219
313
  backgroundColor: props.image
@@ -224,12 +318,15 @@
224
318
 
225
319
  const showInfo = async () => {
226
320
  isVisible.value = true
321
+ // Wait for two render cycles to ensure content is stable
227
322
  await nextTick()
228
- updatePosition()
323
+ await nextTick()
324
+ await updatePosition()
229
325
  }
230
326
 
231
327
  const hideInfo = () => {
232
328
  isVisible.value = false
329
+ infoBoxWidth.value = 0 // Reset width when hiding
233
330
  }
234
331
 
235
332
  const toggleInfo = () => {
@@ -257,25 +354,55 @@
257
354
  }
258
355
 
259
356
  const updatePosition = async () => {
260
- if (infoBox.value && textContent.value) {
261
- await nextTick()
262
-
263
- if (isIconInView()) {
264
- const contentWidth = textContent.value.offsetWidth
265
- infoBoxWidth.value = Math.min(
266
- Math.max(contentWidth, 200), // Set a minimum width of 200px
267
- parseInt(props.maxWidth, 10)
268
- )
269
- infoBoxHeight.value = infoBox.value.$el.offsetHeight
270
- boxStyle.value = calculatePosition(infoBoxWidth.value)
357
+ console.log('updatePosition')
358
+ if (!infoBox.value || !textContent.value) return
271
359
 
272
- // Now make it visible if it should be
273
- if (isVisible.value) {
274
- infoBox.value.$el.style.visibility = 'visible'
275
- }
276
- } else if (isVisible.value) {
277
- hideInfo()
278
- }
360
+ // First make the content visible but hidden to get accurate measurements
361
+ if (infoBox.value.$el) {
362
+ infoBox.value.$el.style.visibility = 'hidden'
363
+ infoBox.value.$el.style.display = 'block'
364
+ }
365
+
366
+ await nextTick()
367
+
368
+ if (!isIconInView()) {
369
+ if (isVisible.value) hideInfo()
370
+ return
371
+ }
372
+
373
+ // Create a temporary clone to measure true width
374
+ const clone = textContent.value.cloneNode(true)
375
+ clone.style.position = 'absolute'
376
+ clone.style.visibility = 'hidden'
377
+ clone.style.width = 'auto'
378
+ clone.style.maxWidth = 'none'
379
+ clone.style.whiteSpace = 'nowrap' // Prevent text wrapping during measurement
380
+ document.body.appendChild(clone)
381
+
382
+ // Get fresh content width from clone
383
+ const contentWidth = clone.offsetWidth
384
+ document.body.removeChild(clone)
385
+
386
+ // Calculate new width
387
+ const calculatedWidth = Math.min(
388
+ Math.max(contentWidth, 200),
389
+ parseInt(props.maxWidth, 10)
390
+ )
391
+
392
+ // Set new dimensions
393
+ infoBoxWidth.value = calculatedWidth
394
+
395
+ // Wait for width to be applied
396
+ await nextTick()
397
+
398
+ infoBoxHeight.value = infoBox.value.$el.offsetHeight
399
+
400
+ // Calculate and apply position
401
+ boxStyle.value = calculatePosition(calculatedWidth)
402
+
403
+ // Make visible after position is set
404
+ if (isVisible.value && infoBox.value.$el) {
405
+ infoBox.value.$el.style.visibility = 'visible'
279
406
  }
280
407
  }
281
408
 
@@ -287,7 +414,10 @@
287
414
  }
288
415
 
289
416
  const handleClickOutside = (event) => {
290
- if (props.openTrigger === 'onClick' && isVisible.value) {
417
+ if (
418
+ (props.openTrigger === 'onClick' || isMobile.value) &&
419
+ isVisible.value
420
+ ) {
291
421
  const clickedElement = event.target
292
422
  if (
293
423
  infoBox.value &&
@@ -299,9 +429,14 @@
299
429
  }
300
430
  }
301
431
 
432
+ const handleResize = () => {
433
+ isMobile.value = window.innerWidth <= 768
434
+ updatePosition()
435
+ }
436
+
302
437
  onMounted(() => {
303
438
  window.addEventListener('scroll', handleScroll, { passive: true })
304
- window.addEventListener('resize', updatePosition)
439
+ window.addEventListener('resize', handleResize)
305
440
  document.addEventListener('scroll', handleScroll, {
306
441
  passive: true,
307
442
  capture: true,
@@ -311,7 +446,7 @@
311
446
 
312
447
  onUnmounted(() => {
313
448
  window.removeEventListener('scroll', handleScroll)
314
- window.removeEventListener('resize', updatePosition)
449
+ window.removeEventListener('resize', handleResize)
315
450
  document.removeEventListener('scroll', handleScroll, { capture: true })
316
451
  document.removeEventListener('click', handleClickOutside)
317
452
  })
@@ -339,10 +474,11 @@
339
474
  wrapperStyle,
340
475
  textStyle,
341
476
  onImageLoad,
477
+ isMobile,
342
478
  }
343
479
  },
344
480
  computed: {
345
- iconColor() {
481
+ computedIconColor() {
346
482
  return this.buttonType === 'error'
347
483
  ? theme.colors.red
348
484
  : theme.colors.mediumGray
@@ -19,7 +19,7 @@
19
19
  <CheckWrapper :has-label="hasLabel">
20
20
  <span class="checkmark"></span>
21
21
  </CheckWrapper>
22
- <LabelText v-if="hasLabel">
22
+ <LabelText v-if="hasLabel" :is-disabled="isDisabled">
23
23
  {{ label }}
24
24
  </LabelText>
25
25
  </Container>
@@ -90,18 +90,20 @@
90
90
  ? '16px'
91
91
  : '25px'};
92
92
  background-color: ${(props) =>
93
- props.isChecked
93
+ props.isDisabled
94
+ ? props.theme.colors.lightGray
95
+ : props.isChecked
94
96
  ? props.backgroundColor
95
97
  ? props.backgroundColor
96
98
  : props.theme.colors.green
97
- : props.isDisabled
98
- ? props.theme.colors.lightGray
99
99
  : props.theme.colors.white};
100
100
  border-radius: 4px;
101
101
  border: 1px solid
102
102
  ${(props) =>
103
103
  props.isChecked
104
- ? props.backgroundColor
104
+ ? props.isDisabled
105
+ ? props.theme.colors.lightGray
106
+ : props.backgroundColor
105
107
  ? props.backgroundColor
106
108
  : props.theme.colors.green
107
109
  : props.theme.colors.mediumGray};
@@ -140,7 +142,11 @@
140
142
  : '10px'};
141
143
  border: solid
142
144
  ${(props) =>
143
- props.checkColor ? props.checkColor : props.theme.colors.white};
145
+ props.isDisabled
146
+ ? props.theme.colors.green
147
+ : props.checkColor
148
+ ? props.checkColor
149
+ : props.theme.colors.white};
144
150
  border-width: ${(props) =>
145
151
  props.size === 'medium'
146
152
  ? '0 3px 3px 0'
@@ -160,11 +166,16 @@
160
166
  width: 0;
161
167
  `
162
168
 
163
- const LabelText = styled.div`
169
+ const LabelTextAttrs = {
170
+ isDisabled: Boolean,
171
+ }
172
+ const LabelText = styled('div', LabelTextAttrs)`
164
173
  font-size: 13px;
165
174
  display: grid;
166
- align-items: flex-start;
175
+ align-items: center;
167
176
  min-height: 18px;
177
+ color: ${(props) =>
178
+ props.isDisabled ? props.theme.colors.grey2 : props.theme.colors.black};
168
179
  `
169
180
 
170
181
  export default {