Merge branch 'update-dnd-package' into 'master'
Replace react-beautiful-dnd with react-dnd See merge request sahkoinsinoorikilta/vtmk/web2.0-frontend!108
This commit is contained in:
@@ -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
|
||||
drop(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 (
|
||||
<div ref={ref}>{children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Draggable;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import Draggable from "@components/Draggable";
|
||||
import colors from "@theme/colors";
|
||||
import { SignupFormQuestion } from "@models/Signup";
|
||||
import { Lang } from "../../../i18n";
|
||||
@@ -18,14 +18,24 @@ const WidgetRow = styled.div`
|
||||
|
||||
interface QuestionListProps {
|
||||
questions: SignupFormQuestion[];
|
||||
innerRef: React.Ref<HTMLDivElement>;
|
||||
placeholder: ReactNode;
|
||||
onChange: (value: SignupFormQuestion[]) => void;
|
||||
}
|
||||
|
||||
class QuestionList extends React.Component<QuestionListProps> {
|
||||
handleNameInputChange = (questions: SignupFormQuestion[], index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const QuestionList: React.FC<QuestionListProps> = ({ questions, onChange }): JSX.Element => {
|
||||
const handleDrag = (srcIndex, dstIndex) => {
|
||||
const srcCopy = { ...questions[srcIndex] };
|
||||
questions.splice(srcIndex, 1);
|
||||
questions.splice(dstIndex, 0, srcCopy);
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
const handleElementRemove = (index: number) => (): void => {
|
||||
const newQuestions = [...questions];
|
||||
newQuestions.splice(index, 1);
|
||||
onChange(newQuestions);
|
||||
};
|
||||
|
||||
const handleNameInputChange = (index: number, lang: Lang): React.ChangeEventHandler<HTMLInputElement> => (event) => {
|
||||
const val = event.target.value;
|
||||
if (lang === "fi") {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
@@ -38,54 +48,39 @@ class QuestionList extends React.Component<QuestionListProps> {
|
||||
onChange(questions);
|
||||
};
|
||||
|
||||
handleElementRemove = (questions: SignupFormQuestion[], index: number) => (): void => {
|
||||
const { onChange } = this.props;
|
||||
const newQuestions = [...questions];
|
||||
newQuestions.splice(index, 1);
|
||||
onChange(newQuestions);
|
||||
};
|
||||
|
||||
renderQuestions(): JSX.Element[] {
|
||||
const { questions, onChange } = this.props;
|
||||
return questions.map((q, index) => {
|
||||
const dataProps = {
|
||||
value: q.options, type: q.type, questions, index,
|
||||
};
|
||||
const optionsWidget = <OptionsWidget inputProps={dataProps} onChange={onChange} />;
|
||||
const typeSelectWidget = <TypeWidget inputProps={dataProps} onChange={onChange} />;
|
||||
return (
|
||||
<Draggable draggableId={q.id} key={q.id} index={index}>
|
||||
{(provided) => (
|
||||
<WidgetRow
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
return (
|
||||
<div data-e2e="admin-signup-question">
|
||||
{questions.map((q, index) => {
|
||||
const inputProps = {
|
||||
value: q.options,
|
||||
type: q.type,
|
||||
questions,
|
||||
index,
|
||||
};
|
||||
const optionsWidget = <OptionsWidget inputProps={inputProps} onChange={onChange} />;
|
||||
const typeSelectWidget = <TypeWidget inputProps={inputProps} onChange={onChange} />;
|
||||
return (
|
||||
<Draggable
|
||||
key={q.id}
|
||||
id={q.id}
|
||||
index={index}
|
||||
handleDrag={handleDrag}
|
||||
>
|
||||
<WidgetRow>
|
||||
<QuestionElement
|
||||
onClick={this.handleElementRemove(questions, index)}
|
||||
onClick={handleElementRemove(index)}
|
||||
>
|
||||
<input type="text" value={q.title_fi} onChange={this.handleNameInputChange(questions, index, "fi")} />
|
||||
<input type="text" value={q.title_en} onChange={this.handleNameInputChange(questions, index, "en")} />
|
||||
<input type="text" value={q.title_fi} onChange={handleNameInputChange(index, "fi")} />
|
||||
<input type="text" value={q.title_en} onChange={handleNameInputChange(index, "en")} />
|
||||
{typeSelectWidget}
|
||||
{optionsWidget}
|
||||
</QuestionElement>
|
||||
</WidgetRow>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { placeholder, innerRef } = this.props;
|
||||
|
||||
return (
|
||||
<div ref={innerRef} data-e2e="admin-signup-question">
|
||||
{this.renderQuestions()}
|
||||
{placeholder}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuestionList;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
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 +33,9 @@ const AddQuestionButton = styled.button`
|
||||
interface SignupQuestionsWidgetProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onFocus: () => void;
|
||||
required: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, onFocus, onChange }) => {
|
||||
const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ value, onChange }) => {
|
||||
const onValueChange = (questions: SignupFormQuestion[]) => {
|
||||
const newValue = JSON.stringify(questions);
|
||||
onChange(newValue);
|
||||
@@ -62,35 +58,14 @@ const SignupQuestionsWidget: React.FC<SignupQuestionsWidgetProps> = ({ 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 (
|
||||
<Widget>
|
||||
<DragDropContext
|
||||
onDragEnd={handleDragEnd(questions)}
|
||||
onDragStart={onFocus}
|
||||
>
|
||||
<Droppable droppableId="questions">
|
||||
{(provided) => (
|
||||
<QuestionList
|
||||
{...provided.droppableProps}
|
||||
innerRef={provided.innerRef}
|
||||
questions={questions}
|
||||
onChange={onValueChange}
|
||||
placeholder={provided.placeholder}
|
||||
/>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
<QuestionList
|
||||
questions={questions}
|
||||
onChange={onValueChange}
|
||||
/>
|
||||
<AddQuestionButton type="button" onClick={handleNewRowClick(questions)} data-e2e="admin-signup-new-question">
|
||||
<AddIcon />
|
||||
New Question
|
||||
|
||||
@@ -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;
|
||||
+34
-22
@@ -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 => (
|
||||
<>
|
||||
<Head>
|
||||
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Aalto-yliopiston Sähköinsinöörikilta ry</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Aalto-yliopiston Sähköinsinöörikilta ry on Otaniemessä vaikuttava opiskelijajärjestö, joka on perustettu vuonna 1921. Kilta järjestää kaikenlaista toimintaa liittyen opintoihin ja vapaa-ajan viettoon."
|
||||
/>
|
||||
<meta name="keywords" content="SIK AYY" />
|
||||
</Head>
|
||||
<GlobalCommonStyles />
|
||||
<LocaleStore>
|
||||
<AppContainer>
|
||||
<Component {...pageProps} />
|
||||
</AppContainer>
|
||||
</LocaleStore>
|
||||
<ToastContainer position="bottom-right" />
|
||||
</>
|
||||
);
|
||||
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 (
|
||||
<>
|
||||
<Head>
|
||||
<meta httpEquiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Aalto-yliopiston Sähköinsinöörikilta ry</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Aalto-yliopiston Sähköinsinöörikilta ry on Otaniemessä vaikuttava opiskelijajärjestö, joka on perustettu vuonna 1921. Kilta järjestää kaikenlaista toimintaa liittyen opintoihin ja vapaa-ajan viettoon."
|
||||
/>
|
||||
<meta name="keywords" content="SIK AYY" />
|
||||
</Head>
|
||||
<GlobalCommonStyles />
|
||||
<LocaleStore>
|
||||
<AppContainer>
|
||||
<DndProvider backend={backendForDND}>
|
||||
<Component {...pageProps} />
|
||||
</DndProvider>
|
||||
</AppContainer>
|
||||
</LocaleStore>
|
||||
<ToastContainer position="bottom-right" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Web20App;
|
||||
|
||||
Reference in New Issue
Block a user