@fluentui/react-northstar
Version:
A themable React component library.
433 lines (430 loc) • 18.1 kB
JavaScript
"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