/** * useColunasOrdenacao * Composable que encapsula a lógica de drag-and-drop e sincronização de estado * do modal de configuração de colunas da EliTabela. * * Responsável por: * - Sincronizar colunas visíveis/invisíveis com a configuração persistida * - Aplicar ordem salva e adicionar novas colunas ao final * - Gerenciar eventos de arrastar-e-soltar entre as duas listas * * Extraído de `EliTabelaModalColunas.vue` para manter o componente abaixo de 250 linhas. */ import { type Ref, ref, watch } from "vue" import type { EliTabelaColunasConfig } from "./colunasStorage" import type { tipoEliColuna } from "./types-eli-tabela" type OrigemLista = "visiveis" | "invisiveis" interface UseColunasOrdenacaoOptions { aberto: Ref rotulosColunas: Ref configInicial: Ref // biome-ignore lint/suspicious/noExplicitAny: colunas genéricas — tipo parametrizado no pai colunas: Ref[]> } /** * Composable de drag-and-drop para reordenação e alternância de visibilidade de colunas. * Sincroniza automaticamente quando `aberto`, `rotulosColunas`, `configInicial` ou `colunas` mudam. * * @param options.aberto - Ref que controla se o modal está aberto (gatilho de sincronização) * @param options.rotulosColunas - Ref com todos os rótulos de colunas da tabela * @param options.configInicial - Ref com a configuração persistida (visíveis + invisíveis) * @param options.colunas - Ref com a definição completa das colunas (para ler `visivel: false` padrão) * @returns Estado reativo e handlers de drag-and-drop prontos para o template */ export function useColunasOrdenacao({ aberto, rotulosColunas, configInicial, colunas, }: UseColunasOrdenacaoOptions) { const visiveisLocal = ref([]) const invisiveisLocal = ref([]) const itemSendoArrastado = ref(null) function sincronizarEstado() { const todos = rotulosColunas.value const configTemDados = (configInicial.value.visiveis?.length ?? 0) > 0 || (configInicial.value.invisiveis?.length ?? 0) > 0 const invisiveisPadraoSet = new Set( colunas.value.filter((c) => c.visivel === false).map((c) => c.rotulo), ) const invisiveisSet = configTemDados ? new Set(configInicial.value.invisiveis ?? []) : invisiveisPadraoSet const baseVisiveis = todos.filter((r) => !invisiveisSet.has(r)) const ordemSalva = configInicial.value.visiveis ?? [] const setVis = new Set(baseVisiveis) const ordenadas: string[] = [] for (const r of ordemSalva) { if (setVis.has(r)) ordenadas.push(r) } for (const r of baseVisiveis) { if (!ordenadas.includes(r)) ordenadas.push(r) } visiveisLocal.value = ordenadas invisiveisLocal.value = todos.filter((r) => invisiveisSet.has(r)) } watch( () => [aberto.value, rotulosColunas.value, configInicial.value, colunas.value] as const, () => { if (aberto.value) sincronizarEstado() }, { deep: true, immediate: true }, ) function onDragStart(e: DragEvent, rotulo: string) { itemSendoArrastado.value = rotulo if (e.dataTransfer) { e.dataTransfer.setData("text/plain", rotulo) e.dataTransfer.effectAllowed = "move" } } function onDragEnd() { itemSendoArrastado.value = null } function onDragOverItem(alvoRotulo: string, destino: OrigemLista) { const draggedRotulo = itemSendoArrastado.value if (!draggedRotulo || draggedRotulo === alvoRotulo) return const origemVisIdx = visiveisLocal.value.indexOf(draggedRotulo) const origemInvIdx = invisiveisLocal.value.indexOf(draggedRotulo) const origem = origemVisIdx >= 0 ? "visiveis" : origemInvIdx >= 0 ? "invisiveis" : null if (!origem) return const origemArr = origem === "visiveis" ? visiveisLocal.value : invisiveisLocal.value const destinoArr = destino === "visiveis" ? visiveisLocal.value : invisiveisLocal.value const dragIdx = origemArr.indexOf(draggedRotulo) const alvoIdx = destinoArr.indexOf(alvoRotulo) origemArr.splice(dragIdx, 1) destinoArr.splice(alvoIdx, 0, draggedRotulo) } function onDragOverLista(destino: OrigemLista) { const draggedRotulo = itemSendoArrastado.value if (!draggedRotulo) return const destinoArr = destino === "visiveis" ? visiveisLocal.value : invisiveisLocal.value if (destinoArr[destinoArr.length - 1] === draggedRotulo) return const origemVisIdx = visiveisLocal.value.indexOf(draggedRotulo) const origemInvIdx = invisiveisLocal.value.indexOf(draggedRotulo) if (origemVisIdx >= 0) visiveisLocal.value.splice(origemVisIdx, 1) if (origemInvIdx >= 0) invisiveisLocal.value.splice(origemInvIdx, 1) destinoArr.push(draggedRotulo) } return { visiveisLocal, invisiveisLocal, itemSendoArrastado, onDragStart, onDragEnd, onDragOverItem, onDragOverLista, } }