Skip to content

Commit

Permalink
fix: refactor use-focus-state to cause one redraw
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Feb 12, 2024
1 parent b7e53d7 commit ebd13e1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 14 deletions.
82 changes: 71 additions & 11 deletions src/use-focus-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,88 @@ import { createNanoEvents } from './nano-events';

const mainbus = createNanoEvents();

let subscribeCounter = 0;

const onFocusIn = event => mainbus.emit('assign', event.target);
const onFocusOut = event => mainbus.emit('reset', event.target);

/**
* attaches focusin/focusout listener-singlenton to the document
* it will emit "reset" event on blur/focusout and cause "set" on focus/focusin
*/
const useDocumentFocusSubscribe = () => {
useEffect(() => {
if (!subscribeCounter) {
document.addEventListener('focusin', onFocusIn);
document.addEventListener('focusout', onFocusOut);
}
subscribeCounter += 1;
return () => {
subscribeCounter -= 1;
if (!subscribeCounter) {
document.removeEventListener('focusin', onFocusIn);
document.removeEventListener('focusout', onFocusOut);
}
};
}, []);
};

const getFocusState = (target, current) => {
if (target === current) {
return 'self';
}
if (current.contains(target)) {
return 'within';
}
return 'within-boundary';
};

export const useFocusState = () => {
const [marker] = useState({});
const [active, setActive] = useState(false);
const [state, setState] = useState('');
const ref = useRef(null);
const focusState = useRef({});

const onFocus = useCallback(() => {
mainbus.emit('focus', marker);
// initial focus
useEffect(() => {
if (ref.current) {
setActive(
ref.current === document.activeElement
|| ref.current.contains(document.activeElement),
);
setState(getFocusState(document.activeElement, ref.current));
}
}, []);

useEffect(() => {
setActive(
ref.current === document.activeElement
|| ref.current.contains(document.activeElement),
);
const onFocus = useCallback((e) => {
// element caught focus. Store, but do not set value yes
focusState.current = {
focused: true,
state: getFocusState(e.target, e.currentTarget),
};
}, []);

useEffect(() => mainbus.on('focus', (event) => {
setActive(event === marker);
}), []);

useDocumentFocusSubscribe();
useEffect(() => {
const fout = mainbus.on('reset', () => {
// focus is going somewhere
focusState.current = {};
});
const fin = mainbus.on('assign', () => {
// focus event propagation is ended
setActive(focusState.current.focused || false);
setState(focusState.current.state || '');
});
return () => {
fout();
fin();
};
}, []);

return {
active,
state,
onFocus,
ref,
};
Expand Down
17 changes: 14 additions & 3 deletions stories/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ const ControlTrap = () => {

const FocusButton = ({ children }) => {
const { active, onFocus, ref } = useFocusState();
return <button tabIndex={active ? undefined : -1} onFocus={onFocus} ref={ref}>{children}</button>;
return (
<button tabIndex={active ? undefined : -1} onFocus={onFocus} ref={ref}>
{active ? '*' : ''}
{children}
</button>
);
};
const RowingFocusTrap = () => {
const { autofocus, focusNext, focusPrev } = useFocusScope();
Expand All @@ -56,7 +61,7 @@ const RowingFocusTrap = () => {
onKeyDown={onKey}
onFocus={onFocus}
ref={ref}
style={{ border: active ? '3px solid green' : '3px solid grey' }}
style={{ border: active ? '3px solid red' : '3px solid #EEE' }}
>
<button>zero</button>
<FocusButton>Button1</FocusButton>
Expand All @@ -75,7 +80,13 @@ const ControlledFocusButton = ({ children, onFocus: reportFocused, isActive }) =
}
}, [active]);

return <button tabIndex={isActive ? undefined : -1} onFocus={onFocus} ref={ref}>{children}</button>;
return (
<button tabIndex={isActive ? undefined : -1} onFocus={onFocus} ref={ref}>
{active ? '*' : ''}
{children}
{isActive ? '*' : ''}
</button>
);
};
const ConstantRowingFocusTrap = () => {
const { focusNext, focusPrev } = useFocusScope();
Expand Down

0 comments on commit ebd13e1

Please sign in to comment.