diff options
| author | 2021-01-04 03:57:10 +0300 | |
|---|---|---|
| committer | 2021-01-06 09:35:58 +0300 | |
| commit | b9dd32cdd57e7d39ee94623b9c74747f532943f1 (patch) | |
| tree | 9bd083b45a69cb87f851d0b41d56c8d9e761b6cb /src | |
| 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]>
Diffstat (limited to 'src')
| -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; +} | 
