Skip to content

Commit 6cb7f90

Browse files
committed
ui: highlight the second-focused key in the All keys row
Solid outline in --accent, distinct from the focused key's --secondary-l2.
1 parent 2bbf705 commit 6cb7f90

10 files changed

Lines changed: 113 additions & 0 deletions

File tree

packages/keybr-lesson-ui/lib/Key.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,31 @@ test("render included", () => {
5757
r.unmount();
5858
});
5959

60+
test("render second focused", () => {
61+
const key = new LessonKey({
62+
letter: FakePhoneticModel.letter1,
63+
samples: [],
64+
timeToType: null,
65+
bestTimeToType: null,
66+
confidence: null,
67+
bestConfidence: null,
68+
}).asSecondFocused();
69+
70+
const r = render(
71+
<FakeIntlProvider>
72+
<FakeSettingsContext>
73+
<Key lessonKey={key} />
74+
</FakeSettingsContext>
75+
</FakeIntlProvider>,
76+
);
77+
78+
const elem = r.container.querySelector(".lessonKey_secondFocused");
79+
isNotNull(elem);
80+
equal(Key.attached(elem), key);
81+
82+
r.unmount();
83+
});
84+
6085
test("render focused", () => {
6186
const key = new LessonKey({
6287
letter: FakePhoneticModel.letter1,

packages/keybr-lesson-ui/lib/Key.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const Key = ({
2424
confidence,
2525
isIncluded,
2626
isFocused,
27+
isSecondFocused,
2728
isForced,
2829
} = lessonKey;
2930
return (
@@ -40,6 +41,7 @@ export const Key = ({
4041
isIncluded && confidence == null && styles.lessonKey_uncalibrated,
4142
isIncluded && isFocused && styles.lessonKey_focused,
4243
isIncluded && isForced && styles.lessonKey_forced,
44+
isSecondFocused && styles.lessonKey_secondFocused,
4345
isSelectable && styles.lessonKey_selectable,
4446
isCurrent && styles.lessonKey_current,
4547
)}

packages/keybr-lesson-ui/lib/KeyLegend.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,21 @@ test("render included", () => {
3737

3838
r.unmount();
3939
});
40+
41+
test("render second focused", () => {
42+
const r = render(
43+
<FakeIntlProvider>
44+
<KeyLegend
45+
isIncluded={false}
46+
confidence={null}
47+
isFocused={false}
48+
isForced={false}
49+
isSecondFocused={true}
50+
/>
51+
</FakeIntlProvider>,
52+
);
53+
54+
isNotNull(r.container.querySelector(".lessonKey_secondFocused"));
55+
56+
r.unmount();
57+
});

packages/keybr-lesson-ui/lib/KeyLegend.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const KeyLegend = ({
88
isIncluded,
99
isFocused,
1010
isForced,
11+
isSecondFocused = false,
1112
size = "normal",
1213
title,
1314
...props
@@ -16,6 +17,7 @@ export const KeyLegend = ({
1617
isIncluded: boolean;
1718
isFocused: boolean;
1819
isForced: boolean;
20+
isSecondFocused?: boolean;
1921
size?: "normal" | "large";
2022
title?: string;
2123
} & MouseProps) => {
@@ -31,6 +33,7 @@ export const KeyLegend = ({
3133
isIncluded && confidence == null && styles.lessonKey_uncalibrated,
3234
isIncluded && isFocused && styles.lessonKey_focused,
3335
isIncluded && isForced && styles.lessonKey_forced,
36+
isSecondFocused && styles.lessonKey_secondFocused,
3437
)}
3538
style={keyStyles(isIncluded ?? false, confidence ?? null)}
3639
title={title}

packages/keybr-lesson-ui/lib/KeyLegendList.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ export const KeyLegendList = () => {
5252
defaultMessage="A key with increased frequency. It takes you the most time to find this key so the algorithm chose it to be included in every generated word."
5353
/>
5454
</li>
55+
<li>
56+
<KeyLegend //
57+
isIncluded={false}
58+
confidence={null}
59+
isFocused={false}
60+
isForced={false}
61+
isSecondFocused={true}
62+
/>{" "}
63+
<FormattedMessage
64+
id="lesson.indicator.secondFocused"
65+
defaultMessage="A locked key that will unlock next, or an already-included key that needs more practice, mixed into the lesson early as a preview."
66+
/>
67+
</li>
5568
<li>
5669
<KeyLegend //
5770
isIncluded={true}

packages/keybr-lesson-ui/lib/styles.module.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
text-decoration: underline;
6060
}
6161

62+
.lessonKey_secondFocused {
63+
outline: 1px solid var(--LessonKey--secondFocused__outline-color);
64+
}
65+
6266
.lessonKey_selectable {
6367
cursor: pointer;
6468
}

packages/keybr-theme-designer/lib/design/LessonKeysPreview.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function LessonKeysPreview() {
2222
bestConfidence: value,
2323
isIncluded: true,
2424
isFocused: value === 0.0,
25+
isSecondFocused: value === 0.1,
2526
isForced: false,
2627
})
2728
}

packages/keybr-themes/lib/themes/theme.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
--LessonKey--uncalibrated__background-color: var(--primary-d1);
2626
--LessonKey--uncalibrated__outline-color: transparent;
2727
--LessonKey--focused__outline-color: var(--secondary-l2);
28+
--LessonKey--secondFocused__outline-color: var(--accent);
2829
--LessonKey--forced__outline-color: transparent;
2930
--LessonKey--current__outline-color: var(--secondary-l2);
3031

packages/page-practice/lib/settings/lesson/GuidedLessonSettings.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { RecoverKeysProp } from "./RecoverKeysProp.tsx";
1010
import { RepeatWordsProp } from "./RepeatWordsProp.tsx";
1111
import { TargetSpeedProp } from "./TargetSpeedProp.tsx";
1212
import { TextManglingProp } from "./TextManglingProp.tsx";
13+
import { TrainSecondKeyProp } from "./TrainSecondKeyProp.tsx";
1314

1415
export function GuidedLessonSettings({
1516
lesson,
@@ -37,6 +38,7 @@ export function GuidedLessonSettings({
3738
<RecoverKeysProp />
3839
<KeyboardOrderProp />
3940
<NaturalWordsProp />
41+
<TrainSecondKeyProp />
4042
<RepeatWordsProp />
4143
<AlphabetSizeProp />
4244
<TextManglingProp />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { lessonProps } from "@keybr/lesson";
2+
import { useSettings } from "@keybr/settings";
3+
import {
4+
CheckBox,
5+
Description,
6+
Explainer,
7+
Field,
8+
FieldList,
9+
} from "@keybr/widget";
10+
import { type ReactNode } from "react";
11+
import { FormattedMessage, useIntl } from "react-intl";
12+
13+
export function TrainSecondKeyProp(): ReactNode {
14+
const { formatMessage } = useIntl();
15+
const { settings, updateSettings } = useSettings();
16+
return (
17+
<>
18+
<FieldList>
19+
<Field>
20+
<CheckBox
21+
label={formatMessage({
22+
id: "settings.trainSecondKey.label",
23+
defaultMessage: "Train second key",
24+
})}
25+
checked={settings.get(lessonProps.guided.trainSecondKey)}
26+
onChange={(value) => {
27+
updateSettings(
28+
settings.set(lessonProps.guided.trainSecondKey, value),
29+
);
30+
}}
31+
/>
32+
</Field>
33+
</FieldList>
34+
<Explainer>
35+
<Description>
36+
<FormattedMessage
37+
id="settings.trainSecondKey.description"
38+
defaultMessage="Occasionally mix in words for a second key alongside the one you are focused on: the next letter to unlock, or, once every letter is unlocked, your second-weakest key."
39+
/>
40+
</Description>
41+
</Explainer>
42+
</>
43+
);
44+
}

0 commit comments

Comments
 (0)