Skip to content

Commit

Permalink
feat: improve the typing test page
Browse files Browse the repository at this point in the history
  • Loading branch information
aradzie committed Nov 1, 2024
1 parent 81b206e commit de4e436
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 185 deletions.
12 changes: 10 additions & 2 deletions packages/keybr-textinput-events/lib/TextEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
memo,
type ReactNode,
type RefObject,
useEffect,
useImperativeHandle,
useRef,
} from "react";
Expand All @@ -19,13 +20,20 @@ export const TextEvents = memo(function TextEvents({
}: Callbacks & {
readonly focusRef?: RefObject<Focusable>;
}): ReactNode {
const inputRef = useRef<HTMLTextAreaElement>(null);
const handler = useInputHandler();
useImperativeHandle(focusRef, () => handler);
useEffect(() => {
handler.setInput(inputRef.current);
return () => {
handler.setInput(null);
};
}, [handler]);
handler.setCallbacks({ onFocus, onBlur, onKeyDown, onKeyUp, onInput });
return (
<div style={divStyle}>
<textarea
ref={handler.setInput.bind(handler)}
ref={inputRef}
autoCapitalize="off"
autoCorrect="off"
spellCheck="false"
Expand All @@ -35,7 +43,7 @@ export const TextEvents = memo(function TextEvents({
);
});

function useInputHandler(): InputHandler {
function useInputHandler() {
const handlerRef = useRef<InputHandler | null>(null);
let handler = handlerRef.current;
if (handler == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { type ReactNode, useState } from "react";
import { ExplainerStateContext } from "./context.ts";

export function ExplainerBoundary({
defaultVisible = true,
children,
}: {
readonly defaultVisible?: boolean;
readonly children: ReactNode;
}): ReactNode {
const [explainersVisible, setExplainersVisible] = useState(true);
const [explainersVisible, setExplainersVisible] = useState(defaultVisible);
return (
<ExplainerStateContext.Provider
value={{
Expand Down
4 changes: 4 additions & 0 deletions packages/keybr-widget/lib/components/text/Spacer.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@
.size5 {
margin-block-start: 5rem;
}

.size10 {
margin-block-start: 10rem;
}
1 change: 1 addition & 0 deletions packages/keybr-widget/lib/components/text/Spacer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function Spacer({ size }: SpacerProps): ReactNode {
[styles.size3]: size === 3,
[styles.size4]: size === 4,
[styles.size5]: size === 5,
[styles.size10]: size === 10,
})}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/keybr-widget/lib/components/text/Spacer.types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type SpacerProps = {
readonly size: 1 | 2 | 3 | 4 | 5;
readonly size: 1 | 2 | 3 | 4 | 5 | 10;
};
4 changes: 2 additions & 2 deletions packages/page-typing-test/lib/TypingTestPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ test("render", async () => {
</FakeIntlProvider>,
);

fireEvent.click(await r.findByText("Settings..."));
fireEvent.click(await r.findByText("Done"));
fireEvent.click(await r.findByTitle("Settings", { exact: false }));
fireEvent.click(await r.findByTitle("Save settings", { exact: false }));

r.unmount();
});
10 changes: 5 additions & 5 deletions packages/page-typing-test/lib/TypingTestPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useSettings } from "@keybr/settings";
import { makeStats, type Stats } from "@keybr/textinput";
import { makeStats } from "@keybr/textinput";
import { type ReactNode, useMemo, useState } from "react";
import { Report } from "./components/Report.tsx";
import { SettingsScreen } from "./components/SettingsScreen.tsx";
Expand All @@ -20,17 +20,17 @@ export function TypingTestPage(): ReactNode {
[settings],
);
const [view, setView] = useState(View.Test);
const [stats, setStats] = useState<Stats>(makeStats([]));
const [stats, setStats] = useState(makeStats([]));

switch (view) {
case View.Test:
return (
<TextGeneratorLoader textSource={compositeSettings.textSource}>
{(textGenerator) => {
{(generator) => {
return (
<TestScreen
settings={compositeSettings}
textGenerator={textGenerator}
generator={generator}
onComplete={(stats) => {
setView(View.Report);
setStats(stats);
Expand All @@ -46,7 +46,7 @@ export function TypingTestPage(): ReactNode {
case View.Report:
return (
<Report
stats={stats!}
stats={stats}
onNext={() => {
setView(View.Test);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
.line {
display: flex;
align-items: center;
border-block-end: var(--separator-border);
}

.text {
flex: auto;
}

.stats {
min-inline-size: 10rem;
text-align: end;
}
2 changes: 1 addition & 1 deletion packages/page-typing-test/lib/components/LineTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function Stats({ length, time, progress }: Progress): ReactNode {
title="Time passed."
/>
{" / "}
<Value value={`${length}`} title="Chars inputted." />
<Value value={`${length}`} title="Characters inputted." />
{" / "}
<Value value={`${Math.floor(progress * 100)}%`} title="Progress made." />
</>
Expand Down
38 changes: 11 additions & 27 deletions packages/page-typing-test/lib/components/Report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import {
Icon,
Name,
NameValue,
useHotkeys,
Value,
} from "@keybr/widget";
import { mdiFileImage, mdiSkipNext } from "@mdi/js";
import { captureElementToImage } from "@sosimple/dom-to-image";
import { mdiSkipNext } from "@mdi/js";
import { memo, type ReactNode } from "react";
import * as styles from "./Report.module.less";

Expand All @@ -29,27 +29,14 @@ export const Report = memo(function Report({
readonly onNext: () => void;
}): ReactNode {
const { formatNumber, formatPercents } = useIntlNumbers();

useHotkeys(["Enter", onNext]);

const dSpeed = makeSpeedDistribution();
const dAccuracy = makeAccuracyDistribution();
const pSpeed = dSpeed.cdf(speed);
const pAccuracy = dAccuracy.cdf(dAccuracy.scale(accuracy));

const handleClickNext = () => {
onNext();
};

const handleClickScreenshot = () => {
const selector = `.${styles.printable}`;
const { backgroundColor } = getComputedStyle(document.body);
captureElementToImage(selector, { backgroundColor })
.then((blob) => {
window.open(URL.createObjectURL(blob), "_blank");
})
.catch((error) => {
console.error(error);
});
};

return (
<div className={styles.report}>
<div className={styles.printable}>
Expand Down Expand Up @@ -121,23 +108,20 @@ export const Report = memo(function Report({

<div className={styles.controlsLine}>
<FieldList>
<Field.Filler />
<Field>
<Button
label="Next test"
icon={<Icon shape={mdiSkipNext} />}
title="Try another test."
onClick={handleClickNext}
/>
</Field>
<Field>
<Button
label="Screenshot"
icon={<Icon shape={mdiFileImage} />}
title="Take a screenshot."
onClick={handleClickScreenshot}
onClick={onNext}
/>
</Field>
<Field.Filler />
</FieldList>
<p>
Press the <kbd>Enter</kbd> key to start a new test.
</p>
</div>
</div>
);
Expand Down
64 changes: 37 additions & 27 deletions packages/page-typing-test/lib/components/SettingsScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Screen } from "@keybr/pages-shared";
import { TypingSettings } from "@keybr/textinput-ui";
import { Button, Field, FieldList, Icon, Tab, TabList } from "@keybr/widget";
import {
Button,
ExplainerBoundary,
Field,
FieldList,
Icon,
Tab,
TabList,
} from "@keybr/widget";
import { mdiCheckCircle } from "@mdi/js";
import { type ReactNode, useState } from "react";
import { TextGeneratorSettings } from "./settings/TextGeneratorSettings.tsx";
Expand All @@ -14,34 +22,36 @@ export function SettingsScreen({

return (
<Screen>
<TabList
selectedIndex={tabIndex}
onSelect={(tabIndex) => {
setTabIndex(tabIndex);
}}
>
<Tab label="Text">
<TextGeneratorSettings />
</Tab>
<ExplainerBoundary defaultVisible={false}>
<TabList
selectedIndex={tabIndex}
onSelect={(tabIndex) => {
setTabIndex(tabIndex);
}}
>
<Tab label="Text">
<TextGeneratorSettings />
</Tab>

<Tab label="Typing">
<TypingSettings />
</Tab>
</TabList>
<Tab label="Typing">
<TypingSettings />
</Tab>
</TabList>

<FieldList>
<Field.Filler />
<Field>
<Button
icon={<Icon shape={mdiCheckCircle} />}
label="Done"
title="Save settings and return to the test."
onClick={() => {
onSubmit();
}}
/>
</Field>
</FieldList>
<FieldList>
<Field.Filler />
<Field>
<Button
icon={<Icon shape={mdiCheckCircle} />}
label="Done"
title="Save settings and return to the test."
onClick={() => {
onSubmit();
}}
/>
</Field>
</FieldList>
</ExplainerBoundary>
</Screen>
);
}
Loading

0 comments on commit de4e436

Please sign in to comment.