Skip to content

Commit

Permalink
Merge pull request #15 from malerba118/solution-walkthrough
Browse files Browse the repository at this point in the history
Solution Explanations
  • Loading branch information
kennethcassel authored Nov 18, 2021
2 parents e7c6334 + efaec29 commit 597807e
Show file tree
Hide file tree
Showing 23 changed files with 1,347 additions and 182 deletions.
91 changes: 91 additions & 0 deletions components/ComplexInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { FC, HTMLProps, useEffect, useState } from "react";

interface ComplexInputProps<T>
extends Omit<HTMLProps<HTMLInputElement>, "value" | "onChange"> {
stringify: (val: T) => string;
parse: (val: string) => T;
value: T;
onChange: (value: T) => void;
}

function ComplexInput<T>({
stringify,
parse,
value,
onChange,
...otherProps
}: ComplexInputProps<T>) {
const [draft, setDraft] = useState<string>(stringify(value));

useEffect(() => {
setDraft(stringify(value));
}, [value]);

const process = () => {
let shouldNotify = false;
let parsed;
try {
parsed = parse(draft);
shouldNotify = true;
} catch (err) {
setDraft(stringify(value));
}
if (shouldNotify) {
onChange(parsed);
}
};

return (
<input
{...otherProps}
onBlur={() => process()}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.currentTarget.blur();
}
otherProps.onKeyDown?.(e);
}}
value={draft}
onChange={(e) => {
setDraft(e.target.value);
}}
/>
);
}

const stringify = (value: number[]) => {
return JSON.stringify(value);
};

const parse = (value: string) => {
const parsed = JSON.parse(value);
if (!Array.isArray(parsed)) {
throw new Error("Should be array");
}
if (parsed.some((x) => typeof x !== "number")) {
throw new Error("Should contain only numbers");
}
return parsed;
};

interface NumberArrayInputProps
extends Omit<HTMLProps<HTMLInputElement>, "value" | "onChange"> {
value: number[];
onChange: (value: number[]) => void;
}

export const NumberArrayInput: FC<NumberArrayInputProps> = ({
value,
onChange,
...otherProps
}) => {
return (
<ComplexInput
{...otherProps}
stringify={stringify}
parse={parse}
value={value}
onChange={onChange}
/>
);
};
86 changes: 86 additions & 0 deletions components/ContentWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
useLayoutEffect,
useRef,
FC,
ReactNode,
useEffect,
Children,
} from "react";
import { useMonaco } from "@monaco-editor/react";
import { useEditor } from "./Editor";
import ReactDOM from "react-dom";
import useForceUpdate from "../hooks/useForceUpdate";

interface ContentWidgetProps {
children: any;
line: number;
widgetId: string;
}

/**
* This component pins its children to a specific line in the editor.
* It's good for adding annotations to the editor.
* @param param0
* @returns
*/
const ContentWidget: FC<ContentWidgetProps> = ({
widgetId,
line,
children,
}) => {
const monaco = useMonaco();
const editor = useEditor();
const ref = useRef(null);

useLayoutEffect(() => {
if (editor) {
// Add a content widget (scrolls inline with text)
var contentWidget = {
domNode: null,
allowEditorOverflow: true,
getId: function () {
return widgetId;
},
getDomNode: function () {
if (!ref.current) {
ref.current = document.createElement("div");
ref.current.style.position = "relative";
ref.current.style.width = 0;
ref.current.classList.add("content-widget-portal");
}
return ref.current;
},
afterRender: function () {
ReactDOM.render(
<div
onMouseOver={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
{children}
</div>,
ref.current
);
},
getPosition: function () {
return {
position: {
lineNumber: line,
column: 0,
},
preference: [monaco.editor.ContentWidgetPositionPreference.EXACT],
};
},
};
editor.addContentWidget(contentWidget);
return () => {
editor.removeContentWidget(contentWidget);
};
}
}, [editor, children]);

return null;
};

export default ContentWidget;
Loading

1 comment on commit 597807e

@vercel
Copy link

@vercel vercel bot commented on 597807e Nov 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.