Skip to content

Commit

Permalink
feat: keyboard shortcut options
Browse files Browse the repository at this point in the history
  • Loading branch information
emcelroy committed Nov 25, 2024
1 parent a0ddd93 commit 2dfe3b9
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 24 deletions.
20 changes: 20 additions & 0 deletions src/components/App.css → src/components/App.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./vars.scss";

.App {
background-color: white;
color: rgb(110,109,95);
Expand All @@ -14,6 +16,17 @@
font-size: .625rem;
}
}
h2 {
font-size: .75rem;
}
h3 {
font-size: .75rem;
}
hr {
border-top: solid 1px $dark-gray;
height: 0;
margin: 20px 0 0;
}
.buttons {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -46,6 +59,13 @@
}
}

.error-message {
color: #d00;
font-size: .75rem;
margin: 0;
padding: 0;
}

.visually-hidden {
border: 0;
clip: rect(0, 0, 0, 0);
Expand Down
29 changes: 27 additions & 2 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { ChatTranscriptComponent } from "./chat-transcript";
import { ChatTranscript, ChatMessage } from "../types";
import { timeStamp } from "../utils";
import { ReadAloudMenu } from "./readaloud-menu";
import { KeyboardShortcutControls } from "./keyboard-shortcut-controls";

import "./App.css";
import "./App.scss";

const kPluginName = "DAVAI";
const kVersion = "0.0.1";
Expand All @@ -30,15 +31,27 @@ export const App = () => {
const [chatTranscript, setChatTranscript] = useState<ChatTranscript>({messages: [{speaker: "DAVAI", content: greeting, timestamp: timeStamp()}]});
const [readAloudEnabled, setReadAloudEnabled] = useState(false);
const [playbackSpeed, setPlaybackSpeed] = useState(1);
const [keyboardShortcutEnabled, setKeyboardShortcutEnabled] = useState(true);
const [keyboardShortcutKeys, setKeyboardShortcutKeys] = useState("Ctrl+Command+A");

useEffect(() => {
initializePlugin({pluginName: kPluginName, version: kVersion, dimensions: kInitialDimensions});
selectSelf();
}, []);

const handleFocusShortcut = () => {
selectSelf();
};

const handleToggleShortcut = () => {
setKeyboardShortcutEnabled(!keyboardShortcutEnabled);
};

const handleCustomizeShortcut = (shortcut: string) => {
console.log("2 Customizing shortcut to", shortcut);
setKeyboardShortcutKeys(shortcut);
};

const handleSetReadAloudEnabled = () => {
setReadAloudEnabled(!readAloudEnabled);
};
Expand Down Expand Up @@ -68,13 +81,25 @@ export const App = () => {
</h1>
</header>
<ChatTranscriptComponent chatTranscript={chatTranscript} />
<ChatInputComponent onSubmit={handleChatInputSubmit} onKeyboardShortcut={handleFocusShortcut} />
<ChatInputComponent
keyboardShortcutEnabled={keyboardShortcutEnabled}
onSubmit={handleChatInputSubmit}
onKeyboardShortcut={handleFocusShortcut}
/>
<ReadAloudMenu
enabled={readAloudEnabled}
onToggle={handleSetReadAloudEnabled}
playbackSpeed={playbackSpeed}
onPlaybackSpeedSelect={handleSetPlaybackSpeed}
/>
<hr />
<h2>Options</h2>
<KeyboardShortcutControls
shortcutEnabled={keyboardShortcutEnabled}
shortcutKeys={keyboardShortcutKeys}
onCustomizeShortcut={handleCustomizeShortcut}
onToggleShortcut={handleToggleShortcut}
/>
</div>
);
};
7 changes: 0 additions & 7 deletions src/components/chat-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,6 @@
padding: 8px 10px;
}

.error {
color: #d00;
font-size: .75rem;
margin: 0;
padding: 0;
}

