Skip to content

NTP: Add useBlurWorkaround hook to fix WebKit blur issue in Omnibar #1820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { h } from 'preact';
import { useContext, useId } from 'preact/hooks';
import { useContext, useId, useRef } from 'preact/hooks';
import { SearchIcon } from '../../components/Icons.js';
import { useTypedTranslationWith } from '../../types';
import { OmnibarContext } from './OmnibarProvider';
import styles from './SearchForm.module.css';
import { SuggestionsList } from './SuggestionsList.js';
import { useSuggestionInput } from './useSuggestionInput.js';
import { useSuggestions } from './useSuggestions';
import { mergeRefs } from '../../utils.js';
import { useBlurWorkaround } from './useBlurWorkaround.js';

/**
* @typedef {import('../strings.json')} Strings
Expand Down Expand Up @@ -39,7 +41,8 @@ export function SearchForm({ term, setTerm }) {
setTerm,
});

const inputRef = useSuggestionInput(inputBase, inputSuggestion);
const inputRef = useRef(/** @type {HTMLInputElement|null} */ (null));
const mergedInputRef = mergeRefs(inputRef, useSuggestionInput(inputBase, inputSuggestion), useBlurWorkaround());

/** @type {(event: SubmitEvent) => void} */
const onFormSubmit = (event) => {
Expand All @@ -55,7 +58,7 @@ export function SearchForm({ term, setTerm }) {
<div class={styles.inputContainer}>
<SearchIcon inert />
<input
ref={inputRef}
ref={mergedInputRef}
type="text"
role="combobox"
class={styles.input}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
}

.input {
--focus-state: 0; /* see useBlurWorkaround.js */
background: none;
border: none;
color: var(--ntp-text-normal);
Expand All @@ -19,6 +20,7 @@
padding: 0;

&:focus {
--focus-state: 1; /* see useBlurWorkaround.js */
outline: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useRef } from 'preact/hooks';

/**
* Safari/WebKit doesn't trigger the blur event on an element when focus is
* moved to the browser's address bar. This hook works around this issue by
* monitoring for when the --focus-state CSS variable, set via the :focus pseudo
* selector, changes from 1 to 0, indicating that the element has lost focus.
*/
export function useBlurWorkaround() {
const ref = useRef(/** @type {HTMLElement|null} */ (null));

useEffect(() => {
const element = ref.current;
if (!element) return;

let lastFocusState = '0';
let rafId = 0;

const checkFocusState = () => {
const currentFocusState = getComputedStyle(element).getPropertyValue('--focus-state').trim();
if (lastFocusState === '1' && currentFocusState === '0') {
element.blur();
}
lastFocusState = currentFocusState;
rafId = requestAnimationFrame(checkFocusState);
};

rafId = requestAnimationFrame(checkFocusState);
return () => cancelAnimationFrame(rafId);
}, []);

return ref;
}
17 changes: 17 additions & 0 deletions special-pages/pages/new-tab/app/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,20 @@ export function useOnMiddleClick(ref, handler) {
};
}, [ref, handler]);
}

/**
* @template T
* @param {...import('preact').Ref<T>} refs
* @returns {import('preact').RefCallback<T>}
*/
export function mergeRefs(...refs) {
return (value) => {
refs.forEach((ref) => {
if (typeof ref === 'function') {
ref(value);
} else if (ref) {
ref.current = value;
}
});
};
}
Loading