@farm-investimentos/front-mfe-components 15.14.8 → 15.14.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm-investimentos/front-mfe-components",
3
- "version": "15.14.8",
3
+ "version": "15.14.10",
4
4
  "author": "farm investimentos",
5
5
  "private": false,
6
6
  "main": "./dist/front-mfe-components.common.js",
@@ -105,15 +105,13 @@
105
105
  padding: gutter('xs') 0;
106
106
  }
107
107
 
108
-
109
-
110
108
  &__bar {
111
109
  height: 30px;
112
110
  z-index: 2;
113
111
  border-radius: 4px;
114
112
  display: flex;
115
113
  align-items: center;
116
- justify-content: start;
114
+ justify-content: flex-start;
117
115
  color: white;
118
116
  font-size: fontSize('sm');
119
117
  padding: 0 gutter('sm');
@@ -86,7 +86,7 @@ export const OnRightSide = () => ({
86
86
  ],
87
87
  };
88
88
  },
89
- template: `<div style="padding-left: 80px; display: flex; justify-content: end;">
89
+ template: `<div style="padding-left: 80px; display: flex; justify-content: flex-end;">
90
90
  <farm-context-menu ref="multi" :items="items" />
91
91
  </div>`,
92
92
  });
@@ -3,14 +3,13 @@
3
3
  <div
4
4
  ref="activatorRef"
5
5
  class="tooltip-activator"
6
- @mouseover="show"
7
- @mouseout="hide"
8
- @mouseleave="hide"
6
+ @mouseenter="onActivatorEnter"
7
+ @mouseleave="onActivatorLeave"
9
8
  >
10
9
  <slot name="activator" />
11
10
  </div>
12
11
 
13
- <div v-if="isVisible" ref="tooltipRef" :class="tooltipClasses" :style="tooltipStyles">
12
+ <div v-if="isVisible" ref="tooltipRef" :class="tooltipClasses" :style="tooltipStyles" @mouseenter="onTooltipEnter" @mouseleave="onTooltipLeave">
14
13
  <div v-if="hasTitle || showCloseButton" class="tooltip-header">
15
14
  <div v-if="hasTitle" class="tooltip-title">
16
15
  <slot name="title" />
@@ -96,6 +95,10 @@ export default defineComponent({
96
95
  type: String,
97
96
  default: undefined,
98
97
  },
98
+ anchorTo: {
99
+ type: String as PropType<'auto' | 'viewport' | 'modal'>,
100
+ default: 'auto',
101
+ },
99
102
  hideOnScroll: {
100
103
  type: Boolean,
101
104
  default: true,
@@ -106,6 +109,8 @@ export default defineComponent({
106
109
  const containerRef = ref<HTMLElement | null>(null);
107
110
  const activatorRef = ref<HTMLElement | null>(null);
108
111
  const tooltipRef = ref<HTMLElement | null>(null);
112
+ const anchorElRef = ref<HTMLElement | null>(null);
113
+ const anchoredToModal = ref(false);
109
114
  const scrollableElementsRef = ref<Element[] | null>(null);
110
115
 
111
116
  const Z_INDEX_OFFSET = 1000;
@@ -161,6 +166,12 @@ export default defineComponent({
161
166
  return props.placement;
162
167
  });
163
168
 
169
+ // Placement realmente utilizado (pode sofrer flip em runtime)
170
+ const currentPlacement = ref(normalizedPlacement.value);
171
+ watch(normalizedPlacement, (val) => {
172
+ currentPlacement.value = val;
173
+ });
174
+
164
175
  const normalizedMaxWidth = computed(() => {
165
176
  if (props.fluid) {
166
177
  return '300px';
@@ -174,12 +185,12 @@ export default defineComponent({
174
185
  [`tooltip-popup--${props.variant}`]: true,
175
186
  [`tooltip-popup--${props.size}`]: true,
176
187
  'tooltip-popup--has-title': hasTitle.value,
177
- [`tooltip-popup--${normalizedPlacement.value}`]: true,
188
+ [`tooltip-popup--${currentPlacement.value}`]: true,
178
189
  }));
179
190
 
180
191
  const tooltipStyles = computed(() => {
181
192
  const styles: Record<string, string> = {
182
- position: 'fixed',
193
+ position: anchoredToModal.value ? 'absolute' : 'fixed',
183
194
  zIndex: String(getTooltipZIndex()),
184
195
  };
185
196
 
@@ -195,7 +206,7 @@ export default defineComponent({
195
206
  });
196
207
 
197
208
  const arrowStyles = computed(() => {
198
- const [verticalPos, horizontalAlign] = normalizedPlacement.value.split('-');
209
+ const [verticalPos, horizontalAlign] = currentPlacement.value.split('-');
199
210
 
200
211
  const styles: Record<string, string> = {
201
212
  position: 'absolute',
@@ -235,14 +246,29 @@ export default defineComponent({
235
246
 
236
247
  nextTick(() => {
237
248
  if (tooltipRef.value && activatorRef.value) {
238
- moveToBody(tooltipRef.value);
249
+ // Resolver onde ancorar o tooltip
250
+ let anchorEl: HTMLElement | null = null;
251
+ if (props.anchorTo === 'viewport') {
252
+ anchorEl = document.body;
253
+ } else if (props.anchorTo === 'modal' || props.anchorTo === 'auto') {
254
+ const modalEl = activatorRef.value.closest('.farm-modal') as HTMLElement | null;
255
+ anchorEl = modalEl || document.body;
256
+ }
257
+
258
+ anchorElRef.value = anchorEl;
259
+ anchoredToModal.value = !!anchorEl && anchorEl !== document.body;
260
+ if (anchoredToModal.value && anchorElRef.value && tooltipRef.value) {
261
+ anchorElRef.value.appendChild(tooltipRef.value);
262
+ } else {
263
+ moveToBody(tooltipRef.value);
264
+ }
239
265
  updatePosition();
240
266
  addScrollListener();
241
267
  }
242
268
  });
243
269
  };
244
270
 
245
- const hide = () => {
271
+ const hide = () => {
246
272
  if (props.disabled || isControlled.value) return;
247
273
 
248
274
  isVisible.value = false;
@@ -250,6 +276,38 @@ export default defineComponent({
250
276
  removeScrollListener();
251
277
  };
252
278
 
279
+ // Hover management to avoid flicker when moving from activator to tooltip
280
+ let hoverInside = false;
281
+ let hideTimeout: number | null = null;
282
+
283
+ const clearHideTimeout = () => {
284
+ if (hideTimeout) {
285
+ window.clearTimeout(hideTimeout);
286
+ hideTimeout = null;
287
+ }
288
+ };
289
+
290
+ const onActivatorEnter = () => {
291
+ clearHideTimeout();
292
+ show();
293
+ };
294
+
295
+ const onActivatorLeave = () => {
296
+ hideTimeout = window.setTimeout(() => {
297
+ if (!hoverInside) hide();
298
+ }, (Array.isArray(props.delay) ? (props.delay[1] || 50) : 50) as number);
299
+ };
300
+
301
+ const onTooltipEnter = () => {
302
+ hoverInside = true;
303
+ clearHideTimeout();
304
+ };
305
+
306
+ const onTooltipLeave = () => {
307
+ hoverInside = false;
308
+ hide();
309
+ };
310
+
253
311
  const close = () => {
254
312
  if (isControlled.value) {
255
313
  emit('input', false);
@@ -278,12 +336,25 @@ export default defineComponent({
278
336
  const position = calculateTooltipPosition(
279
337
  activatorRect,
280
338
  tooltipRect,
281
- normalizedPlacement.value,
339
+ currentPlacement.value,
282
340
  props.offset
283
341
  );
284
342
 
285
- tooltipRef.value.style.left = `${position.left}px`;
286
- tooltipRef.value.style.top = `${position.top}px`;
343
+ // Atualiza placement efetivo (com flip) para setinha/classes
344
+ currentPlacement.value = position.placementUsed as TooltipPlacement;
345
+
346
+ if (anchoredToModal.value && anchorElRef.value) {
347
+ const containerRect = anchorElRef.value.getBoundingClientRect();
348
+ const scrollLeft = anchorElRef.value.scrollLeft || 0;
349
+ const scrollTop = anchorElRef.value.scrollTop || 0;
350
+ const left = position.left - containerRect.left + scrollLeft;
351
+ const top = position.top - containerRect.top + scrollTop;
352
+ tooltipRef.value.style.left = `${left}px`;
353
+ tooltipRef.value.style.top = `${top}px`;
354
+ } else {
355
+ tooltipRef.value.style.left = `${position.left}px`;
356
+ tooltipRef.value.style.top = `${position.top}px`;
357
+ }
287
358
  };
288
359
 
289
360
  const getScrollableElements = () => {
@@ -372,8 +443,12 @@ export default defineComponent({
372
443
  tooltipClasses,
373
444
  tooltipStyles,
374
445
  arrowStyles,
375
- show,
376
- hide,
446
+ show,
447
+ hide,
448
+ onActivatorEnter,
449
+ onActivatorLeave,
450
+ onTooltipEnter,
451
+ onTooltipLeave,
377
452
  close,
378
453
  };
379
454
  },
@@ -1,6 +1,7 @@
1
1
  export interface TooltipPosition {
2
- left: number;
3
- top: number;
2
+ left: number;
3
+ top: number;
4
+ placementUsed: string; // ex.: 'top-left', 'bottom-center'
4
5
  }
5
6
 
6
7
  export interface TooltipRect {
@@ -20,16 +21,19 @@ export function calculateTooltipPosition(
20
21
  placement: string,
21
22
  offset: number = 8
22
23
  ): TooltipPosition {
23
- const [verticalPos, horizontalAlign] = placement.split('-');
24
+ const parts = placement.split('-');
25
+ let verticalPos = parts[0];
26
+ const horizontalAlign = parts[1];
24
27
 
25
28
  let left = 0;
26
29
  let top = 0;
27
30
 
28
- if (verticalPos === 'top') {
29
- top = activatorRect.top - tooltipRect.height - offset;
30
- } else {
31
- top = activatorRect.bottom + offset;
32
- }
31
+ const computeTop = (vert: string) =>
32
+ vert === 'top'
33
+ ? activatorRect.top - tooltipRect.height - offset
34
+ : activatorRect.bottom + offset;
35
+
36
+ top = computeTop(verticalPos);
33
37
 
34
38
  switch (horizontalAlign) {
35
39
  case 'left':
@@ -45,16 +49,26 @@ export function calculateTooltipPosition(
45
49
  break;
46
50
  }
47
51
 
48
- if (left < offset) left = offset;
49
- if (left + tooltipRect.width > window.innerWidth - offset) {
50
- left = window.innerWidth - tooltipRect.width - offset;
51
- }
52
- if (top < offset) top = offset;
53
- if (top + tooltipRect.height > window.innerHeight - offset) {
54
- top = window.innerHeight - tooltipRect.height - offset;
55
- }
52
+ // Flip vertical if not enough space
53
+ const tooHigh = top < offset;
54
+ const tooLow = top + tooltipRect.height > window.innerHeight - offset;
55
+ if ((verticalPos === 'top' && tooHigh) || (verticalPos === 'bottom' && tooLow)) {
56
+ verticalPos = verticalPos === 'top' ? 'bottom' : 'top';
57
+ top = computeTop(verticalPos);
58
+ }
59
+
60
+ // Clamp within viewport horizontally and vertically
61
+ if (left < offset) left = offset;
62
+ if (left + tooltipRect.width > window.innerWidth - offset) {
63
+ left = window.innerWidth - tooltipRect.width - offset;
64
+ }
65
+ if (top < offset) top = offset;
66
+ if (top + tooltipRect.height > window.innerHeight - offset) {
67
+ top = window.innerHeight - tooltipRect.height - offset;
68
+ }
56
69
 
57
- return { left, top };
70
+ const placementUsed = `${verticalPos}-${horizontalAlign || 'center'}`;
71
+ return { left, top, placementUsed };
58
72
  }
59
73
 
60
74
  export function moveToBody(element: HTMLElement): void {