diff options
author | 2021-01-04 03:57:10 +0300 | |
---|---|---|
committer | 2021-01-06 09:35:58 +0300 | |
commit | b9dd32cdd57e7d39ee94623b9c74747f532943f1 (patch) | |
tree | 9bd083b45a69cb87f851d0b41d56c8d9e761b6cb | |
parent | Implements Scroll Button (diff) |
Implements Input Types
Adds functionality and JSX for all input types. Adds a dispatcher that
can pick and return the needed element.
Signed-off-by: Hassan Abouelela <[email protected]>
-rw-r--r-- | src/components/InputTypes/Checkbox.tsx | 22 | ||||
-rw-r--r-- | src/components/InputTypes/Code.tsx | 11 | ||||
-rw-r--r-- | src/components/InputTypes/Radio.tsx | 18 | ||||
-rw-r--r-- | src/components/InputTypes/Range.tsx | 52 | ||||
-rw-r--r-- | src/components/InputTypes/Select.tsx | 60 | ||||
-rw-r--r-- | src/components/InputTypes/ShortText.tsx | 11 | ||||
-rw-r--r-- | src/components/InputTypes/TextArea.tsx | 11 | ||||
-rw-r--r-- | src/components/InputTypes/index.tsx | 71 |
8 files changed, 256 insertions, 0 deletions
diff --git a/src/components/InputTypes/Checkbox.tsx b/src/components/InputTypes/Checkbox.tsx new file mode 100644 index 0000000..ed02b83 --- /dev/null +++ b/src/components/InputTypes/Checkbox.tsx @@ -0,0 +1,22 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React, { ChangeEvent } from "react"; + +interface CheckboxProps { + index: number, + option: string, + handler: (event: ChangeEvent<HTMLInputElement>) => void +} + +export default function Checkbox(props: CheckboxProps): JSX.Element { + return ( + <label> + <label className="unselected_checkbox_label checkbox_label unselectable"> + <input type="checkbox" value={props.option} + name={`${("000" + props.index).slice(-4)}. ${props.option}`} onChange={props.handler}/> + <span className="checkmark_span"/> + </label> + {props.option}<br/> + </label> + ); +} diff --git a/src/components/InputTypes/Code.tsx b/src/components/InputTypes/Code.tsx new file mode 100644 index 0000000..51ca98d --- /dev/null +++ b/src/components/InputTypes/Code.tsx @@ -0,0 +1,11 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React, { ChangeEvent } from "react"; + +interface CodeProps { + handler: (event: ChangeEvent<HTMLInputElement>) => void +} + +export default function Code(props: CodeProps): JSX.Element { + return <input type="text" className="text" onChange={props.handler}/>; +} diff --git a/src/components/InputTypes/Radio.tsx b/src/components/InputTypes/Radio.tsx new file mode 100644 index 0000000..81f8375 --- /dev/null +++ b/src/components/InputTypes/Radio.tsx @@ -0,0 +1,18 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React, { ChangeEvent } from "react"; + +interface RadioProps { + option: string, + question_id: string, + handler: (event: ChangeEvent<HTMLInputElement>) => void +} + +export default function Radio(props: RadioProps): JSX.Element { + return ( + <label> + <input type="radio" name={props.question_id} className="radio" onChange={props.handler}/> + {props.option}<br/> + </label> + ); +} diff --git a/src/components/InputTypes/Range.tsx b/src/components/InputTypes/Range.tsx new file mode 100644 index 0000000..a0260ad --- /dev/null +++ b/src/components/InputTypes/Range.tsx @@ -0,0 +1,52 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React from "react"; + +interface RangeProps { + question_id: string, + options: Array<string>, + state_dict: Map<string, string | boolean | null> +} + +let last_selection: Element; + +interface handler_props { + state_dict: Map<string, string | boolean | null>, + ref: React.RefObject<HTMLLabelElement> +} + +function handler(this: handler_props): void { + if (last_selection) { + last_selection.classList.toggle("selected"); + } + + const dot: Element = this.ref.current!.lastElementChild!; // eslint-disable-line + dot.classList.toggle("selected"); + + last_selection = dot; + + const value: string = this.ref.current!.textContent!; //eslint-disable-line + this.state_dict.set("value", value); +} + +export default function Range(props: RangeProps): JSX.Element { + const range = props.options.map((option, index) => { + const ref: React.RefObject<HTMLLabelElement> = React.createRef(); + return ( + <label key={index} ref={ref} onClick={handler.bind({state_dict: props.state_dict, ref: ref})}> + <span>{option}</span> + <div className="range_dot"/> + </label> + ); + }); + + return ( + <div className="range"> + { range } + + <div className="range_slider_container"> + <div className="range_slider"/> + </div> + </div> + ); +} diff --git a/src/components/InputTypes/Select.tsx b/src/components/InputTypes/Select.tsx new file mode 100644 index 0000000..355f5d0 --- /dev/null +++ b/src/components/InputTypes/Select.tsx @@ -0,0 +1,60 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React from "react"; + +interface SelectProps { + options: Array<string>, + state_dict: Map<string, string | boolean | null> +} + +interface HandlerProps { + props: SelectProps, + ref: React.RefObject<HTMLDivElement> +} + +class Select extends React.Component<SelectProps> { + constructor(props: SelectProps) { + super(props); + } + + click_handler(this: HandlerProps, event: React.MouseEvent<HTMLDivElement, MouseEvent>): void { + if (!this.ref.current) { + return; + } + + this.ref.current.classList.toggle("active"); + + const target: Element = (event.target as Element); + if (target.id === "option") { + const selected_option: Element = this.ref.current.getElementsByClassName("selected_option")[0]; + const new_option_text: string = target.textContent ?? "..."; + + if (selected_option.textContent === "..." && target.parentElement) { + target.parentElement.remove(); + } else { + target.textContent = selected_option.textContent; + } + + selected_option.textContent = new_option_text; + this.props.state_dict.set("value", selected_option.textContent); + } + } + + render(): JSX.Element { + const container_ref: React.RefObject<HTMLDivElement> = React.createRef(); + + return ( + <div className="select_container" ref={container_ref} onClick={this.click_handler.bind({ref: container_ref, props: this.props})}> + <span className="select_arrow"/> + <span className="selected_option">...</span> + <div className="select_options_container"> + <div className="select_options"> + { this.props.options.map((option, index) => <div key={index}><hr/><div id="option">{option}</div></div>) } + </div> + </div> + </div> + ); + } +} + +export default Select;
\ No newline at end of file diff --git a/src/components/InputTypes/ShortText.tsx b/src/components/InputTypes/ShortText.tsx new file mode 100644 index 0000000..182571a --- /dev/null +++ b/src/components/InputTypes/ShortText.tsx @@ -0,0 +1,11 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React, { ChangeEvent } from "react"; + +interface ShortTextProps { + handler: (event: ChangeEvent<HTMLInputElement>) => void +} + +export default function ShortText(props: ShortTextProps): JSX.Element { + return <input type="text" className="short_text" placeholder="Enter Text..." onChange={props.handler}/>; +} diff --git a/src/components/InputTypes/TextArea.tsx b/src/components/InputTypes/TextArea.tsx new file mode 100644 index 0000000..550adea --- /dev/null +++ b/src/components/InputTypes/TextArea.tsx @@ -0,0 +1,11 @@ +/** @jsx jsx */ +import { jsx } from "@emotion/react"; +import React, { ChangeEvent } from "react"; + +interface TextAreaProps { + handler: (event: ChangeEvent<HTMLTextAreaElement>) => void +} + +export default function TextArea(props: TextAreaProps): JSX.Element { + return <textarea className="text_area" placeholder="Enter Text..." onChange={props.handler}/>; +} diff --git a/src/components/InputTypes/index.tsx b/src/components/InputTypes/index.tsx new file mode 100644 index 0000000..b1d8afe --- /dev/null +++ b/src/components/InputTypes/index.tsx @@ -0,0 +1,71 @@ +import Checkbox from "./Checkbox"; +import Code from "./Code"; +import Radio from "./Radio"; +import Range from "./Range"; +import Select from "./Select"; +import ShortText from "./ShortText"; +import TextArea from "./TextArea"; + +import React, { ChangeEvent } from "react"; + +import { QuestionType } from "../../api/question"; +import { QuestionProp } from "../Question"; + +const _require_options: Array<QuestionType> = [ + QuestionType.Radio, + QuestionType.Checkbox, + QuestionType.Select, + QuestionType.Range +]; + +export default function create_input({ question, public_state }: QuestionProp, handler: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void): JSX.Element | JSX.Element[] { + let result: JSX.Element | JSX.Element[]; + + // eslint-disable-next-line + // @ts-ignore + let options: string[] = question.data["options"]; + + // Catch input types that require options but don't have any + if ((options === undefined || typeof options !== "object") && _require_options.includes(question.type)) { + // TODO: Implement some sort of warning here + options = []; + } + + /* eslint-disable react/react-in-jsx-scope */ + switch (question.type) { + case QuestionType.TextArea: + result = <TextArea handler={handler}/>; + break; + + case QuestionType.Checkbox: + result = options.map((option, index) => <Checkbox index={index} option={option} handler={handler} key={index}/>); + break; + + case QuestionType.Radio: + result = options.map((option, index) => <Radio option={option} question_id={question.id} handler={handler} key={index}/>); + break; + + case QuestionType.Select: + result = <Select options={options} state_dict={public_state}/>; + break; + + case QuestionType.ShortText: + result = <ShortText handler={handler}/>; + break; + + case QuestionType.Range: + result = <Range question_id={question.id} options={options} state_dict={public_state}/>; + break; + + case QuestionType.Code: + // TODO: Implement + result = <Code handler={handler}/>; + break; + + default: + result = <TextArea handler={handler}/>; + } + /* eslint-enable react/react-in-jsx-scope */ + + return result; +} |