diff --git a/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx b/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx index 706f3b2..8d2c086 100644 --- a/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx +++ b/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode, useRef } from "react"; +import { useDrag, useDrop } from "react-dnd"; import styled from "styled-components"; -import { Draggable } from "react-beautiful-dnd"; import colors from "@theme/colors"; import { SignupFormQuestion } from "@models/Signup"; import { Lang } from "../../../i18n"; @@ -8,7 +8,64 @@ import OptionsWidget from "./OptionsWidget"; import TypeWidget from "./TypeWidget"; import QuestionElement from "./Question"; -const WidgetRow = styled.div` +const type = "Draggable"; + +const Draggable = ({ + id, index, handleDrag, children, +}) => { + const ref = useRef(null); // Initialize the reference + + // useDrop hook is responsible for handling whether any item gets hovered or dropped on the element + const [, drop] = useDrop({ + // accept receives a definition of what must be the type of the dragged item to be droppable + accept: type, + // This method is called when we hover over an element while dragging + hover(item: any) { // item is the dragged element + if (!ref.current) { + return; + } + const dragIndex = item.index; + // current element where the dragged element is hovered on + const hoverIndex = index; + // If the dragged element is hovered in the same place, then do nothing + if (dragIndex === hoverIndex) { + return; + } + // If it is dragged around other elements, then move the image and set the state with position changes + handleDrag(dragIndex, hoverIndex); + /* + Update the index for dragged item directly to avoid flickering + when the image was half dragged into the next + */ + // eslint-disable-next-line no-param-reassign + item.index = hoverIndex; + }, + }); + + // useDrag will be responsible for making an element draggable. It also expose, isDragging method to add any styles while dragging + const [{ isDragging }, drag] = useDrag(() => ({ + // what type of item this to determine if a drop target accepts it + type, + // data of the item to be available to the drop methods + item: { id, index }, + // method to collect additional data for drop handling like whether is currently being dragged + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + })); + + /* + Initialize drag and drop into the element using its reference. + Here we initialize both drag and drop on the same element (i.e., Image component) + */ + drag(drop(ref)); + + return ( +
{children}
+ ); +}; + +const WidgetRow = styled(Draggable)` margin-bottom: 1rem; display: flex; flex-flow: column nowrap; @@ -18,8 +75,6 @@ const WidgetRow = styled.div` interface QuestionListProps { questions: SignupFormQuestion[]; - innerRef: React.Ref; - placeholder: ReactNode; onChange: (value: SignupFormQuestion[]) => void; } @@ -45,6 +100,14 @@ class QuestionList extends React.Component { onChange(newQuestions); }; + handleDrag = (questions: SignupFormQuestion[]) => (srcIndex, dstIndex) => { + const { onChange } = this.props; + const srcCopy = { ...questions[srcIndex] }; + questions.splice(srcIndex, 1); + questions.splice(dstIndex, 0, srcCopy); + onChange(questions); + }; + renderQuestions(): JSX.Element[] { const { questions, onChange } = this.props; return questions.map((q, index) => { @@ -54,35 +117,31 @@ class QuestionList extends React.Component { const optionsWidget = ; const typeSelectWidget = ; return ( - - {(provided) => ( - - - - - {typeSelectWidget} - {optionsWidget} - - - )} - + + + + + {typeSelectWidget} + {optionsWidget} + + + ); }); } render(): JSX.Element { - const { placeholder, innerRef } = this.props; - return ( -
+
{this.renderQuestions()} - {placeholder}
); } diff --git a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx index fc548cc..3619041 100644 --- a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx +++ b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx @@ -1,7 +1,8 @@ import React from "react"; +import { DndProvider } from "react-dnd"; +import { HTML5Backend } from "react-dnd-html5-backend"; import styled from "styled-components"; import shortid from "shortid"; -import { DragDropContext, Droppable } from "react-beautiful-dnd"; import colors from "@theme/colors"; import AddIcon from "@components/AddIcon"; import { SignupFormQuestion } from "@models/Signup"; @@ -34,12 +35,9 @@ const AddQuestionButton = styled.button` interface SignupQuestionsWidgetProps { value: string; onChange: (value: string) => void; - onFocus: () => void; - required: boolean; - disabled: boolean; } -const SignupQuestionsWidget: React.FC = ({ value, onFocus, onChange }) => { +const SignupQuestionsWidget: React.FC = ({ value, onChange }) => { const onValueChange = (questions: SignupFormQuestion[]) => { const newValue = JSON.stringify(questions); onChange(newValue); @@ -62,35 +60,16 @@ const SignupQuestionsWidget: React.FC = ({ value, on onValueChange(newQuestions); }; - const handleDragEnd = (questions: SignupFormQuestion[]) => (result) => { - const srcIndex = result.source.index; - const dstIndex = result.destination.index; - const srcCopy = { ...questions[srcIndex] }; - questions.splice(srcIndex, 1); - questions.splice(dstIndex, 0, srcCopy); - - onValueChange(questions); - }; const questions: SignupFormQuestion[] = JSON.parse(value); return ( - - - {(provided) => ( - - )} - - + + + New Question