Skip to content

Commit e20d5d2

Browse files
committed
Add useBlurWorkaround hook to fix Safari blur issue on input
1 parent f1a3a75 commit e20d5d2

File tree

4 files changed

+58
-3
lines changed

4 files changed

+58
-3
lines changed

special-pages/pages/new-tab/app/omnibar/components/SearchForm.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { h } from 'preact';
2-
import { useContext, useId } from 'preact/hooks';
2+
import { useContext, useId, useRef } from 'preact/hooks';
33
import { SearchIcon } from '../../components/Icons.js';
44
import { useTypedTranslationWith } from '../../types';
55
import { OmnibarContext } from './OmnibarProvider';
66
import styles from './SearchForm.module.css';
77
import { SuggestionsList } from './SuggestionsList.js';
88
import { useSuggestionInput } from './useSuggestionInput.js';
99
import { useSuggestions } from './useSuggestions';
10+
import { mergeRefs } from '../../utils.js';
11+
import { useBlurWorkaround } from './useBlurWorkaround.js';
1012

1113
/**
1214
* @typedef {import('../strings.json')} Strings
@@ -39,7 +41,8 @@ export function SearchForm({ term, setTerm }) {
3941
setTerm,
4042
});
4143

42-
const inputRef = useSuggestionInput(inputBase, inputSuggestion);
44+
const inputRef = useRef(/** @type {HTMLInputElement|null} */ (null));
45+
const mergedInputRef = mergeRefs(inputRef, useSuggestionInput(inputBase, inputSuggestion), useBlurWorkaround());
4346

4447
/** @type {(event: SubmitEvent) => void} */
4548
const onFormSubmit = (event) => {
@@ -55,7 +58,7 @@ export function SearchForm({ term, setTerm }) {
5558
<div class={styles.inputContainer}>
5659
<SearchIcon inert />
5760
<input
58-
ref={inputRef}
61+
ref={mergedInputRef}
5962
type="text"
6063
role="combobox"
6164
class={styles.input}

special-pages/pages/new-tab/app/omnibar/components/SearchForm.module.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
}
1212

1313
.input {
14+
--focus-state: 0; /* see useBlurWorkaround.js */
1415
background: none;
1516
border: none;
1617
color: var(--ntp-text-normal);
@@ -19,6 +20,7 @@
1920
padding: 0;
2021

2122
&:focus {
23+
--focus-state: 1; /* see useBlurWorkaround.js */
2224
outline: none;
2325
}
2426
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { useEffect, useRef } from 'preact/hooks';
2+
3+
/**
4+
* Safari/WebKit doesn't trigger the blur event on an element when focus is
5+
* moved to the browser's address bar. This hook works around this issue by
6+
* monitoring for when the --focus-state CSS variable, set via the :focus pseudo
7+
* selector, changes from 1 to 0, indicating that the element has lost focus.
8+
*/
9+
export function useBlurWorkaround() {
10+
const ref = useRef(/** @type {HTMLElement|null} */ (null));
11+
12+
useEffect(() => {
13+
const element = ref.current;
14+
if (!element) return;
15+
16+
let lastFocusState = '0';
17+
let rafId = null;
18+
19+
const checkFocusState = () => {
20+
const currentFocusState = getComputedStyle(element).getPropertyValue('--focus-state').trim();
21+
if (lastFocusState === '1' && currentFocusState === '0') {
22+
element.blur();
23+
}
24+
lastFocusState = currentFocusState;
25+
rafId = requestAnimationFrame(checkFocusState);
26+
};
27+
28+
rafId = requestAnimationFrame(checkFocusState);
29+
return () => cancelAnimationFrame(rafId);
30+
}, []);
31+
32+
return ref;
33+
}

special-pages/pages/new-tab/app/utils.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,20 @@ export function useOnMiddleClick(ref, handler) {
9797
};
9898
}, [ref, handler]);
9999
}
100+
101+
/**
102+
* @template T
103+
* @param {...import('preact').Ref<T>} refs
104+
* @returns {import('preact').RefCallback<T>}
105+
*/
106+
export function mergeRefs(...refs) {
107+
return (value) => {
108+
refs.forEach((ref) => {
109+
if (typeof ref === 'function') {
110+
ref(value);
111+
} else if (ref !== null) {
112+
ref.current = value;
113+
}
114+
});
115+
};
116+
}

0 commit comments

Comments
 (0)