aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Hassan Abouelela <[email protected]>2021-06-19 20:42:41 +0300
committerGravatar GitHub <[email protected]>2021-06-19 20:42:41 +0300
commita764beda06a2001d35f0a0f91eec615eb92e322f (patch)
tree9aaf0670bef90be84b961c70ddad518fdea78b95 /src
parentAdds Support For Linebreaks In Content (diff)
parentUpdate 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.tsx40
-rw-r--r--src/components/InputTypes/index.tsx8
-rw-r--r--src/components/Question.tsx22
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;
}