.buttons-container {
align-items: center;
display: flex;
Expand Down
2 changes: 1 addition & 1 deletion src/components/chat-input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe("test chat input component", () => {
const mockHandleSubmit = jest.fn();

it("renders a textarea and submit button that lets user send chat messages", () => {
render(<ChatInputComponent onSubmit={mockHandleSubmit} onKeyboardShortcut={jest.fn()} />);
render(<ChatInputComponent keyboardShortcutEnabled={true} onSubmit={mockHandleSubmit} onKeyboardShortcut={jest.fn()} />);

const chatInput = screen.getByTestId("chat-input");
const chatInputLabel = within(chatInput).getByTestId("chat-input-label");
Expand Down
27 changes: 13 additions & 14 deletions src/components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { isInputElement } from "../utils";
import "./chat-input.scss";

interface IProps {
keyboardShortcutEnabled: boolean;
onKeyboardShortcut: () => void;
onSubmit: (messageText: string) => void;
}

export const ChatInputComponent = ({onKeyboardShortcut, onSubmit}: IProps) => {
export const ChatInputComponent = ({keyboardShortcutEnabled, onKeyboardShortcut, onSubmit}: IProps) => {
const textAreaRef = useRef<HTMLTextAreaElement>(null);
// const [browserSupportsDictation, setBrowserSupportsDictation] = useState(false);
// const [dictationEnabled, setDictationEnabled] = useState(false);
Expand Down Expand Up @@ -118,24 +119,22 @@ export const ChatInputComponent = ({onKeyboardShortcut, onSubmit}: IProps) => {

useEffect(() => {
const keydownListeners: (() => void)[] = [];
// Add keyboard shortcut listener to the parent window if one exists.
if (window.parent && window.parent !== window) {
keydownListeners.push(addShortcutListener(window.parent));
}

// Add keyboard shortcut listener to the current window.
keydownListeners.push(addShortcutListener(window));
if (keyboardShortcutEnabled) {
// Add keyboard shortcut listener to the parent window if one exists.
if (window.parent && window.parent !== window) {
keydownListeners.push(addShortcutListener(window.parent));
}

// Add keyboard shortcut listener to the current window.
keydownListeners.push(addShortcutListener(window));
}

// Clean up the listeners when the component unmounts.
return () => {
keydownListeners.forEach((cleanup) => cleanup());
};
}, [addShortcutListener]);

useEffect(() => {
// Set focus on textarea when component first mounts.
textAreaRef.current?.focus();
}, []);
}, [addShortcutListener, keyboardShortcutEnabled]);

return (
<div className="chat-input" data-testid="chat-input">
Expand All @@ -158,7 +157,7 @@ export const ChatInputComponent = ({onKeyboardShortcut, onSubmit}: IProps) => {
{showError &&
<div
aria-live="assertive"
className="error"
className="error-message"
data-testid="input-error"
id="input-error"
role="alert"
Expand Down
63 changes: 63 additions & 0 deletions src/components/keyboard-shortcut-controls.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@import "./vars.scss";

.keyboard-shortcut-controls {
display: flex;
flex-direction: column;

button {
appearance: none;
background-color: $light-teal-3;
border: solid 1px rgb(118, 118, 118);
border-radius: 6px;
color: $dark-gray;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: .75rem;
height: 32px;
text-align: center;
width: auto;
}

fieldset {
border: none;
flex-wrap: wrap;
margin: 0;
padding: 0;
text-align: left;

&[disabled] {
cursor: not-allowed;
opacity: 0.75;

button, input, label {
cursor: not-allowed;
}
}

button {
margin: 0 0 0 10px;
}
input[type="text"] {
border: solid 1px rgb(118, 118, 118);
border-radius: 6px;
height: 16px;
padding: 7px 10px;
}
}

h3 {
margin: 10px 0 0;
text-align: left;
}

label {
cursor: pointer;
display: block;
font-size: .75rem;
font-weight: bold;
line-height: 1.4;
margin: 0 0 5px;
user-select: none;
white-space: nowrap;
}
}
67 changes: 67 additions & 0 deletions src/components/keyboard-shortcut-controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { FormEvent, useState } from "react";

import "./keyboard-shortcut-controls.scss";

interface IProps {
shortcutEnabled: boolean;
shortcutKeys: string;
onCustomizeShortcut?: (shortcut: string) => void;
onToggleShortcut: () => void;
}

export const KeyboardShortcutControls = (props: IProps) => {
const { shortcutEnabled, shortcutKeys, onCustomizeShortcut, onToggleShortcut } = props;
const toggleButtonLabel = shortcutEnabled ? "Disable Keyboard Shortcut" : "Enable Keyboard Shortcut";
const [showError, setShowError] = useState(false);

const handleToggleShortcut = () => {
onToggleShortcut();
};

const handleCustomizeShortcut = (event: FormEvent) => {
event.preventDefault();
const form = event.target as HTMLInputElement;
// get the text value from the form's input element
const shortcut = form.querySelector("input")?.value;
console.log("Customizing shortcut to", shortcut);
if (shortcut) {
onCustomizeShortcut?.(shortcut);
setShowError(false);
} else {
setShowError(true);
}
};

return (
<div className="keyboard-shortcut-controls">
<h3>Keyboard Shortcut</h3>
<button onClick={handleToggleShortcut}>
{toggleButtonLabel}
</button>
<form onSubmit={handleCustomizeShortcut}>
<fieldset disabled={!shortcutEnabled}>
<label htmlFor="custom-keyboard-shortcut">Customize Keyboard Shortcut</label>
<input
aria-describedby={showError ? "input-error" : undefined}
aria-invalid={showError}
defaultValue={shortcutKeys}
id="custom-keyboard-shortcut"
type="text"
/>
<button type="submit">Customize</button>
{showError &&
<div
aria-live="assertive"
className="error-message"
data-testid="input-error"
id="input-error"
role="alert"
>
Please enter a value for the keyboard shortcut.
</div>
}
</fieldset>
</form>
</div>
);
};
1 change: 1 addition & 0 deletions src/components/readaloud-menu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
appearance: none;
background-color: $light-teal-3;
color: $dark-gray;
cursor: pointer;
height: 32px;
width: 32px;
border-radius: 6px;
Expand Down

0 comments on commit 2dfe3b9

Please sign in to comment.