UNPKG

@fluentui/react-northstar

Version:
433 lines (430 loc) 18.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.carouselSlotClassNames = exports.carouselClassName = exports.Carousel = void 0; var _invoke2 = _interopRequireDefault(require("lodash/invoke")); var _debounce2 = _interopRequireDefault(require("lodash/debounce")); var customPropTypes = _interopRequireWildcard(require("@fluentui/react-proptypes")); var _accessibility = require("@fluentui/accessibility"); var React = _interopRequireWildcard(require("react")); var PropTypes = _interopRequireWildcard(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _reactComponentRef = require("@fluentui/react-component-ref"); var _Animation = require("../Animation/Animation"); var _utils = require("../../utils"); var _CarouselItem = require("./CarouselItem"); var _Text = require("../Text/Text"); var _CarouselNavigation = require("./CarouselNavigation"); var _CarouselNavigationItem = require("./CarouselNavigationItem"); var _CarouselPaddle = require("./CarouselPaddle"); var _reactBindings = require("@fluentui/react-bindings"); var _state = require("@fluentui/state"); var _CarouselPaddlesContainer = require("./CarouselPaddlesContainer"); var _utils2 = require("./utils"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var carouselClassName = 'ui-carousel'; exports.carouselClassName = carouselClassName; var carouselSlotClassNames = { itemsContainer: carouselClassName + "__itemscontainer", paddleNext: carouselClassName + "__paddlenext", paddlePrevious: carouselClassName + "__paddleprevious", pagination: carouselClassName + "__pagination", navigation: carouselClassName + "__navigation" }; exports.carouselSlotClassNames = carouselSlotClassNames; function useDirection(activeIndex, circular, itemsLength) { var prevActiveIndex = React.useRef(activeIndex); React.useEffect(function () { prevActiveIndex.current = activeIndex; }, [activeIndex]); var direction = React.useMemo(function () { if (circular) { if (activeIndex === 0 && prevActiveIndex.current === itemsLength - 1) { return 'start'; } if (activeIndex === itemsLength - 1 && prevActiveIndex.current === 0) { return 'end'; } } if (activeIndex > prevActiveIndex.current) { return 'start'; } if (activeIndex < prevActiveIndex.current) { return 'end'; } return undefined; }, [activeIndex, circular, itemsLength]); return direction; } /** * A Carousel displays data organised as a gallery. * * @accessibility * Implements [ARIA Carousel](https://www.w3.org/WAI/tutorials/carousels/structure/) design pattern. * @accessibilityIssues * [VoiceOver doens't narrate label referenced by aria-labelledby attribute, when role is "tabpanel"](https://bugs.chromium.org/p/chromium/issues/detail?id=1040924) */ var Carousel = /*#__PURE__*/React.forwardRef(function (props, ref) { var context = (0, _reactBindings.useFluentContext)(); var _useTelemetry = (0, _reactBindings.useTelemetry)(Carousel.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var accessibility = props.accessibility, items = props.items, circular = props.circular, getItemPositionText = props.getItemPositionText, paddlePrevious = props.paddlePrevious, paddleNext = props.paddleNext, navigation = props.navigation, thumbnails = props.thumbnails, children = props.children, ariaRoleDescription = props['aria-roledescription'], ariaLabel = props['aria-label'], className = props.className, design = props.design, styles = props.styles, variables = props.variables, disableClickableNav = props.disableClickableNav, animationEnterFromPrev = props.animationEnterFromPrev, animationEnterFromNext = props.animationEnterFromNext, animationExitToPrev = props.animationExitToPrev, animationExitToNext = props.animationExitToNext; var ElementType = (0, _reactBindings.getElementType)(props); var _useStateManager = (0, _reactBindings.useStateManager)(_state.createCarouselManager, { mapPropsToInitialState: function mapPropsToInitialState() { return { activeIndex: props.defaultActiveIndex }; }, mapPropsToState: function mapPropsToState() { return { activeIndex: props.activeIndex }; } }), state = _useStateManager.state, actions = _useStateManager.actions; var ariaLiveOn = state.ariaLiveOn, shouldFocusContainer = state.shouldFocusContainer, isFromKeyboard = state.isFromKeyboard, activeIndex = state.activeIndex; var dir = useDirection(activeIndex, circular, items == null ? void 0 : items.length); var itemRefs = React.useMemo(function () { return Array.from({ length: items == null ? void 0 : items.length }, function () { return /*#__PURE__*/React.createRef(); }); }, // As we are using "panels.length" it's fine to have dependency on them // eslint-disable-next-line react-hooks/exhaustive-deps [items == null ? void 0 : items.length]); var nextPaddleHidden = items !== undefined && !circular && activeIndex === items.length - 1; var previousPaddleHidden = items !== undefined && !circular && activeIndex === 0; var unhandledProps = (0, _reactBindings.useUnhandledProps)(Carousel.handledProps, props); var getA11yProps = (0, _reactBindings.useAccessibility)(accessibility, { debugName: Carousel.displayName, actionHandlers: { showNextSlideByKeyboardNavigation: function showNextSlideByKeyboardNavigation(e) { e.preventDefault(); showNextSlide(e, true); }, showPreviousSlideByKeyboardNavigation: function showPreviousSlideByKeyboardNavigation(e) { e.preventDefault(); showPreviousSlide(e, true); }, showNextSlideByPaddlePress: function showNextSlideByPaddlePress(e) { e.preventDefault(); showNextSlide(e, false); handleNextPaddleFocus(); }, showPreviousSlideByPaddlePress: function showPreviousSlideByPaddlePress(e) { e.preventDefault(); showPreviousSlide(e, false); handlePreviousPaddleFocus(); } }, mapPropsToBehavior: function mapPropsToBehavior() { return { paddlePreviousHidden: previousPaddleHidden, paddleNextHidden: nextPaddleHidden, navigation: navigation, ariaLiveOn: ariaLiveOn, 'aria-roledescription': ariaRoleDescription, 'aria-label': ariaLabel }; } }); var _useStyles = (0, _reactBindings.useStyles)(Carousel.displayName, { className: carouselClassName, mapPropsToStyles: function mapPropsToStyles() { return { shouldFocusContainer: shouldFocusContainer, isFromKeyboard: isFromKeyboard }; }, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: variables }; }, rtl: context.rtl }), classes = _useStyles.classes; var paddleNextRef = React.useRef(); var paddlePreviousRef = React.useRef(); var focusItemAtIndex = React.useMemo(function () { return (0, _debounce2.default)(function (index) { var _itemRefs$index$curre; (_itemRefs$index$curre = itemRefs[index].current) == null ? void 0 : _itemRefs$index$curre.focus(); }, 400); }, [itemRefs]); React.useEffect(function () { return function () { focusItemAtIndex.cancel(); }; }, [focusItemAtIndex, items]); var setActiveIndex = function setActiveIndex(e, index, focusItem) { var lastItemIndex = items.length - 1; var nextActiveIndex = index; if (index < 0) { if (!circular) { return; } nextActiveIndex = lastItemIndex; } if (index > lastItemIndex) { if (!circular) { return; } nextActiveIndex = 0; } actions.setIndexes(nextActiveIndex); (0, _invoke2.default)(props, 'onActiveIndexChange', e, Object.assign({}, props, { activeIndex: index })); if (focusItem) { focusItemAtIndex(nextActiveIndex); } }; var overrideItemProps = function overrideItemProps(predefinedProps) { return { onFocus: function onFocus(e, itemProps) { actions.setShouldFocusContainer(e.currentTarget === e.target); actions.setIsFromKeyboard((0, _utils.isFromKeyboard)()); (0, _invoke2.default)(predefinedProps, 'onFocus', e, itemProps); }, onBlur: function onBlur(e, itemProps) { actions.setShouldFocusContainer(e.currentTarget.contains(e.relatedTarget)); actions.setIsFromKeyboard(false); (0, _invoke2.default)(predefinedProps, 'onBlur', e, itemProps); } }; }; var renderContent = function renderContent() { return /*#__PURE__*/React.createElement("div", getA11yProps('itemsContainerWrapper', { className: classes.itemsContainerWrapper }), /*#__PURE__*/React.createElement("div", getA11yProps('itemsContainer', { className: (0, _classnames.default)(carouselSlotClassNames.itemsContainer, classes.itemsContainer) }), items && items.map(function (item, index) { var itemRef = itemRefs[index]; var active = activeIndex === index; var animationName = (0, _utils2.getAnimationName)({ active: active, dir: dir, animationEnterFromPrev: animationEnterFromPrev, animationEnterFromNext: animationEnterFromNext, animationExitToPrev: animationExitToPrev, animationExitToNext: animationExitToNext }); return /*#__PURE__*/React.createElement(_Animation.Animation, { visible: active, key: item['key'] || index, mountOnEnter: true, unmountOnExit: true, name: animationName }, /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: itemRef }, _CarouselItem.CarouselItem.create(item, { defaultProps: function defaultProps() { return Object.assign({ active: active, navigation: !!navigation }, getItemPositionText && { itemPositionText: getItemPositionText(index, items.length) }); }, overrideProps: overrideItemProps }))); }))); }; var handleNextPaddleFocus = function handleNextPaddleFocus() { // if 'next' paddle will disappear, will focus 'previous' one. if (!navigation && activeIndex >= props.items.length - 2 && !circular) { paddlePreviousRef.current.focus(); } }; var handlePreviousPaddleFocus = function handlePreviousPaddleFocus() { // if 'previous' paddle will disappear, will focus 'next' one. if (!navigation && activeIndex <= 1 && !circular) { paddleNextRef.current.focus(); } }; var showPreviousSlide = function showPreviousSlide(e, focusItem) { setActiveIndex(e, +activeIndex - 1, focusItem); }; var showNextSlide = function showNextSlide(e, focusItem) { setActiveIndex(e, +activeIndex + 1, focusItem); }; var handlePaddleOverrides = function handlePaddleOverrides(predefinedProps, paddleName) { return { variables: (0, _reactBindings.mergeVariablesOverrides)(variables, predefinedProps.variables), onClick: function onClick(e, paddleProps) { if (disableClickableNav) return; (0, _invoke2.default)(predefinedProps, 'onClick', e, paddleProps); if (paddleName === 'paddleNext') { showNextSlide(e, false); handleNextPaddleFocus(); } else if (paddleName === 'paddlePrevious') { showPreviousSlide(e, false); handlePreviousPaddleFocus(); } }, onBlur: function onBlur(e, paddleProps) { if (e.relatedTarget !== paddleNextRef.current) { actions.setAriaLiveOn(false); } }, onFocus: function onFocus(e, paddleProps) { (0, _invoke2.default)(predefinedProps, 'onFocus', e, paddleProps); actions.setAriaLiveOn(true); } }; }; var paddles = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: paddlePreviousRef }, _CarouselPaddle.CarouselPaddle.create(paddlePrevious, { defaultProps: function defaultProps() { return getA11yProps('paddlePrevious', { className: carouselSlotClassNames.paddlePrevious, previous: true, hidden: previousPaddleHidden, disableClickableNav: disableClickableNav }); }, overrideProps: function overrideProps(predefinedProps) { return handlePaddleOverrides(predefinedProps, 'paddlePrevious'); } })), /*#__PURE__*/React.createElement(_reactComponentRef.Ref, { innerRef: paddleNextRef }, _CarouselPaddle.CarouselPaddle.create(paddleNext, { defaultProps: function defaultProps() { return getA11yProps('paddleNext', { className: carouselSlotClassNames.paddleNext, next: true, hidden: nextPaddleHidden, disableClickableNav: disableClickableNav }); }, overrideProps: function overrideProps(predefinedProps) { return handlePaddleOverrides(predefinedProps, 'paddleNext'); } }))); var renderPaddles = function renderPaddles() { return (0, _utils.createShorthand)(_CarouselPaddlesContainer.CarouselPaddlesContainer, {}, { overrideProps: function overrideProps() { return { children: paddles }; } }); }; var renderNavigation = function renderNavigation() { if (!items || !items.length) { return null; } return navigation ? _CarouselNavigation.CarouselNavigation.create(navigation, { defaultProps: function defaultProps() { return { className: carouselSlotClassNames.navigation, iconOnly: true, activeIndex: activeIndex, thumbnails: thumbnails, disableClickableNav: disableClickableNav }; }, overrideProps: function overrideProps(predefinedProps) { return { onItemClick: function onItemClick(e, itemProps) { if (disableClickableNav) return; var index = itemProps.index; setActiveIndex(e, index, true); (0, _invoke2.default)(predefinedProps, 'onClick', e, itemProps); } }; } }) : getItemPositionText ? /*#__PURE__*/React.createElement(_Text.Text, { "aria-hidden": "true", align: "center", as: "div", className: carouselSlotClassNames.pagination, content: getItemPositionText(+activeIndex, items.length) }) : null; }; var element = /*#__PURE__*/React.createElement(ElementType, getA11yProps('root', Object.assign({ className: classes.root, ref: ref }, unhandledProps)), (0, _utils.childrenExist)(children) ? children : /*#__PURE__*/React.createElement(React.Fragment, null, renderPaddles(), renderContent(), renderNavigation())); setEnd(); return element; }); exports.Carousel = Carousel; Carousel.displayName = 'Carousel'; Carousel.propTypes = Object.assign({}, _utils.commonPropTypes.createCommon({ content: false }), { activeIndex: PropTypes.number, 'aria-roledescription': PropTypes.string, 'aria-label': PropTypes.string, circular: PropTypes.bool, defaultActiveIndex: PropTypes.number, getItemPositionText: PropTypes.func, items: customPropTypes.collectionShorthand, navigation: PropTypes.oneOfType([customPropTypes.collectionShorthand, customPropTypes.itemShorthand]), navigationPosition: PropTypes.oneOf(['below', 'above', 'start', 'end']), onActiveIndexChange: PropTypes.func, paddleNext: customPropTypes.itemShorthand, paddlesPosition: PropTypes.oneOf(['inside', 'outside', 'inline']), paddlePrevious: customPropTypes.itemShorthand, thumbnails: PropTypes.bool, disableClickableNav: PropTypes.bool, animationEnterFromPrev: PropTypes.string, animationEnterFromNext: PropTypes.string, animationExitToPrev: PropTypes.string, animationExitToNext: PropTypes.string }); Carousel.defaultProps = { accessibility: _accessibility.carouselBehavior, paddlePrevious: {}, paddleNext: {}, animationEnterFromPrev: 'carousel-slide-to-previous-enter', animationEnterFromNext: 'carousel-slide-to-next-enter', animationExitToPrev: '', animationExitToNext: '' }; Carousel.Item = _CarouselItem.CarouselItem; Carousel.Navigation = _CarouselNavigation.CarouselNavigation; Carousel.NavigationItem = _CarouselNavigationItem.CarouselNavigationItem; Carousel.Paddle = _CarouselPaddle.CarouselPaddle; Carousel.PaddlesContainer = _CarouselPaddlesContainer.CarouselPaddlesContainer; Carousel.handledProps = Object.keys(Carousel.propTypes); Carousel.create = (0, _utils.createShorthandFactory)({ Component: Carousel, mappedArrayProp: 'items' }); //# sourceMappingURL=Carousel.js.map