UNPKG

@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

122 lines • 8.98 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useRef } from 'react'; import clsx from 'clsx'; import { useComponentMetadata, useMergeRefs, useUniqueId, warnOnce, } from '@awsui/component-toolkit/internal'; import { getAnalyticsMetadataAttribute } from '@awsui/component-toolkit/internal/analytics-metadata'; import { ActionsWrapper } from '../alert/actions-wrapper'; import { InternalButton } from '../button/internal'; import InternalIcon from '../icon/internal'; import { DATA_ATTR_ANALYTICS_FLASHBAR, DATA_ATTR_ANALYTICS_SUPPRESS_FLOW_EVENTS, } from '../internal/analytics/selectors'; import { getVisualContextClassname } from '../internal/components/visual-context'; import { PACKAGE_VERSION } from '../internal/environment'; import { isDevelopment } from '../internal/is-development'; import { awsuiPluginsInternal } from '../internal/plugins/api'; import { createUseDiscoveredAction, createUseDiscoveredContent } from '../internal/plugins/helpers'; import useContainerWidth from '../internal/utils/use-container-width'; import InternalLiveRegion from '../live-region/internal'; import InternalSpinner from '../spinner/internal'; import { getDismissButtonStyles, getFlashStyles } from './style'; import analyticsSelectors from './analytics-metadata/styles.css.js'; import styles from './styles.css.js'; const ICON_TYPES = { success: 'status-positive', warning: 'status-warning', info: 'status-info', error: 'status-negative', 'in-progress': 'status-in-progress', }; const useDiscoveredAction = createUseDiscoveredAction(awsuiPluginsInternal.flashbar.onActionRegistered); const useDiscoveredContent = createUseDiscoveredContent('flash', awsuiPluginsInternal.flashContent); function dismissButton(dismissLabel, onDismiss, style, type, ref, id, onDismissed, persistenceConfig) { return (React.createElement("div", { className: styles['dismiss-button-wrapper'], ...getAnalyticsMetadataAttribute({ action: 'dismiss' }) }, React.createElement(InternalButton, { ref: ref, onClick: event => { if (onDismiss) { onDismiss(event); } if (onDismissed) { onDismissed(id, persistenceConfig); } }, className: styles['dismiss-button'], variant: "flashbar-icon", iconName: "close", formAction: "none", ariaLabel: dismissLabel, style: getDismissButtonStyles(style, type) }))); } export const focusFlashFocusableArea = (flash) => { if (!flash) { return; } const dismissButton = flash.querySelector(`.${styles['dismiss-button']}`); const focusContainer = flash.querySelector(`.${styles['flash-focus-container']}`); if (dismissButton) { dismissButton.focus(); } else if (focusContainer) { focusContainer.focus(); } }; export function focusFlashById(element, itemId) { if (!element) { return; } const flashElement = element.querySelector(`[data-itemid="${CSS.escape(itemId)}"]`); if (!flashElement) { return; } const focusContainer = flashElement.querySelector(`.${styles['flash-focus-container']}`); focusContainer === null || focusContainer === void 0 ? void 0 : focusContainer.focus(); } export const Flash = React.forwardRef(({ id, header, content, dismissible, dismissLabel, loading, action, buttonText, onButtonClick, onDismiss, className, transitionState, ariaRole, i18nStrings, type = 'info', analyticsMetadata, style, rootRef, onDismissed, persistenceConfig, ...props }, ref) => { if (isDevelopment) { if (buttonText && !onButtonClick) { warnOnce('Flashbar', `You provided a \`buttonText\` prop without an \`onButtonClick\` handler. This will render a non-interactive action button.`); } if (dismissible && !onDismiss) { warnOnce('Flashbar', `You have set the \`dismissible\` prop without an \`onDismiss\` handler. This will render a non-interactive dismiss button.`); } } const [containerWidth, containerMeasureRef] = useContainerWidth(); const elementRef = useComponentMetadata('Flash', PACKAGE_VERSION, analyticsMetadata); // Merge all refs including the rootRef if provided const mergedRef = useMergeRefs(ref, rootRef, elementRef, containerMeasureRef); const flashIconId = useUniqueId('flash-icon'); const flashMessageId = useUniqueId('flash-message'); const headerRefObject = useRef(null); const contentRefObject = useRef(null); const dismissButtonRefObject = useRef(null); const { discoveredActions, headerRef: headerRefAction, contentRef: contentRefAction } = useDiscoveredAction(type); const { initialHidden, headerReplacementType, contentReplacementType, headerRef: headerRefContent, contentRef: contentRefContent, replacementHeaderRef, replacementContentRef, } = useDiscoveredContent({ type, header, children: content }); const headerRef = useMergeRefs(headerRefAction, headerRefContent, headerRefObject); const contentRef = useMergeRefs(contentRefAction, contentRefContent, contentRefObject); const statusIconAriaLabel = props.statusIconAriaLabel || (i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings[`${loading || type === 'in-progress' ? 'inProgress' : type}IconAriaLabel`]); const iconType = ICON_TYPES[type]; const icon = loading ? (React.createElement("span", { role: "img", "aria-label": statusIconAriaLabel }, React.createElement(InternalSpinner, null))) : (React.createElement(InternalIcon, { name: iconType, ariaLabel: statusIconAriaLabel })); const effectiveType = loading ? 'info' : type; const analyticsAttributes = { [DATA_ATTR_ANALYTICS_FLASHBAR]: effectiveType }; if (analyticsMetadata === null || analyticsMetadata === void 0 ? void 0 : analyticsMetadata.suppressFlowMetricEvents) { analyticsAttributes[DATA_ATTR_ANALYTICS_SUPPRESS_FLOW_EVENTS] = 'true'; } return ( // We're not using "polite" or "assertive" here, just turning default behavior off. // eslint-disable-next-line @awsui/components-react/prefer-live-region React.createElement("div", { ref: mergedRef, role: ariaRole, "aria-live": ariaRole ? 'off' : undefined, "data-itemid": id, className: clsx(styles.flash, styles[`flash-type-${effectiveType}`], className, transitionState && { [styles.enter]: transitionState === 'enter', [styles.entering]: transitionState === 'entering', [styles.entered]: transitionState === 'entered', [styles.exit]: transitionState === 'exit', [styles.exiting]: transitionState === 'exiting', [styles.exited]: transitionState === 'exited', }, getVisualContextClassname(type === 'warning' && !loading ? 'flashbar-warning' : 'flashbar'), initialHidden && styles['initial-hidden']), style: getFlashStyles(style, effectiveType), ...analyticsAttributes }, React.createElement("div", { className: styles['flash-body'] }, React.createElement("div", { className: styles['flash-focus-container'], tabIndex: -1, role: "group", "aria-labelledby": `${flashIconId} ${flashMessageId}` }, React.createElement("div", { className: clsx(styles['flash-icon'], styles['flash-text']), id: flashIconId }, icon), React.createElement("div", { className: clsx(styles['flash-message'], styles['flash-text']), id: flashMessageId }, React.createElement("div", { className: clsx(styles['flash-header'], headerReplacementType !== 'original' ? styles.hidden : analyticsSelectors['flash-header']), ref: headerRef }, header), React.createElement("div", { className: clsx(styles['header-replacement'], headerReplacementType !== 'replaced' && styles.hidden), ref: replacementHeaderRef }), React.createElement("div", { className: clsx(styles['flash-content'], contentReplacementType !== 'original' ? styles.hidden : analyticsSelectors['flash-header']), ref: contentRef }, content), React.createElement("div", { className: clsx(styles['content-replacement'], contentReplacementType !== 'replaced' && styles.hidden), ref: replacementContentRef }))), React.createElement(ActionsWrapper, { className: styles['action-button-wrapper'], testUtilClasses: { actionSlot: styles['action-slot'], actionButton: styles['action-button'] }, action: action, discoveredActions: discoveredActions, buttonText: buttonText, onButtonClick: onButtonClick, containerWidth: containerWidth, wrappedClass: styles['action-wrapped'] })), dismissible && dismissButton(dismissLabel, onDismiss, style, effectiveType, dismissButtonRefObject, id, onDismissed, persistenceConfig), ariaRole === 'status' && (React.createElement(InternalLiveRegion, { sources: [statusIconAriaLabel, headerRefObject, contentRefObject] })))); }); //# sourceMappingURL=flash.js.map