DraggableButton

src/components/button/DraggableButton.vue
Action medium 2 usos

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.

Área da aplicação — fixed top:120px right:53px (simulado)
customRight: 53px
top: 120px
cursor: grab
Real src/views/chat-attendance/Index.vue:547
<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.

Área da aplicação — fixed bottom:120px left:20px (simulado)
left: 20px
bottom: 120px
cursor: grab · iconPack="material-icons" · icon="chat"
Sintético src/components/button/DraggableButton.vue
<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.

Idle — cursor: grab
moved = false · dragging = false
Dragging — cursor: grabbing
moved = true · dragging = true
Sintético src/components/button/DraggableButton.vue
<!-- 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

MinimizeOrdersButton — 0 usos, candidato a remoção
O arquivo 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

NomeTipoDefaultDescriçã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

NomePayloadDescriçã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:

1
X
customLeft → usa valor direto em px
2
X
customRight → calcula screenW - btnW - customRight
3
X
horizontalPosition"left": 20px / "right": screenW - btnW - 20px

Eixo vertical — avaliado na ordem:

1
Y
customTop → usa valor direto em px
2
Y
customBottom → calcula screenH - btnH - customBottom
3
Y
verticalPosition"top": 120px / "bottom": screenH - btnH - 120px

Comportamento de arrasto

FaseEventoAçã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

TipoDependência
Vuesax vs-button (radius, color='primary', type='filled')
Vuesax vs-icon — quando featherIcon === false
Custom feather-icon — quando featherIcon === true

Onde é usado

ArquivoCondiçãoAçã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