@ui5/webcomponents-react
Version:
React Wrapper for UI5 Web Components and additional components
195 lines (193 loc) • 7.4 kB
JavaScript
'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 };