keylighter/assets/ts/try-form.tsx

143 lines
5.5 KiB
TypeScript

import * as React from "react";
import { useState } from "react";
import * as ReactDOM from "react-dom"
const formatter = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 });
type HighlightFormProps = {
onSubmit(code: string, language: string);
}
const HighlightForm = ({ onSubmit }: HighlightFormProps) => {
const [code, setCode] = useState<string>("");
const [language, setLanguage] = useState<string>("");
const handleReset = () => {
setCode("");
setLanguage("");
};
const handleSubmit = () => onSubmit && onSubmit(code, language);
return (
<form className="demo__form">
<div className="form-group">
<label htmlFor="form_source">
<span className="far fa-code" aria-hidden="true"/> try it
</label>
<textarea name="source" className="form-control" id="form_source" rows={ 15 }
onChange={ ev => setCode((ev.target as HTMLTextAreaElement).value) }
value={ code }
/>
</div>
<div className="form-group row">
<label htmlFor="form_language" className="col-md-2 col-form-label">
<span className="far fa-lightbulb" aria-hidden="true"/> language
</label>
<div className="col-md-10">
<input type="text" name="language" id="form_language" className="form-control" required
value={ language }
onChange={ ev => setLanguage((ev.target as HTMLInputElement).value) }
/>
</div>
</div>
<div className="form-group">
<div id="language-selector" className="standalone languages">
</div>
</div>
<div className="demo__actions">
<button type="button" className="btn btn-sm btn-outline-danger" onClick={ handleReset }>
<span className="far fa-trash fa-fw" aria-hidden="true"/> reset
</button>
<button type="button" className="btn btn-sm btn-outline-primary" onClick={ handleSubmit }>
<span className="far fa-code fa-fw" aria-hidden="true"/> highlight
</button>
</div>
</form>
)
};
const Loading = () => <div className="loading">
<span className="fad fa-spinner-third fa-spin fa-10x" />
</div>;
type HighlightedCodeProps = {
highlighted: HighlightedResponse;
onClose?(): void;
}
const HighlightedCode = ({ highlighted, onClose }: HighlightedCodeProps) =>
<>
<pre className="keylighter" dangerouslySetInnerHTML={{ __html: highlighted.html }}/>
<div className="d-flex align-items-center">
<div className="demo__times">
<div className="demo__time-splits">
<div className="demo__time-split demo__time-split--tokenization" style={{ width: `${highlighted.times.tokenization / highlighted.times.total * 100}%` }}/>
<div className="demo__time-split demo__time-split--processing" style={{ width: `${highlighted.times.processing / highlighted.times.total * 100}%` }}/>
<div className="demo__time-split demo__time-split--formatting" style={{ width: `${highlighted.times.formatting / highlighted.times.total * 100}%` }}/>
</div>
<div className="demo__time-total">
Total time: { formatter.format(highlighted.times.total) }ms
</div>
</div>
<div className="demo__actions ml-auto">
<button type="button" className="btn btn-sm btn-outline-primary" onClick={ () => onClose && onClose() }>
<span className="far fa-sync fa-fw mr-1" aria-hidden="true"/>
try another
</button>
</div>
</div>
</>;
type DemoState = "form" | "submitting" | "show";
type HighlightedResponse = {
html: string;
times: {
total: number;
tokenization: number;
processing: number;
formatting: number;
}
}
const KeylighterDemo = props => {
const [state, setState] = useState<DemoState>("form");
const [highlighted, setHighlighted] = useState<HighlightedResponse>(null);
const handleCodeHighlight = async (source, language) => {
setState("submitting");
setHighlighted(await fetch('/highlight', {
credentials: "include",
method: "POST",
body: JSON.stringify({ source, language }),
headers: {
'Content-Type': 'application/json'
}
}).then(res => res.json()));
setState("show");
};
return (
<div id="try-form" className="demo">
<div className="container">
{ state === "form" && <HighlightForm onSubmit={ handleCodeHighlight }/> }
{ state === "submitting" && <Loading /> }
{ state === "show" && <HighlightedCode highlighted={ highlighted } onClose={ () => setState("form") } /> }
</div>
</div>
)
};
export function replace<T extends Element>(component: any, element: HTMLElement, callback?: () => void) {
const parent = document.createElement('div');
ReactDOM.render(component, parent, () => {
element.replaceWith(...Array.from(parent.childNodes));
callback && callback();
});
}
const root = document.getElementById('try-form');
replace(<KeylighterDemo />, root);