@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
87 lines • 4.19 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import styles from './styles.css.js';
export default forwardRef(ApplicationController);
function ApplicationController({ activeElementKey, activeElementRef, onFocus, onBlur, onKeyDown }, ref) {
const containerRef = useRef(null);
const applicationRef = useRef(null);
const focusTransitionRef = useRef(false);
const [isFocused, setFocused] = useState(false);
// Calls provided onFocus handler when the application obtains focus, ignoring internal focus juggling.
const onApplicationFocus = useCallback((event) => {
if (focusTransitionRef.current === false) {
setFocused(true);
onFocus === null || onFocus === void 0 ? void 0 : onFocus(event);
}
else {
focusTransitionRef.current = false;
}
}, [onFocus]);
// Calls provided onBlur handler when the application loses focus, ignoring internal focus juggling.
const onApplicationBlur = useCallback((event) => {
if (focusTransitionRef.current === false) {
setFocused(false);
onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
// The application controller can only be focused programmatically.
muteApplication(applicationRef.current);
}
}, [onBlur]);
const onApplicationKeyDown = onKeyDown;
useImperativeHandle(ref, () => ({
focus: () => focusApplication(applicationRef.current, (activeElementRef === null || activeElementRef === void 0 ? void 0 : activeElementRef.current) || null),
}), [activeElementRef]);
// Re-attaches and re-focuses the application for screen readers to treat it as an update.
useEffect(() => {
// Skip if not focused or if the transition is already happening.
if (!isFocused || focusTransitionRef.current === true) {
return;
}
focusTransitionRef.current = true;
containerRef.current.removeChild(applicationRef.current);
containerRef.current.appendChild(applicationRef.current);
focusApplication(applicationRef.current, (activeElementRef === null || activeElementRef === void 0 ? void 0 : activeElementRef.current) || null);
}, [isFocused, activeElementKey, activeElementRef]);
return (React.createElement("g", { ref: containerRef },
React.createElement("g", { tabIndex: -1, ref: applicationRef, onFocus: onApplicationFocus, onBlur: onApplicationBlur, onKeyDown: onApplicationKeyDown, className: styles.application })));
}
// Focuses application but before copies aria-attributes from the target.
function focusApplication(app, target) {
// Remove prev attributes.
for (const attributeName of app.getAttributeNames()) {
if (attributeName === 'role' || attributeName.slice(0, 4) === 'aria') {
app.removeAttribute(attributeName);
}
}
// Copy new attributes.
if (target) {
for (const attributeName of target.getAttributeNames()) {
if (attributeName === 'role' || attributeName.slice(0, 4) === 'aria') {
const attributeValue = target.getAttribute(attributeName);
if (attributeValue) {
app.setAttribute(attributeName, attributeValue);
}
}
}
}
// Make app focusable.
app.tabIndex = 0;
app.setAttribute('focusable', 'true');
app.setAttribute('aria-hidden', 'false');
// Focus app.
app.focus({ preventScroll: true });
}
// The application is to be only focused programmatically.
function muteApplication(app) {
// Remove prev attributes.
for (const attributeName of app.getAttributeNames()) {
if (attributeName === 'role' || attributeName.slice(0, 4) === 'aria') {
app.removeAttribute(attributeName);
}
}
// Make app non-focusable.
app.tabIndex = -1;
app.setAttribute('focusable', 'false');
app.setAttribute('aria-hidden', 'true');
}
//# sourceMappingURL=application-controller.js.map