UNPKG

@ui5/webcomponents-react

Version:

React Wrapper for UI5 Web Components and additional components

195 lines (193 loc) 7.4 kB
'use client'; import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; import { isPhone } from '@ui5/webcomponents-base/dist/Device.js'; import { useI18nBundle, useStylesheet } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import { forwardRef, useEffect, useReducer, useRef, useState } from 'react'; import { AVAILABLE_ACTIONS, CANCEL, X_OF_Y } from '../../i18n/i18n-defaults.js'; import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js'; import { flattenFragments, getUi5TagWithSuffix } from '../../internal/utils.js'; import { CustomThemingParameters } from '../../themes/CustomVariables.js'; import { Button, ResponsivePopover } from '../../webComponents/index.js'; import { classNames, styleData } from './ActionSheet.module.css.js'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; if (isPhone()) { addCustomCSSWithScoping('ui5-responsive-popover', ` :host([data-actionsheet]) [ui5-button] { display: none; } :host([data-actionsheet]) [ui5-dialog] { top: auto !important; bottom: 0; height: auto; border-radius: ${CustomThemingParameters.ActionSheetMobileHeaderBorderRadius}; background-color: ${CustomThemingParameters.ActionSheetMobileHeaderBackground}; box-shadow: ${CustomThemingParameters.ActionSheetMobileHeaderBoxShadow}; box-sizing: border-box; min-height: unset; } :host([data-actionsheet]) [ui5-title] { color: ${CustomThemingParameters.ActionSheetMobileHeaderTextColor} !important; text-shadow: none; text-align: start !important; } `); } function ActionSheetButton(props) { const { index, totalLength, ...buttonProps } = props; const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); return /*#__PURE__*/_jsx(Button // aria-describedby={ariaTextId} , { accessibleName: `${buttonProps.children} ${i18nBundle.getText(X_OF_Y, index + 1, totalLength)}`, ...buttonProps, design: ButtonDesign.Transparent, "data-action-btn-index": index }); // const id = useIsomorphicId(); // const ariaTextId = `__button${id}-actionSheetHiddenText`; // <span id={ariaTextId} aria-hidden="true" className={classes.hiddenText}> // {i18nBundle.getText(X_OF_Y, index + 1, totalLength)} // </span> } /** * The `ActionSheet` holds a list of buttons from which the user can select to complete an action. * * The children of the action sheet should be `Button` components. Elements in the `ActionSheet` are start-aligned. Actions should be arranged in order of importance, from top to bottom. * * ### Guidelines * - Always display text or text and icons for the actions. Do not use icons only. * - Always provide a Cancel button on mobile phones. * - Avoid scrolling on action sheets. * */ const ActionSheet = /*#__PURE__*/forwardRef((props, ref) => { const { accessibilityAttributes, children, className, header, headerText, hideCancelButton, onOpen, open, ...rest } = props; useStylesheet(styleData, ActionSheet.displayName); const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); const actionBtnsRef = useRef(null); const [focusedItem, setFocusedItem] = useReducer((_, action) => { return parseInt(action.target.dataset.actionBtnIndex); }, 0); const childrenToRender = flattenFragments(children); const childrenArrayLength = childrenToRender.length; const childrenLength = isPhone() && !hideCancelButton ? childrenArrayLength + 1 : childrenArrayLength; const [internalOpen, setInternalOpen] = useState(undefined); useEffect(() => { const tagName = getUi5TagWithSuffix('ui5-responsive-popover'); void customElements.whenDefined(tagName).then(() => { setInternalOpen(open); }); }, [open]); const handleCancelBtnClick = () => { setInternalOpen(false); }; const renderActionSheetButton = (element, index, childrenArray) => { return /*#__PURE__*/_jsx(ActionSheetButton, { index: index, totalLength: childrenArray.length, tabIndex: focusedItem === index ? 0 : -1, ...element.props, onClick: e => { setInternalOpen(false); if (typeof element.props?.onClick === 'function') { element.props?.onClick(e); } }, onFocus: e => { if (typeof element.props?.onFocus === 'function') { element.props?.onFocus(e); } setFocusedItem(e); } }, index); }; const handleAfterOpen = e => { if (isPhone()) { actionBtnsRef.current.querySelector(`[data-action-btn-index="${focusedItem}"]`).focus(); } if (typeof onOpen === 'function') { onOpen(e); } }; const handleKeyDown = e => { const currentIndex = parseInt(e.target.dataset.actionBtnIndex); const isRtl = actionBtnsRef.current?.matches(':dir(rtl)'); switch (e.key) { case 'ArrowDown': case isRtl ? 'ArrowLeft' : 'ArrowRight': if (currentIndex + 1 < childrenLength) { e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="${currentIndex + 1}"]`).focus(); } break; case 'ArrowUp': case isRtl ? 'ArrowRight' : 'ArrowLeft': if (currentIndex > 0) { e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="${currentIndex - 1}"]`).focus(); } break; case 'PageUp': e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="${Math.max(currentIndex - 5, 0)}"]`).focus(); break; case 'PageDown': e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="${Math.min(currentIndex + 5, childrenLength - 1)}"]`).focus(); break; case 'Home': e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="0"]`).focus(); break; case 'End': e.preventDefault(); actionBtnsRef.current.querySelector(`[data-action-btn-index="${childrenLength - 1}"]`).focus(); break; } }; const displayHeader = isPhone(); return /*#__PURE__*/_jsx(ResponsivePopover, { open: internalOpen, headerText: displayHeader ? headerText : undefined, header: displayHeader ? header : undefined, accessibleName: i18nBundle.getText(AVAILABLE_ACTIONS), ...rest, onOpen: handleAfterOpen, ref: ref, className: clsx(classNames.actionSheet, isPhone() && classNames.actionSheetMobile, className), "data-actionsheet": true, children: /*#__PURE__*/_jsxs("div", { className: isPhone() ? classNames.contentMobile : undefined, "data-component-name": "ActionSheetMobileContent", role: accessibilityAttributes?.actionSheetMobileContent?.role ?? 'application', onKeyDown: handleKeyDown, ref: actionBtnsRef, children: [childrenToRender.map(renderActionSheetButton), isPhone() && !hideCancelButton && /*#__PURE__*/_jsx(Button, { design: ButtonDesign.Negative, onClick: handleCancelBtnClick, tabIndex: focusedItem === childrenLength - 1 ? 0 : -1, "data-action-btn-index": childrenLength - 1, "data-cancel-btn": true, onFocus: setFocusedItem, children: i18nBundle.getText(CANCEL) })] }) }); }); ActionSheet.displayName = 'ActionSheet'; export { ActionSheet };