Issue with Swapping Element Positions in a Grid Using SolidJS and @thisbeyond/solid-dnd

19 Views Asked by At

I am developing a hash game (or similar) using SolidJS, utilizing the @thisbeyond/solid-dnd library to implement drag-and-drop functionalities. My goal is to allow users to drag and drop blocks within a grid to reorganize elements. Although the dragging and drop location detection are working correctly, I am facing difficulties in updating the component's state and reflecting the position swap visually in the grid.

Current Code:

import { createSignal, For, Show } from "solid-js";
import {
  DragDropProvider,
  DragDropSensors,
  createDraggable,
  createDroppable,
  DragDropDebugger,
} from "@thisbeyond/solid-dnd";
import styles from "./hashgame.module.css";

// Componente para cada quadrado que pode ser arrastado e onde pode soltar outros quadrados
const DraggableSquare = (props) => {
  // Destruturação das props para ficar mais fácil de usar
  const { item, swapItems, isDraggable } = props;

  // Cria um quadrado que pode ser arrastado, passando o ID único
  const draggable = createDraggable(`draggable-${item}`, {
    data: item, // Dados que quero passar junto quando arrasto, no caso, o próprio item
  });

  // Cria uma área onde posso soltar o quadrado arrastado, usando o mesmo ID pra ficar fácil de identificar
  const droppable = createDroppable(`droppable-${item}`, {
    onDrop: ({ detail }) => {
      console.log(detail);
      // Função que roda quando solto o quadrado aqui
      const sourceIndex = detail.draggable.data; // Pega o item arrastado
      const targetIndex = item; // E o item alvo onde soltei
      swapItems(sourceIndex, targetIndex); // Troca eles de lugar
    },
  });

  // Renderiza o quadrado com as props de arrastável e soltável aplicadas
  // Além disso, aplica uma classe diferente se não for pra ser arrastável
  return (
    <div
      use:draggable
      use:droppable
      class={`${styles.square} ${!isDraggable ? styles.squareInv : ""}`}
    >
      {item} {/* Mostra o número/item dentro do quadrado */}
    </div>
  );
};

// Componente principal do jogo
const HashGame = () => {
  // Cria um estado inicial com um array de 25 elementos
  const [divArray, setDivArray] = createSignal(
    Array.from({ length: 25 }, (_, i) => i),
  );

  // Define quais índices não vão poder ser arrastados
  const invisibilitySquareIndex = [0, 2, 4, 10, 12, 14, 20, 22, 24];

  // Função para trocar dois quadrados de lugar no estado
  const swapItems = (sourceIndex, targetIndex) => {
    console.log("Antes da trocac:", divArray())
    setDivArray((current) => {
      // Atualiza o estado atual
      const newArr = [...current]; // Copia o array pra não alterar o estado diretamente
      // Encontra a posição de cada item no array
      const sourcePos = current.indexOf(sourceIndex);
      const targetPos = current.indexOf(targetIndex);
      console.log(`Source Index: ${sourceIndex}, Target Index: ${targetIndex}`); // Log dos índices
      console.log(`Source Pos: ${sourcePos}, Target Pos: ${targetPos}`); // Log das posições      
      // Troca os itens de lugar
      [newArr[sourcePos], newArr[targetPos]] = [
        newArr[targetPos],
        newArr[sourcePos],
      ];
      console.log("Depois da troca:", newArr); // Log do novo array
      return newArr; // Retorna o novo array atualizado
    });
  };

  // Renderiza o jogo
  return (
    <DragDropProvider>
      <DragDropSensors />
      <div className={`${styles.boardRow} ${styles.unselectable}`}>
        {" "}
        {/* Container para os quadrados */}
        <For each={divArray()}>
          {(
            item,
            index, // Loop para renderizar cada quadrado
          ) => (
            <Show when={!invisibilitySquareIndex.includes(index)}>
              {" "}
              {/* Só mostra se não estiver no array de invisíveis */}
              <DraggableSquare
                item={item}
                swapItems={swapItems}
                isDraggable={!invisibilitySquareIndex.includes(item)} // Se for arrastável ou não
              />
            </Show>
          )}
        </For>
      </div>
      <DragDropDebugger />
    </DragDropProvider>
  );
};

export default HashGame;
.boardRow {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-columns: repeat(5, 1fr);
  gap: 0.3rem;
  width: 400px;
  height: 400px;
}

.square {
  width: 4.5rem;
  height: 4.5rem;
  border: 2px solid black;
  display: flex;
  justify-content: center;
  align-items: center;
}
.squareInv {
  opacity: 0;
  pointer-events: none;
}
/* Remove qualquer forma de selecionar um elemento*/
.unselectable {
  -webkit-user-select: none; /* Safari */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE10+/Edge */
  user-select: none; /* Chrome/Opera */
}

Expected Behavior: When a block is dragged and dropped onto another, I expect them to swap positions in the array representing the grid, and for this swap to be visually reflected in the interface.

Current Behavior: The drag and drop are detected, but the position swap does not occur either in the state array or visually in the interface. The blocks remain in their original positions.

Attempted Solutions:

I checked if the IDs of the draggable and droppable elements are unique. I added console.log in the swap function to check if it is being called, but I got no log in the console. I reviewed the documentation of the @thisbeyond/solid-dnd library to ensure I am using the APIs correctly. Questions:

Is there something wrong with the way I am using the @thisbeyond/solid-dnd library to handle drag and drop? How can I ensure that the swap of positions in the state array is visually reflected in the interface after a drag-and-drop operation? Is there a more effective approach in SolidJS to update an array's state and reflect those changes in the UI when working with drag-and-drop functionalities? I appreciate any guidance or suggestion you can offer in advance!

0

There are 0 best solutions below