FAQ_DRAG_DROP.md

FAQ Reordenação Drag & Drop (Perguntas Frequentes)

Public Markdown Document

FAQ Reordenação Drag & Drop (Perguntas Frequentes)

R : Adicione a prop sortable="true" ao DynamicList:

917 palavras24 headingsrepository root

FAQ - Reordenação Drag & Drop (Perguntas Frequentes)

❓ Perguntas sobre Funcionalidade

P: Como ativo a reordenação no meu componente?

R: Adicione a prop sortable="true" ao DynamicList:

<DynamicList
  :schema="schema"
  :data="data"
  :sortable="true"
  @reorder="handleReorder"
/>

P: Qual é a diferença entre DynamicList e PropertyListEditor?

R:

  • DynamicList.vue: Componente genérico para exibir coleções de dados com reordenação opcional
  • PropertyListEditor.vue: Componente especializado para editar propriedades de objetos (metadados) com suporte completo a inline-editing

P: O que é o "drag-handle"?

R: É o ícone ⋮⋮ (seis pontos) que aparece no início de cada linha quando sortable="true". Você deve clicar/arrastar nele para mover a linha de posição.

P: Preciso atualizar a API quando reordenar?

R: Não imediatamente. O componente emite o evento reorder com a nova ordem. Você deve:

  1. Capturar o evento
  2. Marcar como "dirty" (isDirty = true)
  3. Aguardar o usuário clicar "Salvar"
  4. Então enviar para a API

P: Como funciona a sincronização com o estado "dirty"?

R:

@reorder="(newList) => {
  isDirty = true;  // Marca como modificado
  dataList = newList;  // Atualiza dados locais
  toast.add({ severity: 'info', summary: 'Alteração pendente' });
}"

P: Posso reordenar dentro de um grupo específico?

R: Sim. Use a propriedade group no componente:

const dragOptions = {
  group: "meu-grupo",  // Restringe a qual grupo pode arrastar
  // ...
}

P: O que acontece se o usuário sair sem salvar?

R: Dois comportamentos:

  1. PropertyListEditor: Detecta isDirty e avisa ("Existem alterações não salvas")
  2. DynamicList: Sem gestão de dirty, você controla no componente pai

❓ Perguntas sobre UI/UX

P: Como faço o handle aparecer apenas ao passar o mouse?

R: Atualmente ele aparece sempre. Se quiser mudar, adicione CSS:

.drag-handle {
  opacity: 0.3;  /* Invisível */
}
.record-card:hover .drag-handle {
  opacity: 1;  /* Visível ao passar mouse */
}

P: Posso customizar o ícone do handle?

R: Sim! Edite em DynamicList.vue ou PropertyListEditor.vue:

<!-- Antes -->
<i class="pi pi-bars text-gray-400 text-sm"></i>

<!-- Depois (exemplo com ícone de drag) -->
<i class="pi pi-arrow-down-up text-gray-400 text-sm"></i>

P: Qual é a cor durante o arraste?

R: Configurada em CSS no .drag class:

.drag {
  opacity: 1;
  transform: rotate(2deg) scale(1.02);
  box-shadow: 0 5px 25px rgba(99, 102, 241, 0.3);  /* Azul índigo */
}

P: Posso mudar a duração da animação?

R: Sim, no dragOptions:

const dragOptions = {
  animation: 200,  // ms (mude aqui)
  // ...
}

P: Como funciona no mobile?

R: Funciona normalmente, mas considere:

  • Handle talvez seja pequeno (aumentar em mobile)
  • Arraste de toque é suportado
  • Recomenda-se aumentar padding em telas pequenas

❓ Perguntas sobre Dados

P: Os dados são salvos automaticamente ao reordenar?

R: Não. Você deve:

  1. Capturar evento reorder
  2. Atualizar estado local
  3. Manualmente chamar API ao clicar "Salvar"

Exemplo:

