Add drag and drop
This commit is contained in:
Generated
+122
@@ -153,6 +153,37 @@
|
||||
"integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz",
|
||||
"integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/runtime-corejs2": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.3.4.tgz",
|
||||
"integrity": "sha512-QwPuQE65kNxjsNKk34Rfgen2R5fk0J2So99SD45uXBp34QOfyz11SqVgJ4xvyCpnCIieSQ0X0hSSc9z/ymlJJw==",
|
||||
"requires": {
|
||||
"core-js": "^2.5.7",
|
||||
"regenerator-runtime": "^0.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
|
||||
@@ -4212,6 +4243,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-box-model": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxbuLFeAPEDb0wPbGfT7783Vb00MVAkvOlMKwr0kA2PD5EGxk6P3MAhedvVuyVJCWb54bb+6HQ7pdPYENf8AZw==",
|
||||
"requires": {
|
||||
"tiny-invariant": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
@@ -9999,6 +10038,11 @@
|
||||
"mimic-fn": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.0.tgz",
|
||||
"integrity": "sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw=="
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||
@@ -13276,6 +13320,11 @@
|
||||
"integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
|
||||
"dev": true
|
||||
},
|
||||
"raf-schd": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.0.tgz",
|
||||
"integrity": "sha512-m7zq0JkIrECzw9mO5Zcq6jN4KayE34yoIS9hJoiZNXyOAT06PPA8PrR+WtJIeFW09YjUfNkMMN9lrmAt6BURCA=="
|
||||
},
|
||||
"ramda": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz",
|
||||
@@ -13404,6 +13453,21 @@
|
||||
"integrity": "sha1-wStu/cIkfBDae4dw0YUICnsEcVY=",
|
||||
"dev": true
|
||||
},
|
||||
"react-beautiful-dnd": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-10.1.0.tgz",
|
||||
"integrity": "sha512-oij2ZLIQ6SFmqy/MiXbuO8HUFHQ39gaPsTYj0Ewk0XwbLM32L+diVP0NXbEK7nhYv5lF2jviXGnda+BYMi6+nA==",
|
||||
"requires": {
|
||||
"@babel/runtime-corejs2": "^7.3.4",
|
||||
"css-box-model": "^1.1.1",
|
||||
"memoize-one": "^5.0.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"raf-schd": "^4.0.0",
|
||||
"react-redux": "^5.0.7",
|
||||
"redux": "^4.0.1",
|
||||
"tiny-invariant": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.1.tgz",
|
||||
@@ -13441,6 +13505,11 @@
|
||||
"shallowequal": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.4",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz",
|
||||
"integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA=="
|
||||
},
|
||||
"react-jsonschema-form": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.2.0.tgz",
|
||||
@@ -13481,6 +13550,30 @@
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz",
|
||||
"integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"invariant": "^2.2.4",
|
||||
"loose-envify": "^1.1.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"react-is": "^16.6.0",
|
||||
"react-lifecycles-compat": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
|
||||
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
|
||||
@@ -13730,6 +13823,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"redux": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
|
||||
"integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"symbol-observable": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"referrer-policy": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz",
|
||||
@@ -16735,6 +16852,11 @@
|
||||
"setimmediate": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"tiny-invariant": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.3.tgz",
|
||||
"integrity": "sha512-ytQx8T4DL8PjlX53yYzcIC0WhIZbpR0p1qcYjw2pHu3w6UtgWwFJQ/02cnhOnBBhlFx/edUIfcagCaQSe3KMWg=="
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
"mobx-react": "^5.2.3",
|
||||
"normalize.css": "^8.0.0",
|
||||
"query-string": "^6.2.0",
|
||||
"react-beautiful-dnd": "^10.1.0",
|
||||
"react-helmet": "^5.2.0",
|
||||
"react-jsonschema-form": "^1.2.0",
|
||||
"react-router": "^4.3.1",
|
||||
|
||||
@@ -3,6 +3,8 @@ import * as React from "react";
|
||||
export interface DatetimeWidgetProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onFocus: () => void;
|
||||
onBlur: () => void;
|
||||
required: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -10,7 +12,7 @@ export interface DatetimeWidgetState {}
|
||||
|
||||
class DatetimeWidget extends React.Component<DatetimeWidgetProps, DatetimeWidgetState> {
|
||||
render() {
|
||||
const { value, onChange, required, disabled } = this.props;
|
||||
const { value, onChange, onFocus, onBlur, required, disabled } = this.props;
|
||||
|
||||
let date, time;
|
||||
if (value && value.length !== 0) {
|
||||
@@ -19,20 +21,25 @@ class DatetimeWidget extends React.Component<DatetimeWidgetProps, DatetimeWidget
|
||||
time = rest.slice(0, 5);
|
||||
}
|
||||
|
||||
const commonProps = {
|
||||
onFocus,
|
||||
onBlur,
|
||||
required,
|
||||
disabled,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="datetime-widget">
|
||||
<input
|
||||
type="date"
|
||||
onChange={(event) => onChange(`${event.target.value}T${time}`)}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
value={date} />
|
||||
value={date}
|
||||
{...commonProps} />
|
||||
<input
|
||||
type="time"
|
||||
onChange={(event) => onChange(`${date}T${event.target.value}:00`)}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
value={time} />
|
||||
value={time}
|
||||
{...commonProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import * as React from "react";
|
||||
import { Fragment } from "react";
|
||||
import * as shortid from "shortid";
|
||||
import { Draggable } from "react-beautiful-dnd";
|
||||
import { Question, InputProps, optionTypes, SignupQuestionError } from "./index";
|
||||
import OptionsWidget from "./OptionsWidget";
|
||||
import TypeWidget from "./TypeWidget";
|
||||
|
||||
export interface QuestionListProps {
|
||||
questions: Question[];
|
||||
innerRef: any;
|
||||
placeholder: any;
|
||||
onChange: (value: Question[]) => void;
|
||||
}
|
||||
export interface QuestionListState { }
|
||||
|
||||
class QuestionList extends React.Component<QuestionListProps, QuestionListState> {
|
||||
renderTextWidget = ({ questions, value, index }: InputProps) => (
|
||||
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
|
||||
)
|
||||
|
||||
handleNameInputChange = (questions: Question[], index: number) => (event) => {
|
||||
const { onChange } = this.props;
|
||||
const val = event.target.value;
|
||||
questions[index].name = val;
|
||||
onChange(questions);
|
||||
}
|
||||
|
||||
renderQuestions() {
|
||||
const { questions, onChange, placeholder } = this.props;
|
||||
return questions.map((q, index) => {
|
||||
const nameWidgetProps = { value: q.name, type: "text", questions, index };
|
||||
const nameWidget = this.renderTextWidget(nameWidgetProps);
|
||||
|
||||
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) => (
|
||||
<div
|
||||
className="signup-questions-widget-row"
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
{nameWidget}
|
||||
{typeSelectWidget}
|
||||
{optionsWidget}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { placeholder, innerRef } = this.props;
|
||||
|
||||
return (
|
||||
<div ref={innerRef}>
|
||||
{ this.renderQuestions() }
|
||||
{ placeholder }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default QuestionList;
|
||||
@@ -11,5 +11,7 @@
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
padding: 0.5rem;
|
||||
background-color: $green;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import * as React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import OptionsWidget from "./OptionsWidget";
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
import { Question, InputProps, optionTypes } from ".";
|
||||
// @ts-ignore
|
||||
import * as AddIcon from "../../assets/img/add-icon.png";
|
||||
import "./SignupQuestionsWidget.scss";
|
||||
import TypeWidget from "./TypeWidget";
|
||||
import QuestionList from "./QuestionList";
|
||||
|
||||
export interface SignupQuestionsWidgetProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onFocus: () => void;
|
||||
required: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
@@ -34,42 +37,39 @@ class SignupQuestionsWidget extends React.Component<SignupQuestionsWidgetProps,
|
||||
this.onValueChange(newQuestions);
|
||||
}
|
||||
|
||||
handleNameInputChange = (questions: Question[], index: number) => (event) => {
|
||||
const val = event.target.value;
|
||||
questions[index].name = val;
|
||||
handleDragEnd = (questions: Question[]) => (result) => {
|
||||
const srcIndex = result.source.index;
|
||||
const dstIndex = result.destination.index;
|
||||
const srcCopy = { ...questions[srcIndex] };
|
||||
const dstCopy = { ...questions[dstIndex] };
|
||||
questions[dstIndex] = srcCopy;
|
||||
questions[srcIndex] = dstCopy;
|
||||
|
||||
this.onValueChange(questions);
|
||||
}
|
||||
|
||||
renderTextWidget = ({ questions, value, index }: InputProps) => (
|
||||
<input type="text" value={value} onChange={this.handleNameInputChange(questions, index)} />
|
||||
)
|
||||
|
||||
renderQuestions = (questions: Question[]) => {
|
||||
return questions.map((q, index) => {
|
||||
const nameWidgetProps = { value: q.name, type: "text", questions, index };
|
||||
const nameWidget = this.renderTextWidget(nameWidgetProps);
|
||||
|
||||
const dataProps = { value: q.options, type: q.type, questions, index };
|
||||
const optionsWidget = <OptionsWidget inputProps={dataProps} onChange={this.onValueChange} />;
|
||||
const typeSelectWidget = <TypeWidget inputProps={dataProps} onChange={this.onValueChange} />;
|
||||
return (
|
||||
<div key={q.id} className="signup-questions-widget-row">
|
||||
{ nameWidget }
|
||||
{ typeSelectWidget }
|
||||
{ optionsWidget }
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value } = this.props;
|
||||
const { value, onFocus } = this.props;
|
||||
|
||||
const questions = JSON.parse(value) as Question[];
|
||||
|
||||
return (
|
||||
<div className="signup-questions-widget">
|
||||
{this.renderQuestions(questions)}
|
||||
<DragDropContext
|
||||
onDragEnd={this.handleDragEnd(questions)}
|
||||
onDragStart={onFocus}
|
||||
>
|
||||
<Droppable droppableId="questions">
|
||||
{(provided) => (
|
||||
<QuestionList
|
||||
{...provided.droppableProps}
|
||||
innerRef={provided.innerRef}
|
||||
questions={questions}
|
||||
onChange={this.onValueChange}
|
||||
placeholder={provided.placeholder} />
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
<button type="button" className="add-link" onClick={this.handleNewRowClick(questions)}>
|
||||
<img src={AddIcon} /> New Question
|
||||
</button>
|
||||
|
||||
@@ -113,6 +113,11 @@ class EventCreatePage extends React.Component<EventCreatePageProps, EventCreateP
|
||||
onChange = (data) => {
|
||||
this.setState({
|
||||
formData: data.formData,
|
||||
});
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
this.setState({
|
||||
statusMessage: null,
|
||||
});
|
||||
}
|
||||
@@ -220,7 +225,8 @@ class EventCreatePage extends React.Component<EventCreatePageProps, EventCreateP
|
||||
widgets={widgets}
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
onError={this.onError} />
|
||||
onError={this.onError}
|
||||
onFocus={this.onFocus} />
|
||||
{ error && <div className="error">{error}</div> }
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -112,6 +112,11 @@ class FeedCreatePage extends React.Component<FeedCreatePageProps, FeedCreatePage
|
||||
onChange = (data) => {
|
||||
this.setState({
|
||||
formData: data.formData,
|
||||
});
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
this.setState({
|
||||
statusMessage: null,
|
||||
});
|
||||
}
|
||||
@@ -218,7 +223,8 @@ class FeedCreatePage extends React.Component<FeedCreatePageProps, FeedCreatePage
|
||||
widgets={widgets}
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
onError={this.onError} />
|
||||
onError={this.onError}
|
||||
onFocus={this.onFocus} />
|
||||
{ error && <div className="error">{error}</div> }
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import * as React from "react";
|
||||
import Helmet from "react-helmet";
|
||||
import "./SignupCreatePage.scss";
|
||||
import { isAuthenticated } from "../../auth";
|
||||
import Form from "react-jsonschema-form";
|
||||
import { Tag, getTags } from "../../models/Tag";
|
||||
import { createForm, getForm, updateForm } from "../../models/SignupForm";
|
||||
import DatetimeWidget from "../../components/DatetimeWidget";
|
||||
import SignupQuestionsWidget from "../../components/SignupQuestionsWidget";
|
||||
@@ -91,6 +89,11 @@ class SignupCreatePage extends React.Component<SignupCreatePageProps, SignupCrea
|
||||
onChange = (data) => {
|
||||
this.setState({
|
||||
formData: data.formData,
|
||||
});
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
this.setState({
|
||||
statusMessage: null,
|
||||
});
|
||||
}
|
||||
@@ -180,7 +183,8 @@ class SignupCreatePage extends React.Component<SignupCreatePageProps, SignupCrea
|
||||
widgets={widgets}
|
||||
onChange={this.onChange}
|
||||
onSubmit={this.onSubmit}
|
||||
onError={this.onError} />
|
||||
onError={this.onError}
|
||||
onFocus={this.onFocus} />
|
||||
{ error && <div className="error">{error}</div> }
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user