Skip to content

Commit

Permalink
refactor(focusVisibleElement): add raf & comments
Browse files Browse the repository at this point in the history
  • Loading branch information
thetaPC committed Feb 27, 2025
1 parent cf44ede commit 07b6a26
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 16 deletions.
3 changes: 2 additions & 1 deletion core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ export namespace Components {
*/
"mode"?: "ios" | "md";
/**
* Used to set focus on an element that uses `ion-focusable`. Do not use this if focusing the element as a result of a keyboard event as the focus utility should handle this for us. This method should be used when we want to programmatically focus an element as a result of another user action. (Ex: We focus the first element inside of a popover when the user presents it, but the popover is not always presented as a result of keyboard action.)
* Sets focus on elements that use `ion-focusable`.
* @param elements - The elements to set focus on.
*/
"setFocus": (elements: HTMLElement[]) => Promise<void>;
/**
Expand Down
14 changes: 7 additions & 7 deletions core/src/components/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ export class App implements ComponentInterface {
@Element() el!: HTMLElement;

/**
* Used to set focus on an element that uses `ion-focusable`.
* Do not use this if focusing the element as a result of a keyboard
* event as the focus utility should handle this for us. This method
* should be used when we want to programmatically focus an element as
* a result of another user action. (Ex: We focus the first element
* inside of a popover when the user presents it, but the popover is not always
* presented as a result of keyboard action.)
* Sets focus on elements that use `ion-focusable`.
*
* @param elements - The elements to set focus on.
*/
@Method()
async setFocus(elements: HTMLElement[]) {
/**
* The focus-visible utility is used to set focus on an
* element that uses `ion-focusable`.
*/
focusElements(elements);
}

Expand Down
2 changes: 2 additions & 0 deletions core/src/utils/focus-visible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const getOrInitFocusVisibleUtility = () => {
* a result of another user action. (Ex: We focus the first element
* inside of a popover when the user presents it, but the popover is not always
* presented as a result of keyboard action.)
*
* @param elements - The elements to set focus on.
*/
export const focusElements = (elements: Element[]) => {
const focusVisible = getOrInitFocusVisibleUtility();
Expand Down
33 changes: 25 additions & 8 deletions core/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ export const hasShadowDom = (el: HTMLElement) => {
return !!el.shadowRoot && !!(el as any).attachShadow;
};

/**
* Focuses a given element while ensuring proper focus management
* within the Ionic framework. If the element is marked as `ion-focusable`,
* this function will delegate focus handling to `ion-app` or manually
* apply focus when a custom app root is used.
*
* This function helps maintain accessibility and expected focus behavior
* in both standard and custom root environments.
*
* @param el - The element to focus.
*/
export const focusVisibleElement = (el: HTMLElement) => {
el.focus();

Expand All @@ -281,15 +292,21 @@ export const focusVisibleElement = (el: HTMLElement) => {
app.setFocus([el]);
} else {
/**
* If the user has provided a custom app root selector,
* then we need to manually set focus on the element
* since the focus-visible utility will not be available
* on the custom app root.
*
* The focus-visible utility is used to set focus on an
* element that uses `ion-focusable`.
* When using a custom app root selector, the focus-visible
* utility is not available to manage focus automatically.
* If we set focus immediately, the element may not be fully
* rendered or interactive, especially if it was just added
* to the DOM. Using requestAnimationFrame ensures that focus
* is applied on the next frame, allowing the DOM to settle
* before changing focus.
*/
focusElements([el]);
requestAnimationFrame(() => {
/**
* The focus-visible utility is used to set focus on an
* element that uses `ion-focusable`.
*/
focusElements([el]);
});
}
}
}
Expand Down

0 comments on commit 07b6a26

Please sign in to comment.