diff options
author | 2021-06-19 20:42:41 +0300 | |
---|---|---|
committer | 2021-06-19 20:42:41 +0300 | |
commit | a764beda06a2001d35f0a0f91eec615eb92e322f (patch) | |
tree | 9aaf0670bef90be84b961c70ddad518fdea78b95 /src | |
parent | Adds Support For Linebreaks In Content (diff) | |
parent | Update HTML Element handler comment (diff) |
Merge pull request #224 from python-discord/codemirror
Implement code field using CodeMirror 6 beta
Diffstat (limited to 'src')
-rw-r--r-- | src/components/InputTypes/Code.tsx | 40 | ||||
-rw-r--r-- | src/components/InputTypes/index.tsx | 8 | ||||
-rw-r--r-- | src/components/Question.tsx | 22 |
3 files changed, 62 insertions, 8 deletions
diff --git a/src/components/InputTypes/Code.tsx b/src/components/InputTypes/Code.tsx index 51ca98d..506e181 100644 --- a/src/components/InputTypes/Code.tsx +++ b/src/components/InputTypes/Code.tsx @@ -1,11 +1,43 @@ /** @jsx jsx */ -import { jsx } from "@emotion/react"; -import React, { ChangeEvent } from "react"; +import { jsx, css } from "@emotion/react"; +import React, { useEffect } from "react"; + +import { basicSetup } from "@codemirror/basic-setup"; +import { python } from "@codemirror/lang-python"; +import { EditorState } from "@codemirror/state"; +import { oneDark } from "@codemirror/theme-one-dark"; +import { EditorView, ViewUpdate } from "@codemirror/view"; interface CodeProps { - handler: (event: ChangeEvent<HTMLInputElement>) => void + handler: (newContent: string) => void, + questionId: string, } +const styles = css` + border: 3px solid lightgray; + border-radius: 5px; + overflow:auto; + height: 20rem; +`; + export default function Code(props: CodeProps): JSX.Element { - return <input type="text" className="text" onChange={props.handler}/>; + const onUpdate = () => EditorView.updateListener.of((v: ViewUpdate) => { + props.handler(v.state.doc.toString()); + + }); + + useEffect(() => { + const el = document.getElementById(`${props.questionId}-code`); + const state = EditorState.create({ + extensions: [basicSetup, python(), onUpdate(), oneDark], + }); + const view = new EditorView({ + state, + parent: el as Element + }); + + return () => view.destroy(); + }, []); + + return <div id={`${props.questionId}-code`} css={styles} />; } diff --git a/src/components/InputTypes/index.tsx b/src/components/InputTypes/index.tsx index c6a83f1..3e13652 100644 --- a/src/components/InputTypes/index.tsx +++ b/src/components/InputTypes/index.tsx @@ -9,6 +9,7 @@ import React, { ChangeEvent } from "react"; import { QuestionType } from "../../api/question"; import { QuestionProp } from "../Question"; +import Code from "./Code"; const require_options: Array<QuestionType> = [ QuestionType.Radio, @@ -18,7 +19,7 @@ const require_options: Array<QuestionType> = [ ]; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export default function create_input({ question, public_state }: QuestionProp, handler: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void, onBlurHandler: () => void, focus_ref: React.RefObject<any>): JSX.Element | JSX.Element[] { +export default function create_input({ question, public_state }: QuestionProp, handler: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string) => void, onBlurHandler: () => void, focus_ref: React.RefObject<any>): JSX.Element | JSX.Element[] { let result: JSX.Element | JSX.Element[]; // eslint-disable-next-line @@ -37,7 +38,10 @@ export default function create_input({ question, public_state }: QuestionProp, h /* eslint-disable react/react-in-jsx-scope */ switch (question.type) { - case QuestionType.Code: // TODO: Implement + case QuestionType.Code: + result = <Code handler={handler} questionId={question.id}/>; + break; + case QuestionType.TextArea: result = <TextArea handler={handler} valid={valid} onBlurHandler={onBlurHandler} focus_ref={focus_ref}/>; break; diff --git a/src/components/Question.tsx b/src/components/Question.tsx index 2f5c496..2914ae6 100644 --- a/src/components/Question.tsx +++ b/src/components/Question.tsx @@ -28,6 +28,8 @@ class RenderedQuestion extends React.Component<QuestionProp> { super(props); if (props.question.type === QuestionType.TextArea) { this.handler = this.text_area_handler.bind(this); + } else if (props.question.type === QuestionType.Code) { + this.handler = this.code_field_handler.bind(this); } else { this.handler = this.normal_handler.bind(this); } @@ -46,8 +48,8 @@ class RenderedQuestion extends React.Component<QuestionProp> { this.props.public_state.set(target, value); } - // This is here to allow dynamic selection between the general handler, and the textarea handler. - handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void {} // eslint-disable-line + // This is here to allow dynamic selection between the general handler, textarea, and code field handlers. + handler(_: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | string): void {} // eslint-disable-line blurHandler(): void { if (this.props.question.required) { @@ -132,6 +134,21 @@ class RenderedQuestion extends React.Component<QuestionProp> { this.setPublicState("value", event.target.value); } + code_field_handler(newContent: string): void { + // If content stays same (what means that user have just zoomed in), then don't validate. + let validate = false; + if (newContent != this.props.public_state.get("value")) { + validate = true; + } + + this.setPublicState("value", newContent); + + // CodeMirror don't provide onBlur event, so we have to run validation here. + if (validate) { + this.blurHandler(); + } + } + validateField(): void { if (!this.props.question.required) { return; @@ -142,6 +159,7 @@ class RenderedQuestion extends React.Component<QuestionProp> { switch (this.props.question.type) { case QuestionType.TextArea: case QuestionType.ShortText: + case QuestionType.Code: if (this.props.public_state.get("value") === "") { invalid = true; } |