Skip to content

Commit

Permalink
getRootActiveElement
Browse files Browse the repository at this point in the history
  • Loading branch information
scomea committed Jan 24, 2024
1 parent 9af1b25 commit d80f077
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 37 deletions.
3 changes: 3 additions & 0 deletions packages/web-components/fast-foundation/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,9 @@ export type GenerateHeaderOptions = ValuesOf<typeof GenerateHeaderOptions>;
// @public
export const getDirection: (rootNode: HTMLElement) => Direction;

// @public (undocumented)
export function getRootActiveElement(element: Element): Element | null;

// @public
export const hidden = ":host([hidden]){display:none}";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { FASTListboxOption } from "../listbox-option/listbox-option.js";
import { DelegatesARIAListbox } from "../listbox/listbox.js";
import { StartEnd } from "../patterns/start-end.js";
import type { StartEndOptions } from "../patterns/start-end.js";
import { getRootActiveElement } from "../utilities/index.js";
import { applyMixins } from "../utilities/apply-mixins.js";
import { FormAssociatedCombobox } from "./combobox.form-associated.js";
import { ComboboxAutocomplete } from "./combobox.options.js";
Expand Down Expand Up @@ -337,7 +338,7 @@ export class FASTCombobox extends FormAssociatedCombobox {
* Overrides: `Listbox.focusAndScrollOptionIntoView`
*/
protected focusAndScrollOptionIntoView(): void {
if (this.contains(document.activeElement)) {
if (this.contains(getRootActiveElement(this))) {
this.control.focus();
if (this.firstSelectedOption) {
requestAnimationFrame(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
keyPageUp,
} from "@microsoft/fast-web-utilities";
import { isFocusable } from "tabbable";
import { getRootActiveElement } from "../utilities/index.js";
import type { ColumnDefinition } from "./data-grid.js";
import { DataGridCellTypes } from "./data-grid.options.js";

Expand Down Expand Up @@ -200,7 +201,8 @@ export class FASTDataGridCell extends FASTElement {
}

public handleFocusout(e: FocusEvent): void {
if (this !== document.activeElement && !this.contains(document.activeElement)) {
const activeElement: Element | null = getRootActiveElement(this);
if (this !== activeElement && !this.contains(activeElement)) {
this.isActiveCell = false;
}
}
Expand Down Expand Up @@ -230,7 +232,7 @@ export class FASTDataGridCell extends FASTElement {
return;
}

const rootActiveElement: Element | null = this.getRootActiveElement();
const rootActiveElement: Element | null = getRootActiveElement(this);

switch (e.key) {
case keyEnter:
Expand Down Expand Up @@ -292,16 +294,6 @@ export class FASTDataGridCell extends FASTElement {
}
}

private getRootActiveElement(): Element | null {
const rootNode = this.getRootNode();

if (rootNode instanceof ShadowRoot) {
return rootNode.activeElement;
}

return document.activeElement;
}

private updateCellView(): void {
this.disconnectCellView();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
keyPageDown,
keyPageUp,
} from "@microsoft/fast-web-utilities";
import { getRootActiveElement } from "../utilities/index.js";
import type { FASTDataGridCell } from "./data-grid-cell.js";
import type { FASTDataGridRow } from "./data-grid-row.js";
import {
Expand Down Expand Up @@ -173,12 +174,10 @@ export class FASTDataGrid extends FASTElement {
if (this.noTabbing) {
this.setAttribute("tabIndex", "-1");
} else {
const activeElement: Element | null = getRootActiveElement(this);
this.setAttribute(
"tabIndex",
this.contains(document.activeElement) ||
this === document.activeElement
? "-1"
: "0"
this.contains(activeElement) || this === activeElement ? "-1" : "0"
);
}
}
Expand Down Expand Up @@ -908,9 +907,10 @@ export class FASTDataGrid extends FASTElement {
};

private queueFocusUpdate(): void {
const activeElement: Element | null = getRootActiveElement(this);
if (
this.isUpdatingFocus &&
(this.contains(document.activeElement) || this === document.activeElement)
(this.contains(activeElement) || this === activeElement)
) {
return;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/web-components/fast-foundation/src/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@microsoft/fast-element";
import { keyEscape, keyTab } from "@microsoft/fast-web-utilities";
import { isTabbable } from "tabbable";
import { getRootActiveElement } from "../utilities/index.js";

/**
* A Switch Custom HTML Element.
Expand Down Expand Up @@ -274,7 +275,7 @@ export class FASTDialog extends FASTElement {
// Add an event listener for focusin events if we are trapping focus
document.addEventListener("focusin", this.handleDocumentFocus);
Updates.enqueue(() => {
if (this.shouldForceFocus(document.activeElement)) {
if (this.shouldForceFocus(getRootActiveElement(this))) {
this.focusFirstElement();
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
keyTab,
uniqueId,
} from "@microsoft/fast-web-utilities";
import { getRootActiveElement } from "../utilities/index.js";
import { FASTListboxOption, isListboxOption } from "../listbox-option/listbox-option.js";
import { ARIAGlobalStatesAndProperties } from "../patterns/index.js";
import { applyMixins } from "../utilities/apply-mixins.js";
Expand Down Expand Up @@ -198,7 +199,7 @@ export abstract class FASTListbox extends FASTElement {
// function is typically called from the `openChanged` observer, `DOM.queueUpdate`
// causes the calls to be grouped into the same frame. To prevent this,
// `requestAnimationFrame` is used instead of `DOM.queueUpdate`.
if (this.contains(document.activeElement) && optionToFocus !== null) {
if (this.contains(getRootActiveElement(this)) && optionToFocus !== null) {
optionToFocus.focus();
requestAnimationFrame(() => {
optionToFocus.scrollIntoView({ block: "nearest" });
Expand Down Expand Up @@ -409,7 +410,7 @@ export abstract class FASTListbox extends FASTElement {
* @internal
*/
public mousedownHandler(e: MouseEvent): boolean | void {
this.shouldSkipFocus = !this.contains(document.activeElement);
this.shouldSkipFocus = !this.contains(getRootActiveElement(this));
return true;
}

Expand Down
19 changes: 5 additions & 14 deletions packages/web-components/fast-foundation/src/picker/picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
FlyoutPosTop,
FlyoutPosTopFill,
} from "../anchored-region/index.js";
import { getRootActiveElement } from "../utilities/index.js";
import { FASTPickerListItem } from "./picker-list-item.js";
import type { FASTPickerList } from "./picker-list.js";
import { FASTPickerMenuOption } from "./picker-menu-option.js";
Expand Down Expand Up @@ -556,7 +557,7 @@ export class FASTPicker extends FormAssociatedPicker {
return;
}

if (open && this.getRootActiveElement() === this.inputElement) {
if (open && getRootActiveElement(this) === this.inputElement) {
this.flyoutOpen = open;
Updates.enqueue(() => {
if (this.menuElement !== undefined) {
Expand Down Expand Up @@ -605,7 +606,7 @@ export class FASTPicker extends FormAssociatedPicker {
if (e.defaultPrevented) {
return false;
}
const activeElement = this.getRootActiveElement();
const activeElement = getRootActiveElement(this);
switch (e.key) {
// TODO: what should "home" and "end" keys do, exactly?
//
Expand Down Expand Up @@ -811,7 +812,7 @@ export class FASTPicker extends FormAssociatedPicker {
this.maxSelected !== 0 &&
this.selectedItems.length >= this.maxSelected
) {
if (this.getRootActiveElement() === this.inputElement) {
if (getRootActiveElement(this) === this.inputElement) {
const selectedItemInstances: Element[] = Array.from(
this.listElement.querySelectorAll("[role='listitem']")
);
Expand All @@ -825,16 +826,6 @@ export class FASTPicker extends FormAssociatedPicker {
}
}

private getRootActiveElement(): Element | null {
const rootNode = this.getRootNode();

if (rootNode instanceof ShadowRoot) {
return rootNode.activeElement;
}

return document.activeElement;
}

/**
* A list item has been invoked.
*/
Expand Down Expand Up @@ -901,7 +892,7 @@ export class FASTPicker extends FormAssociatedPicker {
this.listElement.querySelectorAll("[role='listitem']")
);

const activeElement = this.getRootActiveElement();
const activeElement = getRootActiveElement(this);
if (activeElement !== null) {
let currentFocusedItemIndex: number =
selectedItemsAsElements.indexOf(activeElement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Updates,
} from "@microsoft/fast-element";
import { keyEscape, uniqueId } from "@microsoft/fast-web-utilities";
import { getRootActiveElement } from "../utilities/index.js";
import { TooltipPlacement } from "./tooltip.options.js";

/**
Expand Down Expand Up @@ -171,7 +172,7 @@ export class FASTTooltip extends FASTElement {
* @internal
*/
private mouseoverAnchorHandler = (): void => {
if (!document.activeElement?.isSameNode(this.anchorElement)) {
if (!getRootActiveElement(this)?.isSameNode(this.anchorElement)) {
this.showTooltip();
}
};
Expand All @@ -183,7 +184,7 @@ export class FASTTooltip extends FASTElement {
*/
private mouseoutAnchorHandler = (e: MouseEvent): void => {
if (
!document.activeElement?.isSameNode(this.anchorElement) &&
!getRootActiveElement(this)?.isSameNode(this.anchorElement) &&
!this.isSameNode(e.relatedTarget as HTMLElement)
) {
this.hideTooltip();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export {
export { ValuesOf } from "./typings.js";
export { whitespaceFilter } from "./whitespace-filter.js";
export { staticallyCompose, StaticallyComposableHTML } from "./template-helpers.js";
export { getRootActiveElement } from "./root-active-element.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// returns the active element in the shadow context of the element in question.
export function getRootActiveElement(element: Element): Element | null {
const rootNode = element.getRootNode();

if (rootNode instanceof ShadowRoot) {
return rootNode.activeElement;
}

return document.activeElement;
}

0 comments on commit d80f077

Please sign in to comment.