diff --git a/src/components/Draggable.tsx b/src/components/Draggable.tsx new file mode 100644 index 0000000..d4b6f53 --- /dev/null +++ b/src/components/Draggable.tsx @@ -0,0 +1,61 @@ +import React, { useRef } from "react"; +import { useDrag, useDrop } from "react-dnd"; + +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: { index: number }) { // 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}
+ ); +}; + +export default Draggable; diff --git a/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx b/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx index 1c19fc6..c61989d 100644 --- a/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx +++ b/src/components/Widgets/SignupQuestionsWidget/QuestionList.tsx @@ -1,6 +1,6 @@ -import React, { useRef } from "react"; -import { useDrag, useDrop } from "react-dnd"; +import React from "react"; import styled from "styled-components"; +import Draggable from "@components/Draggable"; import colors from "@theme/colors"; import { SignupFormQuestion } from "@models/Signup"; import { Lang } from "../../../i18n"; @@ -8,63 +8,6 @@ import OptionsWidget from "./OptionsWidget"; import TypeWidget from "./TypeWidget"; import QuestionElement from "./Question"; -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; diff --git a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx index 42b4f79..995ee3e 100644 --- a/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx +++ b/src/components/Widgets/SignupQuestionsWidget/SignupQuestionsWidget.tsx @@ -1,7 +1,4 @@ import React from "react"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import { TouchBackend } from "react-dnd-touch-backend"; import styled from "styled-components"; import shortid from "shortid"; import colors from "@theme/colors"; @@ -63,25 +60,12 @@ const SignupQuestionsWidget: React.FC = ({ value, on const questions: SignupFormQuestion[] = JSON.parse(value); - // simple way to check whether the device support touch (it doesn't check all fallback, it supports only modern browsers) - const isTouchDevice = () => { - if ("ontouchstart" in window) { - return true; - } - return false; - }; - - // Assigning backend based on touch support on the device - const backendForDND = isTouchDevice() ? TouchBackend : HTML5Backend; - return ( - - - + New Question diff --git a/src/hooks/useIsTouchDevice.ts b/src/hooks/useIsTouchDevice.ts new file mode 100644 index 0000000..5879303 --- /dev/null +++ b/src/hooks/useIsTouchDevice.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from "react"; + +const useIsTouchDevice = () => { + const [isTouchDevice, setTouchDevice] = useState(false); + useEffect(() => { + // simple way to check whether the device support touch (it doesn't check all fallback, it supports only modern browsers) + if (window !== undefined && "ontouchstart" in window) { + setTouchDevice(true); + } + }, []); + return isTouchDevice; +}; + +export default useIsTouchDevice; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 2638a73..2ccafd8 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,7 @@ import React from "react"; +import { DndProvider } from "react-dnd"; +import { HTML5Backend } from "react-dnd-html5-backend"; +import { TouchBackend } from "react-dnd-touch-backend"; import Head from "next/head"; import { AppProps } from "next/app"; import styled, { createGlobalStyle } from "styled-components"; @@ -10,6 +13,7 @@ import "react-mde/lib/styles/css/react-mde-all.css"; import "react-toastify/dist/ReactToastify.css"; import "normalize.css"; +import useIsTouchDevice from "@hooks/useIsTouchDevice"; import LocaleStore from "../i18n"; const fontFamily = "'Montserrat', sans-serif"; @@ -128,27 +132,35 @@ const AppContainer = styled.div` background-color: ${colors.white}; `; -const Web20App = ({ Component, pageProps }: AppProps): JSX.Element => ( - <> - - - - - Aalto-yliopiston Sähköinsinöörikilta ry - - - - - - - - - - - -); +const Web20App = ({ Component, pageProps }: AppProps): JSX.Element => { + const isTouchDevice = useIsTouchDevice(); + // Assigning backend based on touch support on the device + const backendForDND = isTouchDevice ? TouchBackend : HTML5Backend; + + return ( + <> + + + + + Aalto-yliopiston Sähköinsinöörikilta ry + + + + + + + + + + + + + + ); +}; export default Web20App;