DraggableButton
Botão flutuante arrastável — o usuário pode reposicionar livremente na tela com drag & drop.
Usa eventos nativos mousedown/mousemove/mouseup no document para rastrear o arrasto.
A distinção entre arrasto e clique é feita via flag interna moved: o evento @click só é emitido quando o botão é solto sem ter movido.
Posicionamento: position: fixed; z-index: 999999; cursor: grab (muda para grabbing durante o arrasto).
A posição é recalculada automaticamente no resize da janela a partir das props.
Suporta ícones via Material Icons (vs-icon) ou Feather Icons (feather-icon) pela prop featherIcon.
Feather Icon — Reabrir Pedido (uso real)
Uso real em chat-attendance/Index.vue: botão com FileIcon (Feather) posicionado no canto superior direito via verticalPosition="top" + horizontalPosition="right" + :customRight="53".
O botão fica visível apenas quando showMinimizeDrag é verdadeiro. Ao clicar (sem arrastar), dispara reopenOrder.
A linha tracejada indica o offset de 53px a partir da borda direita da viewport.
<DraggableButton
v-show="showMinimizeDrag"
@click.prevent="reopenOrder"
verticalPosition="top"
horizontalPosition="right"
:customRight="53"
featherIcon
icon="FileIcon"
/>
Material Icon — bottom-left (sintético)
Quando featherIcon é false (padrão), o componente usa vs-icon com o iconPack configurado.
Exemplo sintético com icon="chat", verticalPosition="bottom" e horizontalPosition="left".
Posição calculada: left: 20px, top: screenH - btnH - 120px.
<DraggableButton
@click="openChat"
verticalPosition="bottom"
horizontalPosition="left"
iconPack="material-icons"
icon="chat"
/>
Estados de arrasto
O componente distingue dois estados visuais: idle (cursor: grab, sombra normal) e dragging (cursor: grabbing, sombra ampliada, escala ligeiramente aumentada via transform: scale(1.07)).
O evento click só é emitido no mouseup se a flag moved permanecer false — ou seja, o usuário apenas pressionou e soltou sem mover.
A posição é clamped entre [0, screenW-btnW] × [0, screenH-btnH] durante o arrasto.
<!-- Lógica interna de drag (DraggableButton.vue) -->
<!-- mousedown: captura offset cursor→botão, anexa listeners no document -->
<!-- mousemove: atualiza top/left clamped ao viewport; seta moved=true -->
<!-- mouseup: remove listeners; se !moved → emite 'click'; reseta estados -->
<div
:style="{
position: 'fixed',
top: top + 'px',
left: left + 'px',
cursor: dragging ? 'grabbing' : 'grab',
zIndex: 999999
}"
@mousedown.prevent="startDrag"
@click="handleClick"
>
<vs-button
v-if="!featherIcon"
:icon="icon"
:icon-pack="iconPack"
radius
color="primary"
type="filled"
/>
<vs-button
v-else
radius
color="primary"
type="filled"
>
<feather-icon
:icon="icon"
svgClasses="w-5 h-5 stroke-current"
:class="{ 'w-4 h-4': iconSmall }"
/>
</vs-button>
</div>
MinimizeOrdersButton — Protótipo abandonado
src/components/orders/MinimizeOrdersButton.vue é um protótipo de botão de minimização de pedidos que nunca chegou a ser utilizado. Não há nenhum uso no codebase. Deve ser removido em uma limpeza de componentes órfãos.
Diferente do DraggableButton (que tem 2 usos reais em chat-attendance),
o MinimizeOrdersButton nunca foi integrado a nenhuma view ou layout.
O DraggableButton é o componente correto para casos de uso de botão flutuante arrastável.
API
Props
| Nome | Tipo | Default | Descrição |
|---|---|---|---|
verticalPosition |
String | "top" |
"top" → 120px a partir do topo; "bottom" → screenH - btnH - 120px. Sobreposto por customTop / customBottom. |
horizontalPosition |
String | "right" |
"left" → 20px da esquerda; "right" → screenW - btnW - 20px. Sobreposto por customLeft / customRight. |
customTop |
Number | null |
Override de topo em px (absoluto). Prioridade sobre verticalPosition. |
customBottom |
Number | null |
Override: calcula screenH - btnH - customBottom. Prioridade sobre verticalPosition. |
customLeft |
Number | null |
Override de esquerda em px (absoluto). Prioridade sobre horizontalPosition. |
customRight |
Number | null |
Override: calcula screenW - btnW - customRight. Prioridade sobre horizontalPosition. |
iconPack |
String | "material-icons" |
Pack de ícones para vs-icon quando featherIcon é false. |
featherIcon |
Boolean | false |
Quando true, usa <feather-icon> com svgClasses='w-5 h-5 stroke-current' em vez de vs-icon. |
iconSmall |
Boolean | false |
Quando featherIcon + iconSmall são true: adiciona classes w-4 h-4 ao feather-icon. |
icon |
String | "" |
Nome do ícone — Material Icons (ex: "chat") ou Feather (ex: "FileIcon") conforme featherIcon. |
Eventos
| Nome | Payload | Descrição |
|---|---|---|
click |
MouseEvent | Emitido no mouseup quando o usuário solta o botão sem ter arrastado (moved === false). Não emitido após drag. |
Lógica de posicionamento (prioridade)
Eixo horizontal — avaliado na ordem:
customLeft → usa valor direto em px
customRight → calcula screenW - btnW - customRight
horizontalPosition → "left": 20px / "right": screenW - btnW - 20px
Eixo vertical — avaliado na ordem:
customTop → usa valor direto em px
customBottom → calcula screenH - btnH - customBottom
verticalPosition → "top": 120px / "bottom": screenH - btnH - 120px
Comportamento de arrasto
| Fase | Evento | Ação |
|---|---|---|
| Início | mousedown no botão |
Captura offset cursor→posição do botão. Anexa mousemove e mouseup no document. Define dragging = true. |
| Arrasto | mousemove no document |
Atualiza top e left clamped a [0, screenW-btnW] × [0, screenH-btnH]. Define moved = true. |
| Fim | mouseup no document |
Remove listeners. Se !moved → emite 'click'. Reseta dragging = false, moved = false. |
| Resize | window resize |
Recalcula posição a partir das props originais (não mantém posição arrastada pelo usuário). |
Dependências
| Tipo | Dependência |
|---|---|
| Vuesax | vs-button (radius, color='primary', type='filled') |
| Vuesax | vs-icon — quando featherIcon === false |
| Custom | feather-icon — quando featherIcon === true |
Onde é usado
| Arquivo | Condição | Ação no click |
|---|---|---|
src/views/chat-attendance/Index.vue:547 |
v-show="showMinimizeDrag" |
reopenOrder |
src/views/chat-attendance/NewIndex.vue |
v-show="showMinimizeDrag && !isKanbanView" |
reopenOrder |