Skip to content

Commit 779f808

Browse files
committed
fix modal enter bug; add arrow key focus shift for buttons
1 parent 1f0648d commit 779f808

File tree

5 files changed

+61
-5
lines changed

5 files changed

+61
-5
lines changed

jsEngine/api/prompts/ButtonModalComponent.svelte

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { ButtonPromptOptions } from 'jsEngine/api/PromptAPI';
33
import Button from 'jsEngine/utils/Button.svelte';
44
import type { SvelteModal } from './SvelteModal';
5-
import { ButtonStyleType } from 'jsEngine/utils/Util';
5+
import { ButtonStyleType, mod } from 'jsEngine/utils/Util';
66
77
const {
88
options,
@@ -11,13 +11,49 @@
1111
options: ButtonPromptOptions<unknown>;
1212
modal: SvelteModal<any, unknown>;
1313
} = $props();
14+
15+
let divEl: HTMLDivElement;
16+
17+
function shiftFocus(next: boolean) {
18+
const focusedButton = divEl.querySelector('button:focus');
19+
20+
if (focusedButton) {
21+
const index = parseInt(focusedButton.getAttribute('data-index') ?? '0', 10);
22+
const nextIndex = mod(index + (next ? 1 : -1), options.buttons.length);
23+
24+
const newFocusedButton = divEl.querySelector(`button[data-index="${nextIndex}"]`);
25+
26+
if (newFocusedButton && newFocusedButton instanceof HTMLElement) {
27+
newFocusedButton.focus();
28+
}
29+
}
30+
}
31+
32+
function handleKey(event: KeyboardEvent) {
33+
if (event.key === 'ArrowRight') {
34+
event.preventDefault();
35+
shiftFocus(true);
36+
}
37+
38+
if (event.key === 'ArrowLeft') {
39+
event.preventDefault();
40+
shiftFocus(true);
41+
}
42+
}
1443
</script>
1544

1645
<p>{options.content}</p>
1746

18-
<div class="modal-button-container">
19-
{#each options.buttons as button}
20-
<Button variant={button.variant ?? ButtonStyleType.DEFAULT} onclick={() => modal.submit(button.value)}>
47+
<div class="modal-button-container" bind:this={divEl}>
48+
{#each options.buttons as button, i}
49+
<Button
50+
variant={button.variant ?? ButtonStyleType.DEFAULT}
51+
onclick={() => modal.submit(button.value)}
52+
other={{
53+
'data-index': i,
54+
onkeydown: (e: KeyboardEvent) => handleKey(e),
55+
}}
56+
>
2157
{button.label}
2258
</Button>
2359
{/each}

jsEngine/api/prompts/InputModalComponent.svelte

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@
2424
2525
function onKeydown(event: KeyboardEvent) {
2626
if (event.key === 'Enter' && inputType !== 'textarea') {
27+
event.stopPropagation();
28+
event.preventDefault();
29+
30+
modal.submit(value);
31+
}
32+
if (event.key === 'Enter' && event.ctrlKey && inputType === 'textarea') {
33+
event.stopPropagation();
34+
event.preventDefault();
35+
2736
modal.submit(value);
2837
}
38+
2939
if (event.key === 'Escape') {
40+
event.stopPropagation();
41+
event.preventDefault();
42+
3043
modal.submit(undefined);
3144
}
3245
}

jsEngine/utils/Button.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
variant = ButtonStyleType.DEFAULT,
99
disabled = false,
1010
tooltip = '',
11+
other = {},
1112
onclick = () => {},
1213
children,
1314
}: {
1415
variant?: ButtonStyleType;
1516
disabled?: boolean;
1617
tooltip?: string;
18+
other?: Record<string, unknown>;
1719
onclick?: (e: MouseEvent) => void | Promise<void>;
1820
children: Snippet;
1921
} = $props();
@@ -28,6 +30,7 @@
2830
aria-label={tooltip}
2931
onclick={onclick}
3032
disabled={disabled}
33+
{...other}
3134
>
3235
{@render children()}
3336
</button>

jsEngine/utils/Util.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ export enum ButtonStyleType {
88
DESTRUCTIVE = 'destructive',
99
PLAIN = 'plain',
1010
}
11+
12+
export function mod(a: number, b: number): number {
13+
return ((a % b) + b) % b;
14+
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@
2020
"lib": ["DOM", "ESNext"],
2121
"allowSyntheticDefaultImports": true
2222
},
23-
"include": ["jsEngine/**/*.ts"]
23+
"include": ["jsEngine/**/*.ts", "jsEngine/**/*.svelte"]
2424
}

0 commit comments

Comments
 (0)