function handleReorder(newOrder) {
  localData.value = newOrder;  // Local
  isDirty.value = true;        // Marcar como pendente
  // Não salva ainda!
}

P: Como garanto que a ordem é única?

R: A ordem é determinada pelo índice no array. Vue mantém a reatividade automaticamente.

P: E se duas propriedades tiverem o mesmo nome?

R: Isso causaria conflito. Implemente validação:

function validatePropertyName(newName) {
  const exists = editingObject.properties.some(p => p.name === newName);
  if (exists) {
    toast.add({ severity: 'error', summary: 'Nome já existe' });
    return false;
  }
  return true;
}

P: Como desfaço uma reordenação?

R: Três opções:

  1. Sem salvar: Recarregue a página (dados não salvos são perdidos)
  2. Com salvar: Reordene novamente manualmente
  3. Undo avançado: Implemente stack de mudanças (ver roadmap)

P: Posso reordenar entre múltiplas listas?

R: Apenas se as listas tiverem o mesmo group:

// Lista A
const dragOptionsA = { group: "shared" };

// Lista B  
const dragOptionsB = { group: "shared" };

// Agora itens podem ir de A para B e vice-versa

❓ Perguntas sobre Performance

P: Qual é o limite de propriedades que posso reordenar?

R: Teoricamente ilimitado, mas:

  • < 50: Muito rápido
  • 50-200: Rápido
  • 200+: Considere virtualização (ainda não implementada)

P: Há lag ao arrastar muitos itens?

R: Se houver, verifique:

  1. Número de computed properties
  2. Watchers profundos
  3. Tamanho dos dados renderizados
  4. Performance do navegador (DevTools > Performance)

P: Como otimizo a renderização?

R:

// ✅ BOM: Computed para dados filtrados
const visibleProperties = computed(() => {
  return properties.value.filter(p => p.show_in_list);
});

// ❌ EVITE: Lógica no template
<div v-for="p in properties.filter(p => p.show_in_list)">

P: Devo usar v-key="item.id" ou v-key="index"?

R: Sempre item.id se disponível. O índice muda ao reordenar, causando re-renders desnecessários.


❓ Perguntas sobre Integração

P: Como integro com dados do backend?

R:

// 1. Fetch dos dados
const properties = ref([]);
onMounted(async () => {
  properties.value = await $fetch('/api/object/lead/properties');
});

// 2. Componente com sortable
<PropertyListEditor
  :properties="properties"
  @reorder="onReorder"
/>

// 3. Handler
function onReorder(newOrder) {
  isDirty = true;
  // Salvar mais tarde com API call
}

P: Como funciona com Pinia store?

R:

// store/object.ts
const properties = ref([]);

function setProperties(newProperties) {
  properties.value = newProperties;
}

function reorderProperties(newOrder) {
  properties.value = newOrder;
  isDirty = true;  // Marcar store como dirty
  // Salvar quando necessário
}

P: Como sincronizo com Sistema de Manifesto?

R: O Schema Manager já faz isso. Seu objeto é:

editingObject.value.properties = reorderedProperties;  // Atualiza referência
isDirty.value = true;  // Marca para salvar

❓ Perguntas sobre Debugging

P: Como vejo quais eventos estão sendo disparados?

R: Adicione logs no handler:

function onReorder(newOrder) {
  console.log('[REORDER EVENT]', newOrder);
  console.log('[ORDER NAMES]', newOrder.map(p => p.name));
}

P: Como verifico o estado drag durante animação?

R: Use DevTools:

  1. Abra Elements/Inspector
  2. Selecione um item durante arraste
  3. Veja classe .drag sendo aplicada

P: Como verifico se isDirty está sendo marcado?

R:

watch(() => isDirty.value, (newVal) => {
  console.log('[DIRTY CHANGED]', newVal);
});

P: Por que o evento reorder é emitido múltiplas vezes?

R: Verifique se você tem múltiplos watchers:

