formkit / drag-and-drop

https://drag-and-drop.formkit.com
MIT License
1.49k stars 30 forks source link

Drag drop not working on mobile #88

Closed hinn254 closed 1 month ago

hinn254 commented 3 months ago

https://github.com/user-attachments/assets/8740f3b1-19ad-4cf1-9487-c2452d1260dc

In this video, I start dragging as soon as I press start survey but nothing is happening, yet it works on desktop. I really need it to work on mobile as well. Any suggestions

cc @sashamilenkovic

hinn254 commented 3 months ago
Here is the code
import { SURVEY_UI_CONFIG } from "@/app/data";
import Prose from "@/components/prose";
import { FormContext } from "@/context/form-context";
import { FormField } from "@/types/form";
import type { ParentConfig } from "@formkit/drag-and-drop";
import { useDragAndDrop } from "@formkit/drag-and-drop/react";
import clsx from "clsx";
import React, { useContext, useEffect } from "react";
import { isMobile, useMobileOrientation } from "react-device-detect";

export default function MaxDiffPreferenceDragDrop({
  field,
  featureList = [""],
  currentIndex = 1,
  setCurrentStep,
  totalSteps = 1,
  task = 0,
}: {
  field: FormField;
  featureList: (string | undefined)[];
  currentIndex: number;
  setCurrentStep: (index: number) => void;
  totalSteps: number;
  task: any;
}) {
  const { handleMaxDiffAnswer, handleMaxDiffAnswerReset, surveyId } =
    useContext(FormContext);

  const [source, features, setFeatures] = useDragAndDrop<
    HTMLUListElement,
    string
  >(featureList as string[], {
    group: `maxDiff-${task}`,
    draggable: (el) => el.id !== "no-drag",
  });

  const config1: Partial<ParentConfig<string>> = {
    group: `maxDiff-${task}`,
  };

  const config2: Partial<ParentConfig<string>> = {
    group: `maxDiff-${task}`,
  };

  config1.accepts = (_parent, lastParent) => {
    if (lastParent.el === target2.current) {
      return false;
    }
    return mostLikedFeatures.length < 1;
  };

  config2.accepts = (_parent, lastParent) => {
    if (lastParent.el === target1.current) {
      return false;
    }
    return leastLikedFeatures.length < 1;
  };

  const [target1, mostLikedFeatures, setMostLikedFeatures] = useDragAndDrop(
    [],
    config1,
  );
  const [target2, leastLikedFeatures, setLeastLikedFeatures] = useDragAndDrop(
    [],
    config2,
  );

  const handleRemoveItem = (item: string, container: string) => {
    if (container === "mostLiked") {
      setMostLikedFeatures((prev) => prev.filter((i) => i !== item));
    } else if (container === "leastLiked") {
      setLeastLikedFeatures((prev) => prev.filter((i) => i !== item));
    }
    setFeatures((prev) => [...prev, item]);
  };

  const handleDragStart = (item: string, container: string) => {
    handleRemoveItem(item, container);
  };

  const handleTouchStart = (item: string, container: string) => {
    handleRemoveItem(item, container);
  };

  useEffect(() => {
    if (mostLikedFeatures.length > 0 && leastLikedFeatures.length > 0) {
      handleMaxDiffAnswer(
        field.id,
        mostLikedFeatures[0],
        leastLikedFeatures[0],
        task,
      );
    } else {
      handleMaxDiffAnswerReset(field.id, task);
    }
  }, [mostLikedFeatures, leastLikedFeatures]);

  const uiConfig = SURVEY_UI_CONFIG[parseInt(surveyId)];
  const { isPortrait } = useMobileOrientation();

  return (
    <div className="my-4 w-full">
      <ul ref={source} className="flex items-stretch justify-around gap-2 py-2">
        {features.map((file) => (
          <li
            key={file}
            onDragStart={(e) => {
              e.dataTransfer.setData("text/plain", file);
              handleDragStart(file, "features");
            }}
            onTouchStart={(e) => {
              handleTouchStart(file, "features");
            }}
            draggable
            className={clsx(
              "flex h-full max-w-52 flex-1 flex-col rounded-lg bg-bgSecondary p-2 px-4 text-center text-sm font-semibold",
              isMobile && isPortrait ? "min-h-16 p-1" : "min-h-32",
            )}
          >
            <Prose
              html={file ?? "Empty"}
              className={clsx(isMobile && isPortrait ? "!text-xs" : "text-sm")}
            />
          </li>
        ))}
      </ul>
      <div className="space-x- flex items-stretch justify-around gap-2 overflow-x-scroll pb-2 sm:justify-evenly md:space-x-4">
        <div
          ref={target1 as React.RefObject<HTMLDivElement>}
          className={clsx(
            "relative mt-4 flex flex-col items-center space-y-3 rounded-lg border border-borderSecondary sm:w-64",
            isMobile && isPortrait ? "min-h-10 p-0" : "min-h-48",
          )}
        >
          <p className="absolute -top-3 bg-white px-2 text-center text-normal font-semibold uppercase">
            Most Important
          </p>
          <ul
            ref={target1 as React.RefObject<HTMLUListElement>}
            className="flex flex-1"
          >
            {mostLikedFeatures.map((item) => (
              <li
                key={item}
                className={clsx(
                  "mb-2 flex w-full flex-col rounded-lg bg-bgInverseSecondary p-2 text-center text-white sm:w-40",
                  isMobile ? "text-normal" : "",
                )}
                style={{
                  backgroundColor: uiConfig?.colors
                    ? uiConfig.colors.primary
                    : "",
                }}
                draggable
                onDragStart={() => handleDragStart(item, "mostLiked")}
                onTouchStart={() => handleTouchStart(item, "mostLiked")}
              >
                {item}
              </li>
            ))}
          </ul>
        </div>
        <div
          ref={target2 as React.RefObject<HTMLDivElement>}
          className={clsx(
            "relative mt-4 flex flex-col items-center space-y-3 rounded-lg border border-borderSecondary sm:w-64",
            isMobile && isPortrait ? "min-h-10 p-0" : "min-h-48",
          )}
        >
          <p className="absolute -top-3 bg-white px-2 text-center text-normal font-semibold uppercase">
            Least Important
          </p>
          <ul
            ref={target2 as React.RefObject<HTMLUListElement>}
            className="flex flex-1"
          >
            {leastLikedFeatures.map((item) => (
              <li
                key={item}
                className={clsx(
                  "mb-2 flex w-full flex-col rounded-lg bg-bgInversePrimary p-2 text-center text-white sm:w-40",
                  isMobile ? "text-normal" : "",
                )}
                style={{
                  backgroundColor: uiConfig?.colors
                    ? uiConfig.colors.primary
                    : "",
                }}
                draggable
                onDragStart={() => handleDragStart(item, "leastLiked")}
                onTouchStart={() => handleTouchStart(item, "leastLiked")}
              >
                {item}
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
}
sashamilenkovic commented 1 month ago

@hinn254 Overriding handleTouchstart is probably the issue. check out the new event listeners on the docs with the new release of v0.2.0. Closing this for now but feel free to reopen if you're still having issues.