keylighter/assets/ts/try-form.tsx
2020-04-13 17:00:16 +02:00

127 lines
4.9 KiB
TypeScript

import { Fragment, h, render } from "preact";
import { useState } from "preact/hooks";
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 class="demo__form">
<div class="form-group">
<label for="form_source">
<span class="far fa-code" aria-hidden="true"/> try it
</label>
<textarea name="source" class="form-control" id="form_source" rows={ 15 } onInput={ ev => setCode((ev.target as HTMLTextAreaElement).value) }>{ code }</textarea>
</div>
<div class="form-group row">
<label for="form_language" class="col-md-2 col-form-label">
<span class="far fa-lightbulb" aria-hidden="true"/> language
</label>
<div class="col-md-10">
<input type="text" name="language" id="form_language" class="form-control" required value={ language } onInput={ ev => setLanguage((ev.target as HTMLInputElement).value) }/>
</div>
</div>
<div class="form-group">
<div id="language-selector" class="standalone languages">
</div>
</div>
<div class="demo__actions">
<button type="button" class="btn btn-sm btn-outline-danger" onClick={ handleReset }>
<span class="far fa-trash fa-fw" aria-hidden="true"/> reset
</button>
<button type="button" class="btn btn-sm btn-outline-primary" onClick={ handleSubmit }>
<span class="far fa-code fa-fw" aria-hidden="true"/> highlight
</button>
</div>
</form>
)
};
const Loading = () => <div class="loading">
<span class="fad fa-spinner-third fa-spin fa-10x" />
</div>;
type HighlightedCodeProps = {
highlighted: HighlightedResponse;
onClose?(): void;
}
const HighlightedCode = ({ highlighted, onClose }: HighlightedCodeProps) =>
<Fragment>
<pre class="keylighter" dangerouslySetInnerHTML={{ __html: highlighted.html }}/>
<div class="d-flex align-items-center">
<div class="demo__times">
<div class="demo__time-splits">
<div class="demo__time-split demo__time-split--tokenization" style={{ width: `${highlighted.times.tokenization / highlighted.times.total * 100}%` }}/>
<div class="demo__time-split demo__time-split--processing" style={{ width: `${highlighted.times.processing / highlighted.times.total * 100}%` }}/>
<div class="demo__time-split demo__time-split--formatting" style={{ width: `${highlighted.times.formatting / highlighted.times.total * 100}%` }}/>
</div>
<div class="demo__time-total">
Total time: { formatter.format(highlighted.times.total) }ms
</div>
</div>
<div class="demo__actions ml-auto">
<button type="button" class="btn btn-sm btn-outline-primary" onClick={ () => onClose && onClose() }>
<span class="far fa-sync fa-fw mr-1" aria-hidden="true"/>
try another
</button>
</div>
</div>
</Fragment>;
type DemoState = "form" | "submitting" | "show";
type HighlightedResponse = {
html: string;
times: {
total: number;
tokenization: number;
processing: number;
formatting: number;
}
}
const KeylighterDemo = () => {
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" class="demo">
<div class="container">
{ state === "form" && <HighlightForm onSubmit={ handleCodeHighlight }/> }
{ state === "submitting" && <Loading /> }
{ state === "show" && <HighlightedCode highlighted={ highlighted } onClose={ () => setState("form") } /> }
</div>
</div>
)
};
const root = document.getElementById('try-form');
render(<KeylighterDemo />, root.parentElement, root);