// ❌ PROBLEMA: Watcher duplicado
watch(properties, () => {
  emit('reorder', properties.value);  // Emite sempre que muda
});

// ✅ SOLUÇÃO: Emita apenas no @end do draggable
@end="onDragEnd"

❓ Perguntas sobre Erros Comuns

P: "Cannot read property 'name' of undefined"

R: Propriedade está null. Verifique:

// ✅ BOM: Validação
const propName = property?.name || 'Unknown';

// ❌ EVITE: Sem checar
const propName = property.name;  // Erro se property = null

P: Array não atualiza após reordenar

R: Você está mutando diretamente? Use Vue.set:

// ❌ ERRADO
properties[0] = newValue;  // Vue não detecta

// ✅ CORRETO
properties.value[0] = newValue;  // Reativo
// ou
properties.value = [...properties.value];  // Novo array

P: Drag & Drop não funciona

R: Verifique:

  1. vuedraggable está importado?
  2. Prop sortable="true" foi adicionado?
  3. Handle .drag-handle existe no template?
  4. Browser suporta drag & drop?

P: Estilos não aplicam durante arraste

R: Você está importando o CSS de .drag?

<style scoped>
.drag {
  opacity: 1;
  box-shadow: 0 5px 25px rgba(99, 102, 241, 0.3);
}
</style>

❓ Perguntas sobre Acessibilidade

P: Como faço reordenação acessível via teclado?

R: Isso ainda não foi implementado. Você pode:

  1. Adicionar botões "Mover Para Cima"/"Mover Para Baixo"
  2. Usar Shift + Setas para reordenar (custom)
  3. Implementar ARIA labels

Exemplo de botões:

<Button 
  icon="pi pi-arrow-up"
  @click="moveUp(idx)"
  aria-label="Mover para cima"
/>
<Button 
  icon="pi pi-arrow-down"
  @click="moveDown(idx)"
  aria-label="Mover para baixo"
/>

P: Qual é o aria-label do handle?

R: Não tem atualmente. Adicione se necessário:

<div class="drag-handle" aria-label="Arrastar para reordenar">

❓ Perguntas sobre Roadmap

P: Vocês vão implementar Undo/Redo?

R: Não está planned ainda, mas é possível usando:

const history = ref([]);
const historyIndex = ref(-1);

function saveToHistory() {
  history.value = history.value.slice(0, historyIndex.value + 1);
  history.value.push(JSON.parse(JSON.stringify(properties.value)));
  historyIndex.value++;
}

function undo() {
  if (historyIndex.value > 0) {
    historyIndex.value--;
    properties.value = JSON.parse(JSON.stringify(history.value[historyIndex.value]));
  }
}

P: Quando virá array virtual para 1000+ itens?

R: Não está planneed. Use paginação ou virtualScroller de PrimeVue.

P: Vocês vão adicionar persistência automática?

R: Considere debounce + auto-save:

const saveToDebounce = useDebounceFn(saveChanges, 2000);

function onReorder(newOrder) {
  isDirty.value = true;
  saveToDebounce();  // Salva automaticamente após 2s
}

❓ Perguntas sobre Suporte

P: Funciona em Safari?

R: Sim, com vuedraggable 4.0+. Tested em Safari 15+.

P: Funciona em IE11?

R: Não. Suporte apenas para navegadores modernos (Chrome, Firefox, Safari, Edge).

P: Posso usar com jQuery?

R: Não, este é um projeto Vue 3. jQuery não é suportado.

P: Qual é o licença deste código?

R: Mesmo projeto (verificar LICENSE.md).


📞 Contato & Suporte

Se você não encontrou resposta aqui:

  1. Verifique IMPLEMENTATION_DRAG_DROP.md
  2. Verifique TESTING_GUIDE.md
  3. Procure nos comentários do código
  4. Abra uma issue no GitHub

Última Atualização: 17/02/2026