@farm-investimentos/front-mfe-components-vue3 1.3.0 → 1.4.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/dist/front-mfe-components.common.js +388 -1109
- package/dist/front-mfe-components.common.js.map +1 -1
- package/dist/front-mfe-components.css +1 -1
- package/dist/front-mfe-components.umd.js +388 -1109
- package/dist/front-mfe-components.umd.js.map +1 -1
- package/dist/front-mfe-components.umd.min.js +1 -1
- package/dist/front-mfe-components.umd.min.js.map +1 -1
- package/package.json +3 -3
- package/src/components/Tooltip/Tooltip.scss +195 -22
- package/src/components/Tooltip/Tooltip.stories.js +264 -80
- package/src/components/Tooltip/Tooltip.vue +407 -122
- package/src/components/Tooltip/__tests__/Tooltip.spec.js +192 -9
- package/src/components/Tooltip/docs/Tooltip.md +287 -0
- package/src/components/Tooltip/index.ts +17 -2
- package/src/components/Tooltip/types.ts +51 -0
|
@@ -1,21 +1,204 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { nextTick } from 'vue';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { mount } from '@vue/test-utils';
|
|
4
|
+
|
|
5
|
+
import Tooltip from '../Tooltip.vue';
|
|
4
6
|
|
|
5
7
|
describe('Tooltip component', () => {
|
|
6
8
|
let wrapper;
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (wrapper) {
|
|
12
|
+
wrapper.unmount();
|
|
13
|
+
}
|
|
10
14
|
});
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const createWrapper = (props = {}, slots = {}) => {
|
|
17
|
+
const finalProps = {
|
|
18
|
+
delay: 0,
|
|
19
|
+
...props,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (!Object.prototype.hasOwnProperty.call(props, 'modelValue')) {
|
|
23
|
+
delete finalProps.modelValue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return mount(Tooltip, {
|
|
27
|
+
props: finalProps,
|
|
28
|
+
slots: {
|
|
29
|
+
activator: '<button>Hover me</button>',
|
|
30
|
+
default: 'Tooltip content',
|
|
31
|
+
...slots,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
};
|
|
15
35
|
|
|
16
|
-
describe('
|
|
36
|
+
describe('basic functionality', () => {
|
|
17
37
|
it('renders correctly', () => {
|
|
18
|
-
|
|
38
|
+
wrapper = createWrapper();
|
|
39
|
+
expect(wrapper.find('.tooltip-container').exists()).toBe(true);
|
|
40
|
+
expect(wrapper.find('.tooltip-activator').exists()).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('props', () => {
|
|
45
|
+
it('applies correct placement class', async () => {
|
|
46
|
+
wrapper = createWrapper({ placement: 'bottom-center', modelValue: true });
|
|
47
|
+
await nextTick();
|
|
48
|
+
|
|
49
|
+
expect(wrapper.find('.tooltip-popup--bottom-center').exists()).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('applies correct variant class', async () => {
|
|
53
|
+
wrapper = createWrapper({ variant: 'dark', modelValue: true });
|
|
54
|
+
await nextTick();
|
|
55
|
+
|
|
56
|
+
expect(wrapper.find('.tooltip-popup--dark').exists()).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('applies correct size class', async () => {
|
|
60
|
+
wrapper = createWrapper({ size: 'lg', modelValue: true });
|
|
61
|
+
await nextTick();
|
|
62
|
+
|
|
63
|
+
expect(wrapper.find('.tooltip-popup--lg').exists()).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('respects disabled prop', () => {
|
|
67
|
+
wrapper = createWrapper({ disabled: true });
|
|
68
|
+
expect(wrapper.props('disabled')).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('controlled mode', () => {
|
|
73
|
+
it('shows tooltip when modelValue is true', async () => {
|
|
74
|
+
wrapper = createWrapper({ modelValue: true });
|
|
75
|
+
await nextTick();
|
|
76
|
+
|
|
77
|
+
expect(wrapper.vm.isVisible).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('does not show tooltip when modelValue is false', async () => {
|
|
81
|
+
wrapper = createWrapper({ modelValue: false });
|
|
82
|
+
await nextTick();
|
|
83
|
+
|
|
84
|
+
expect(wrapper.vm.isVisible).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('shows close button when controlled and has title', async () => {
|
|
88
|
+
wrapper = createWrapper({ modelValue: true }, { title: '<span>Title</span>' });
|
|
89
|
+
await nextTick();
|
|
90
|
+
|
|
91
|
+
expect(wrapper.find('.tooltip-close').exists()).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('emits update:modelValue when close button is clicked', async () => {
|
|
95
|
+
wrapper = createWrapper({ modelValue: true }, { title: '<span>Title</span>' });
|
|
96
|
+
await nextTick();
|
|
97
|
+
|
|
98
|
+
await wrapper.find('.tooltip-close').trigger('click');
|
|
99
|
+
|
|
100
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
|
101
|
+
expect(wrapper.emitted('update:modelValue')[0]).toEqual([false]);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('slots', () => {
|
|
106
|
+
it('renders activator slot content', () => {
|
|
107
|
+
wrapper = createWrapper(
|
|
108
|
+
{},
|
|
109
|
+
{
|
|
110
|
+
activator: '<span class="custom-activator">Custom</span>',
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(wrapper.find('.custom-activator').exists()).toBe(true);
|
|
115
|
+
expect(wrapper.find('.custom-activator').text()).toBe('Custom');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('renders title slot when provided', async () => {
|
|
119
|
+
wrapper = createWrapper(
|
|
120
|
+
{ modelValue: true },
|
|
121
|
+
{ title: '<span class="custom-title">Custom Title</span>' }
|
|
122
|
+
);
|
|
123
|
+
await nextTick();
|
|
124
|
+
|
|
125
|
+
expect(wrapper.find('.tooltip-title').exists()).toBe(true);
|
|
126
|
+
expect(wrapper.find('.custom-title').exists()).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('renders default slot content', async () => {
|
|
130
|
+
wrapper = createWrapper({ modelValue: true });
|
|
131
|
+
await nextTick();
|
|
132
|
+
|
|
133
|
+
expect(wrapper.find('.tooltip-content').text()).toContain('Tooltip content');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('computed properties', () => {
|
|
138
|
+
it('calculates tooltip classes correctly', async () => {
|
|
139
|
+
wrapper = createWrapper({
|
|
140
|
+
modelValue: true,
|
|
141
|
+
variant: 'dark',
|
|
142
|
+
size: 'lg',
|
|
143
|
+
placement: 'top-center',
|
|
144
|
+
});
|
|
145
|
+
await nextTick();
|
|
146
|
+
|
|
147
|
+
const classes = wrapper.vm.tooltipClasses;
|
|
148
|
+
expect(classes['tooltip-popup']).toBe(true);
|
|
149
|
+
expect(classes['tooltip-popup--visible']).toBe(true);
|
|
150
|
+
expect(classes['tooltip-popup--dark']).toBe(true);
|
|
151
|
+
expect(classes['tooltip-popup--lg']).toBe(true);
|
|
152
|
+
expect(classes['tooltip-popup--top-center']).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('calculates arrow styles correctly', () => {
|
|
156
|
+
wrapper = createWrapper({ placement: 'top-center' });
|
|
157
|
+
|
|
158
|
+
const styles = wrapper.vm.arrowStyles;
|
|
159
|
+
expect(styles.left).toBe('50%');
|
|
160
|
+
expect(styles.transform).toBe('translateX(-50%)');
|
|
161
|
+
expect(styles.bottom).toBe('-6px');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('calculates controlled state correctly', () => {
|
|
165
|
+
const uncontrolledWrapper = createWrapper();
|
|
166
|
+
expect(uncontrolledWrapper.vm.isControlled).toBe(false);
|
|
167
|
+
|
|
168
|
+
const controlledWrapper = createWrapper({ modelValue: true });
|
|
169
|
+
expect(controlledWrapper.vm.isControlled).toBe(true);
|
|
170
|
+
|
|
171
|
+
uncontrolledWrapper.unmount();
|
|
172
|
+
controlledWrapper.unmount();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('props validation', () => {
|
|
177
|
+
it('accepts valid placement values', () => {
|
|
178
|
+
const placements = [
|
|
179
|
+
'top-left',
|
|
180
|
+
'top-center',
|
|
181
|
+
'top-right',
|
|
182
|
+
'bottom-left',
|
|
183
|
+
'bottom-center',
|
|
184
|
+
'bottom-right',
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
placements.forEach(placement => {
|
|
188
|
+
wrapper = createWrapper({ placement, modelValue: true });
|
|
189
|
+
expect(wrapper.find(`.tooltip-popup--${placement}`).exists()).toBe(true);
|
|
190
|
+
wrapper.unmount();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('accepts valid size values', () => {
|
|
195
|
+
const sizes = ['sm', 'md', 'lg'];
|
|
196
|
+
|
|
197
|
+
sizes.forEach(size => {
|
|
198
|
+
wrapper = createWrapper({ size, modelValue: true });
|
|
199
|
+
expect(wrapper.find(`.tooltip-popup--${size}`).exists()).toBe(true);
|
|
200
|
+
wrapper.unmount();
|
|
201
|
+
});
|
|
19
202
|
});
|
|
20
203
|
});
|
|
21
204
|
});
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# 🎯 Tooltip Component
|
|
2
|
+
|
|
3
|
+
## Visão Geral
|
|
4
|
+
|
|
5
|
+
O componente Tooltip fornece informações contextuais ao usuário através de uma pequena caixa flutuante que aparece ao interagir com um elemento. Ideal para exibir textos explicativos, dicas ou informações adicionais sem poluir a interface.
|
|
6
|
+
|
|
7
|
+
## 🚀 Features
|
|
8
|
+
|
|
9
|
+
### Posicionamento Inteligente
|
|
10
|
+
- 6 posições pré-definidas com detecção automática de colisão
|
|
11
|
+
- Reposicionamento automático quando próximo às bordas da viewport
|
|
12
|
+
- Seta indicadora que sempre aponta para o centro do elemento ativador
|
|
13
|
+
|
|
14
|
+
### Modos de Ativação
|
|
15
|
+
- **Hover**: Aparece ao passar o mouse (padrão)
|
|
16
|
+
- **Click**: Ativado por clique
|
|
17
|
+
- **Manual**: Controle total via v-model
|
|
18
|
+
|
|
19
|
+
### Comportamento Responsivo
|
|
20
|
+
- Z-index dinâmico que detecta modais e se ajusta automaticamente
|
|
21
|
+
- Scroll listeners para manter posicionamento correto
|
|
22
|
+
- Suporte a elementos scrolláveis dentro de modais
|
|
23
|
+
|
|
24
|
+
### Performance
|
|
25
|
+
- Lazy rendering - só renderiza quando visível
|
|
26
|
+
- Cache inteligente para detecção de modais
|
|
27
|
+
- Event listeners otimizados com passive: true
|
|
28
|
+
|
|
29
|
+
## 📦 Instalação e Importação
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { Tooltip } from '@farm-investimentos/front-mfe-components-vue3';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 🎨 API
|
|
36
|
+
|
|
37
|
+
### Props
|
|
38
|
+
|
|
39
|
+
| Prop | Tipo | Padrão | Descrição |
|
|
40
|
+
|------|------|---------|-----------|
|
|
41
|
+
| `modelValue` | `boolean` | `undefined` | Controle de visibilidade (v-model) |
|
|
42
|
+
| `trigger` | `'hover' \| 'click' \| 'manual'` | `'hover'` | Modo de ativação |
|
|
43
|
+
| `placement` | `TooltipPlacement` | `'top-center'` | Posição do tooltip |
|
|
44
|
+
| `offset` | `number` | `8` | Distância do elemento ativador (px) |
|
|
45
|
+
| `variant` | `'dark'` | `'dark'` | Tema visual (apenas dark disponível) |
|
|
46
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Tamanho do tooltip |
|
|
47
|
+
| `maxWidth` | `string \| number` | `undefined` | Largura máxima customizada |
|
|
48
|
+
| `delay` | `number \| [number, number]` | `[100, 50]` | Delay para mostrar/esconder (ms) |
|
|
49
|
+
| `disabled` | `boolean` | `false` | Desabilita o tooltip |
|
|
50
|
+
| `fluid` | `boolean` | `false` | Largura fluida (max 300px) |
|
|
51
|
+
|
|
52
|
+
### Eventos
|
|
53
|
+
|
|
54
|
+
| Evento | Payload | Descrição |
|
|
55
|
+
|--------|---------|-----------|
|
|
56
|
+
| `update:modelValue` | `boolean` | Emitido ao mudar visibilidade |
|
|
57
|
+
| `show` | - | Emitido ao mostrar |
|
|
58
|
+
| `hide` | - | Emitido ao esconder |
|
|
59
|
+
|
|
60
|
+
### Slots
|
|
61
|
+
|
|
62
|
+
| Slot | Props | Descrição |
|
|
63
|
+
|------|-------|-----------|
|
|
64
|
+
| `activator` | - | Elemento que ativa o tooltip |
|
|
65
|
+
| `default` | - | Conteúdo principal |
|
|
66
|
+
| `title` | - | Título opcional (só aparece em modo controlado) |
|
|
67
|
+
|
|
68
|
+
## 💡 Exemplos de Uso
|
|
69
|
+
|
|
70
|
+
### Básico
|
|
71
|
+
```vue
|
|
72
|
+
<Tooltip>
|
|
73
|
+
<template #activator>
|
|
74
|
+
<button>Hover me</button>
|
|
75
|
+
</template>
|
|
76
|
+
Informação adicional sobre este botão
|
|
77
|
+
</Tooltip>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Posicionamento Customizado
|
|
81
|
+
```vue
|
|
82
|
+
<Tooltip placement="bottom-right" :offset="12">
|
|
83
|
+
<template #activator>
|
|
84
|
+
<IconHelp />
|
|
85
|
+
</template>
|
|
86
|
+
Ajuda contextual posicionada à direita
|
|
87
|
+
</Tooltip>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Modo Controlado com Título
|
|
91
|
+
```vue
|
|
92
|
+
<template>
|
|
93
|
+
<Tooltip v-model="showHelp">
|
|
94
|
+
<template #activator>
|
|
95
|
+
<button @click="showHelp = !showHelp">
|
|
96
|
+
Toggle Help
|
|
97
|
+
</button>
|
|
98
|
+
</template>
|
|
99
|
+
<template #title>
|
|
100
|
+
Informações Importantes
|
|
101
|
+
</template>
|
|
102
|
+
<ul>
|
|
103
|
+
<li>Primeiro ponto</li>
|
|
104
|
+
<li>Segundo ponto</li>
|
|
105
|
+
<li>Terceiro ponto</li>
|
|
106
|
+
</ul>
|
|
107
|
+
</Tooltip>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<script setup>
|
|
111
|
+
import { ref } from 'vue';
|
|
112
|
+
const showHelp = ref(false);
|
|
113
|
+
</script>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Tooltip Fluido para Conteúdo Longo
|
|
117
|
+
```vue
|
|
118
|
+
<Tooltip :fluid="true">
|
|
119
|
+
<template #activator>
|
|
120
|
+
<span>Ver descrição completa</span>
|
|
121
|
+
</template>
|
|
122
|
+
Este é um texto mais longo que se adapta automaticamente
|
|
123
|
+
à largura do conteúdo, expandindo até o máximo de 300px
|
|
124
|
+
antes de quebrar em múltiplas linhas.
|
|
125
|
+
</Tooltip>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Diferentes Tamanhos
|
|
129
|
+
```vue
|
|
130
|
+
<div class="flex gap-4">
|
|
131
|
+
<Tooltip size="sm">
|
|
132
|
+
<template #activator>
|
|
133
|
+
<Badge>SM</Badge>
|
|
134
|
+
</template>
|
|
135
|
+
Tooltip pequeno
|
|
136
|
+
</Tooltip>
|
|
137
|
+
|
|
138
|
+
<Tooltip size="md">
|
|
139
|
+
<template #activator>
|
|
140
|
+
<Badge>MD</Badge>
|
|
141
|
+
</template>
|
|
142
|
+
Tooltip médio (padrão)
|
|
143
|
+
</Tooltip>
|
|
144
|
+
|
|
145
|
+
<Tooltip size="lg">
|
|
146
|
+
<template #activator>
|
|
147
|
+
<Badge>LG</Badge>
|
|
148
|
+
</template>
|
|
149
|
+
Tooltip grande com mais espaçamento
|
|
150
|
+
</Tooltip>
|
|
151
|
+
</div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Com Delay Customizado
|
|
155
|
+
```vue
|
|
156
|
+
<Tooltip :delay="[500, 0]">
|
|
157
|
+
<template #activator>
|
|
158
|
+
<button>Hover com delay</button>
|
|
159
|
+
</template>
|
|
160
|
+
Aparece após 500ms, desaparece instantaneamente
|
|
161
|
+
</Tooltip>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## 🎯 Casos de Uso Comuns
|
|
165
|
+
|
|
166
|
+
### 1. Ícones de Ajuda
|
|
167
|
+
```vue
|
|
168
|
+
<label>
|
|
169
|
+
CPF
|
|
170
|
+
<Tooltip placement="top-right">
|
|
171
|
+
<template #activator>
|
|
172
|
+
<IconQuestion class="ml-1 text-gray-400" />
|
|
173
|
+
</template>
|
|
174
|
+
Digite apenas números, sem pontos ou traços
|
|
175
|
+
</Tooltip>
|
|
176
|
+
</label>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 2. Botões com Ações Desabilitadas
|
|
180
|
+
```vue
|
|
181
|
+
<Tooltip :disabled="canDelete">
|
|
182
|
+
<template #activator>
|
|
183
|
+
<button :disabled="!canDelete" @click="handleDelete">
|
|
184
|
+
Excluir
|
|
185
|
+
</button>
|
|
186
|
+
</template>
|
|
187
|
+
Você não tem permissão para excluir este item
|
|
188
|
+
</Tooltip>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 3. Truncate com Preview Completo
|
|
192
|
+
```vue
|
|
193
|
+
<Tooltip>
|
|
194
|
+
<template #activator>
|
|
195
|
+
<p class="truncate max-w-xs">
|
|
196
|
+
{{ longText }}
|
|
197
|
+
</p>
|
|
198
|
+
</template>
|
|
199
|
+
{{ longText }}
|
|
200
|
+
</Tooltip>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 4. Status com Detalhes
|
|
204
|
+
```vue
|
|
205
|
+
<Tooltip placement="bottom-center">
|
|
206
|
+
<template #activator>
|
|
207
|
+
<StatusBadge :status="order.status" />
|
|
208
|
+
</template>
|
|
209
|
+
<div>
|
|
210
|
+
<p><strong>Status:</strong> {{ order.status }}</p>
|
|
211
|
+
<p><strong>Atualizado:</strong> {{ order.updatedAt }}</p>
|
|
212
|
+
<p><strong>Por:</strong> {{ order.updatedBy }}</p>
|
|
213
|
+
</div>
|
|
214
|
+
</Tooltip>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## ⚡ Performance e Boas Práticas
|
|
218
|
+
|
|
219
|
+
### Do's ✅
|
|
220
|
+
- Use tooltips para informações secundárias e não-críticas
|
|
221
|
+
- Mantenha o conteúdo conciso e direto
|
|
222
|
+
- Use o placement adequado para não cobrir elementos importantes
|
|
223
|
+
- Para conteúdo complexo, considere usar um Popover ou Modal
|
|
224
|
+
|
|
225
|
+
### Don'ts ❌
|
|
226
|
+
- Não use tooltips para informações essenciais
|
|
227
|
+
- Evite tooltips em elementos mobile/touch
|
|
228
|
+
- Não coloque formulários ou ações dentro de tooltips
|
|
229
|
+
- Evite tooltips aninhados
|
|
230
|
+
|
|
231
|
+
## 🔧 Troubleshooting
|
|
232
|
+
|
|
233
|
+
### Tooltip aparece atrás de outros elementos
|
|
234
|
+
O componente já calcula automaticamente o z-index baseado em modais presentes. Se ainda houver problemas, verifique se o elemento pai não tem `overflow: hidden`.
|
|
235
|
+
|
|
236
|
+
### Tooltip não aparece
|
|
237
|
+
1. Verifique se `disabled` não está `true`
|
|
238
|
+
2. Em modo controlado, certifique-se que `modelValue` está sendo atualizado
|
|
239
|
+
3. Verifique se o slot `activator` tem apenas um elemento raiz
|
|
240
|
+
|
|
241
|
+
### Posicionamento incorreto
|
|
242
|
+
O tooltip se reposiciona automaticamente ao detectar scroll ou resize. Se o problema persistir:
|
|
243
|
+
1. Verifique se o elemento activator não está com `position: fixed`
|
|
244
|
+
2. Certifique-se que o container pai não tem transforms 3D
|
|
245
|
+
|
|
246
|
+
## 🎨 Customização
|
|
247
|
+
|
|
248
|
+
### CSS Variables
|
|
249
|
+
```css
|
|
250
|
+
.tooltip-popup {
|
|
251
|
+
--tooltip-bg: #333333;
|
|
252
|
+
--tooltip-color: #f5f5f5;
|
|
253
|
+
--tooltip-radius: 5px;
|
|
254
|
+
--tooltip-font-size: 12px;
|
|
255
|
+
--tooltip-padding: 16px;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Classes para Styling
|
|
260
|
+
```css
|
|
261
|
+
.tooltip-popup--visible { /* Quando visível */ }
|
|
262
|
+
.tooltip-popup--sm { /* Tamanho pequeno */ }
|
|
263
|
+
.tooltip-popup--md { /* Tamanho médio */ }
|
|
264
|
+
.tooltip-popup--lg { /* Tamanho grande */ }
|
|
265
|
+
.tooltip-popup--has-title { /* Quando tem título */ }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## 📊 Especificações Técnicas
|
|
269
|
+
|
|
270
|
+
### Posições Suportadas
|
|
271
|
+
- `top-left`: Acima e alinhado à esquerda
|
|
272
|
+
- `top-center`: Acima e centralizado
|
|
273
|
+
- `top-right`: Acima e alinhado à direita
|
|
274
|
+
- `bottom-left`: Abaixo e alinhado à esquerda
|
|
275
|
+
- `bottom-center`: Abaixo e centralizado
|
|
276
|
+
- `bottom-right`: Abaixo e alinhado à direita
|
|
277
|
+
|
|
278
|
+
### Comportamento da Seta
|
|
279
|
+
- Sempre aponta para o centro do elemento ativador
|
|
280
|
+
- Ajusta-se dinamicamente quando o tooltip é reposicionado
|
|
281
|
+
- Mantém margem mínima de 12px das bordas
|
|
282
|
+
|
|
283
|
+
### Detecção de Colisão
|
|
284
|
+
- Viewport boundaries detection
|
|
285
|
+
- Automatic repositioning
|
|
286
|
+
- Scroll containers support
|
|
287
|
+
- Modal-aware positioning
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import Tooltip from './Tooltip.vue';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
TooltipProps,
|
|
4
|
+
TooltipEmits,
|
|
5
|
+
TooltipPlacement,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
TooltipVariant,
|
|
8
|
+
TooltipSize,
|
|
9
|
+
} from './types';
|
|
4
10
|
|
|
5
11
|
export { Tooltip };
|
|
12
|
+
export type {
|
|
13
|
+
TooltipProps,
|
|
14
|
+
TooltipEmits,
|
|
15
|
+
TooltipPlacement,
|
|
16
|
+
TooltipTrigger,
|
|
17
|
+
TooltipVariant,
|
|
18
|
+
TooltipSize,
|
|
19
|
+
};
|
|
20
|
+
export default Tooltip;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type TooltipPlacement =
|
|
2
|
+
| 'top-left'
|
|
3
|
+
| 'top-center'
|
|
4
|
+
| 'top-right'
|
|
5
|
+
| 'bottom-left'
|
|
6
|
+
| 'bottom-center'
|
|
7
|
+
| 'bottom-right';
|
|
8
|
+
|
|
9
|
+
export type TooltipTrigger = 'hover' | 'click' | 'manual';
|
|
10
|
+
|
|
11
|
+
export type TooltipVariant = 'dark' | 'light';
|
|
12
|
+
|
|
13
|
+
export type TooltipSize = 'sm' | 'md' | 'lg';
|
|
14
|
+
|
|
15
|
+
export interface TooltipProps {
|
|
16
|
+
// Visibility control (Vue 3 style)
|
|
17
|
+
modelValue?: boolean; // v-model
|
|
18
|
+
trigger?: TooltipTrigger;
|
|
19
|
+
|
|
20
|
+
// Placement (all 6 positions are supported)
|
|
21
|
+
placement?: TooltipPlacement;
|
|
22
|
+
offset?: number;
|
|
23
|
+
|
|
24
|
+
// Aparência
|
|
25
|
+
variant?: TooltipVariant;
|
|
26
|
+
size?: TooltipSize;
|
|
27
|
+
maxWidth?: string | number;
|
|
28
|
+
|
|
29
|
+
// Comportamento
|
|
30
|
+
delay?: number | [number, number]; // [show, hide]
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
fluid?: boolean;
|
|
33
|
+
position?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TooltipEmits {
|
|
37
|
+
'update:modelValue': [value: boolean]; // Vue 3 v-model
|
|
38
|
+
show: [];
|
|
39
|
+
hide: [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Position {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TooltipState {
|
|
48
|
+
isVisible: boolean;
|
|
49
|
+
position: Position;
|
|
50
|
+
actualPlacement: TooltipPlacement;
|
|
51
|
+
}
|