@salesforce/design-system-react
Version:
Salesforce Lightning Design System for React
529 lines (445 loc) • 20.8 kB
JavaScript
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */
/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */
// # Carousel Component
// Implements the [Carousel design pattern](https://www.lightningdesignsystem.com/components/carousel/) in React.
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { CAROUSEL } from '../../utilities/constants';
import generateId from '../../utilities/generate-id';
import { canUseDOM, canUseEventListeners } from '../../utilities/execution-environment';
import CarouselIndicators from './private/carousel-indicators';
import PreviousNextCarouselNavigator from './private/previous-next-carousel-navigator';
import CarouselItem from './private/carousel-item';
import AutoplayButton from './private/auto-play-button'; // ### Event Helpers
import KEYS from '../../utilities/key-code';
import EventUtil from '../../utilities/event';
/* eslint-disable jsx-a11y/no-static-element-interactions */
// ### Default Props
var defaultProps = {
assistiveText: {
autoplayButton: 'Start / Stop auto-play',
nextPanel: 'Next Panel',
previousPanel: 'Previous Panel'
},
autoplayInterval: 4000,
hasAutoplay: false,
hasPreviousNextPanelNavigation: false,
isInfinite: false,
itemsPerPanel: 1
};
/**
* A carousel allows multiple pieces of featured content to occupy an allocated amount of space.
* Currently panel index and auto play cannot be controlled by the app.
*/
var Carousel = /*#__PURE__*/function (_React$Component) {
_inherits(Carousel, _React$Component);
var _super = _createSuper(Carousel);
// ### Display Name
// Always use the canonical component name as the React display name.
// ### Prop Types
// ### Default Props
function Carousel(props) {
var _this;
_classCallCheck(this, Carousel);
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "onNextPanelHandler", function (event) {
var next = _this.getCurrentPanel() + 1;
if (next > _this.nrOfPanels - 1) {
next = 0;
}
_this.setCurrentPanel(event, next, _this.changeTranslationAutomatically);
});
_defineProperty(_assertThisInitialized(_this), "onPreviousPanelHandler", function (event) {
var prev = _this.getCurrentPanel() - 1;
if (prev < 0) {
prev = _this.nrOfPanels - 1;
}
_this.setCurrentPanel(event, prev, _this.changeTranslationAutomatically);
});
_defineProperty(_assertThisInitialized(_this), "onIndicatorBlur", function () {
_this.setState({
indicatorsHaveFocus: false
});
});
_defineProperty(_assertThisInitialized(_this), "onIndicatorClickHandler", function (event, panel) {
_this.setCurrentPanel(event, panel, _this.changeTranslationAutomatically);
_this.setState({
indicatorsHaveFocus: true
});
if (_this.getIsAutoplayOn()) {
_this.stopAutoplay(event);
}
});
_defineProperty(_assertThisInitialized(_this), "onIndicatorFocus", function (event) {
_this.setState({
indicatorsHaveFocus: true
});
if (_this.getIsAutoplayOn()) {
_this.stopAutoplay(event);
}
});
_defineProperty(_assertThisInitialized(_this), "onAutoplayBtnClick", function (event) {
var isAutoplayOn = _this.getIsAutoplayOn();
if (_this.props.onRequestAutoplayToggle) {
_this.props.onRequestAutoplayToggle(event, {
isAutoplayOn: isAutoplayOn
});
} else {
var actionToTake = isAutoplayOn ? _this.stopAutoplay : _this.startAutoplay;
_this.setState({
isAutoplayOn: !isAutoplayOn
});
actionToTake(event);
}
});
_defineProperty(_assertThisInitialized(_this), "getPanelId", function (_ref) {
var carouselId = _ref.carouselId,
itemId = _ref.itemId;
return "content-id-".concat(carouselId, "-").concat(itemId);
});
_defineProperty(_assertThisInitialized(_this), "setDimensions", function () {
if (canUseDOM && _this.stageItem !== undefined && _this.stageItem.current !== undefined && _this.stageItem.current.offsetWidth !== undefined) {
_this.setState({
stageWidth: _this.stageItem.current.offsetWidth
}, _this.changeTranslationAutomatically);
}
});
_defineProperty(_assertThisInitialized(_this), "setTranslationAmount", function (amount, cb) {
_this.setState({
translateX: amount
}, cb);
});
_defineProperty(_assertThisInitialized(_this), "setCurrentPanel", function (event, amount, cb) {
if (_this.props.onRequestPanelChange) {
_this.props.onRequestPanelChange(event, {
currentPanel: _this.getCurrentPanel(),
requestedPanel: amount
});
} else {
_this.setState({
currentPanel: amount
}, cb);
}
});
_defineProperty(_assertThisInitialized(_this), "startAutoplay", function (event) {
_this.autoplayId = setInterval(function () {
if (_this.canGoToNext()) {
_this.onNextPanelHandler(event);
} else if (_this.props.isInfinite) {
_this.setCurrentPanel(event, 0, _this.changeTranslationAutomatically);
} else {
_this.stopAutoplay(event);
}
}, _this.props.autoplayInterval);
});
_defineProperty(_assertThisInitialized(_this), "stopAutoplay", function (event, ignoreCallbacksAndStateUpdates) {
if (_this.autoplayId) {
clearInterval(_this.autoplayId);
}
if (!ignoreCallbacksAndStateUpdates) {
if (_this.props.onRequestAutoplayToggle) {
_this.props.onRequestAutoplayToggle(event, {
isAutoplayOn: _this.getIsAutoplayOn()
});
} else {
_this.setState({
isAutoplayOn: false
});
}
}
});
_defineProperty(_assertThisInitialized(_this), "changeTranslationAutomatically", function () {
_this.setTranslationAmount(-((_this.state.stageWidth || _this.stageWidth) * _this.getCurrentPanel()));
});
_defineProperty(_assertThisInitialized(_this), "canGoToNext", function () {
return _this.getCurrentPanel() < _this.nrOfPanels - 1;
});
_defineProperty(_assertThisInitialized(_this), "canGoToPrevious", function () {
return _this.getCurrentPanel() > 0;
});
_defineProperty(_assertThisInitialized(_this), "handleKeyDown", function (event) {
var _keyDownCallbacks;
var keyDownCallbacks = (_keyDownCallbacks = {}, _defineProperty(_keyDownCallbacks, KEYS.LEFT, function () {
if (_this.props.isInfinite || _this.canGoToPrevious()) {
_this.onPreviousPanelHandler(event);
_this.setState({
indicatorsHaveFocus: true
});
if (_this.getIsAutoplayOn()) {
_this.stopAutoplay(event);
}
}
}), _defineProperty(_keyDownCallbacks, KEYS.RIGHT, function () {
if (_this.props.isInfinite || _this.canGoToNext()) {
_this.onNextPanelHandler(event);
_this.setState({
indicatorsHaveFocus: true
});
if (_this.getIsAutoplayOn()) {
_this.stopAutoplay(event);
}
}
}), _keyDownCallbacks);
if (keyDownCallbacks[event.keyCode]) {
EventUtil.trapImmediate(event);
keyDownCallbacks[event.keyCode]();
}
});
_this.nrOfPanels = Math.ceil(props.items.length / props.itemsPerPanel);
_this.stageItem = /*#__PURE__*/React.createRef();
_this.state = {
currentPanel: props.currentPanel !== undefined ? props.currentPanel : 0,
indicatorsHaveFocus: false,
isAutoplayOn: props.isAutoplayOn !== undefined ? props.isAutoplayOn : props.hasAutoplay,
stageWidth: 0,
translateX: -1000000
};
_this.generatedId = generateId();
return _this;
}
_createClass(Carousel, [{
key: "componentDidMount",
value: function componentDidMount() {
if (canUseDOM && this.stageItem !== undefined && this.stageItem.current !== undefined && this.stageItem.current.offsetWidth !== undefined) {
this.stageWidth = this.stageItem.current.offsetWidth;
}
if (canUseEventListeners) {
window.addEventListener('resize', this.setDimensions, false);
}
this.changeTranslationAutomatically();
if (this.getIsAutoplayOn()) {
this.startAutoplay({
mountAutoplayEvent: true
});
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (this.props.currentPanel !== undefined && prevProps.currentPanel !== this.props.currentPanel) {
this.changeTranslationAutomatically();
}
if (this.props.isAutoplayOn !== undefined && prevProps.isAutoplayOn !== this.props.isAutoplayOn) {
if (this.props.isAutoplayOn) {
this.startAutoplay({
updateAutoplayEvent: true
});
} else {
this.stopAutoplay({
updateAutoplayEvent: true
}, true);
}
}
if (prevProps.items.length !== this.props.items.length || prevProps.itemsPerPanel !== this.props.itemsPerPanel) {
this.nrOfPanels = Math.ceil(this.props.items.length / this.props.itemsPerPanel);
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (canUseEventListeners) {
window.removeEventListener('resize', this.setDimensions, false);
}
this.stopAutoplay({
unmountAutoplayEvent: true
}, true);
}
}, {
key: "getCurrentPanel",
value: function getCurrentPanel() {
return this.props.currentPanel !== undefined ? this.props.currentPanel : this.state.currentPanel;
}
}, {
key: "getIsAutoplayOn",
value: function getIsAutoplayOn() {
return this.props.isAutoplayOn !== undefined ? this.props.isAutoplayOn : this.state.isAutoplayOn;
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props = this.props,
hasAutoplay = _this$props.hasAutoplay,
hasPreviousNextPanelNavigation = _this$props.hasPreviousNextPanelNavigation,
isInfinite = _this$props.isInfinite;
var currentPanel = this.getCurrentPanel();
var id = this.props.id || this.generatedId;
var isPreviousBtnDisabled = !(isInfinite || this.canGoToPrevious());
var isNextBtnDisabled = !(isInfinite || this.canGoToNext());
var itemWidth = (this.state.stageWidth || this.stageWidth) / this.props.itemsPerPanel;
var carouselMargins = hasPreviousNextPanelNavigation ? {
marginLeft: '44px',
marginRight: '44px'
} : {};
return /*#__PURE__*/React.createElement("div", {
className: classnames('slds-carousel', this.props.className),
id: id,
onKeyDown: this.handleKeyDown
}, /*#__PURE__*/React.createElement("div", {
className: "slds-grid_vertical slds-col slds-path__scroller"
}, hasAutoplay && /*#__PURE__*/React.createElement(AutoplayButton, {
assistiveText: this.props.assistiveText.autoplayButton,
isAutoplayOn: this.getIsAutoplayOn(),
onClick: this.onAutoplayBtnClick
}), /*#__PURE__*/React.createElement("div", {
className: "slds-is-relative",
style: carouselMargins
}, hasPreviousNextPanelNavigation && /*#__PURE__*/React.createElement(PreviousNextCarouselNavigator, {
assistiveText: this.props.assistiveText.previousPanel,
iconName: "chevronleft",
isDisabled: isPreviousBtnDisabled,
onClick: function onClick(event) {
if (_this2.getIsAutoplayOn()) {
_this2.stopAutoplay(event);
}
_this2.onPreviousPanelHandler(event);
},
inlineStyle: {
left: '-38px'
}
}), /*#__PURE__*/React.createElement("div", {
ref: this.stageItem,
className: "slds-carousel__stage slds-show"
}, /*#__PURE__*/React.createElement("div", {
className: "slds-carousel__panels slds-is-relative",
style: {
transform: "translateX(".concat(this.state.translateX, "px)")
}
}, this.props.items.map(function (item, index) {
return /*#__PURE__*/React.createElement(CarouselItem, _extends({
carouselId: id,
getPanelId: _this2.getPanelId,
onClick: function onClick(event) {
_this2.props.onItemClick(event, {
item: item
});
},
onFocus: function onFocus(event) {
if (_this2.getIsAutoplayOn()) {
_this2.stopAutoplay(event);
}
},
onRenderItem: _this2.props.onRenderItem
}, item, {
isInCurrentPanel: index >= currentPanel * _this2.props.itemsPerPanel && index < currentPanel * _this2.props.itemsPerPanel + _this2.props.itemsPerPanel,
itemWidth: itemWidth,
key: item.id,
panelIndex: Math.ceil((index + 1) / _this2.props.itemsPerPanel) - 1
}));
}))), hasPreviousNextPanelNavigation && /*#__PURE__*/React.createElement(PreviousNextCarouselNavigator, {
assistiveText: this.props.assistiveText.nextPanel,
iconName: "chevronright",
isDisabled: isNextBtnDisabled,
onClick: function onClick(event) {
if (_this2.getIsAutoplayOn()) {
_this2.stopAutoplay(event);
}
_this2.onNextPanelHandler(event);
},
inlineStyle: {
right: '-38px'
}
})), /*#__PURE__*/React.createElement(CarouselIndicators, {
noOfIndicators: this.nrOfPanels,
carouselId: id,
currentIndex: currentPanel,
getPanelId: this.getPanelId,
hasFocus: this.state.indicatorsHaveFocus,
onBlur: this.onIndicatorBlur,
onClick: this.onIndicatorClickHandler,
onFocus: this.onIndicatorFocus,
items: this.props.items,
itemsPerPanel: this.props.itemsPerPanel
})));
}
}]);
return Carousel;
}(React.Component);
_defineProperty(Carousel, "displayName", CAROUSEL);
_defineProperty(Carousel, "propTypes", {
/**
* Description of the carousel items for screen-readers.
*/
assistiveText: PropTypes.object,
/**
* Interval for the autoplay iteration
*/
autoplayInterval: PropTypes.number,
/**
* CSS classes that are applied to the main 'slds-carousel' classed component container
*/
className: PropTypes.oneOfType([PropTypes.array, PropTypes.object, PropTypes.string]),
/**
* Dictates the currently active/visible carousel panel. Use with `onRequestPanelChange` for a controlled carousel component. If not provided, the carousel will manage this itself via state.
*/
currentPanel: PropTypes.number,
/**
* Boolean showing whether the autoplay button is available or not
*/
hasAutoplay: PropTypes.bool,
/**
* Boolean for displaying the navigation indicators (left/right arrows) of the carousel
*/
hasPreviousNextPanelNavigation: PropTypes.bool,
/**
* Id of component, if desired. If not provided an id is automatically generated
*/
id: PropTypes.string,
/**
* Boolean that dictates whether autoplay is active or not. Use with `onRequestAutoplayToggle` for a controlled carousel component.
*/
isAutoplayOn: PropTypes.bool,
/**
* Boolean for infinite loop navigation. Note: if not true autoplay will stop automatically at the last panel.
*/
isInfinite: PropTypes.bool,
/**
* * **Array of item objects used by the default carousel item renderer.**
* Each object can contain:
* * `id`: The id of the carousel item. [REQUIRED]
* * `heading`: Primary string that will be used as the heading
* * `description`: Secondary string that is used to describe the item
* * `buttonLabel`: If assigned a call to button action will be rendered with this text, if unassigned no button is rendered
* * `imageAssistiveText`: Image alt text, if not present heading will be used instead
* * `href`: Used for item link, if not provided '#' is used instead
* * `src`: Item image src value
*/
items: PropTypes.array.isRequired,
/**
* Number of items to be displayed at a time in the carousel
*/
itemsPerPanel: PropTypes.number,
/**
* Accepts a custom carousel item rendering function
*/
onRenderItem: PropTypes.func,
/**
* Called whenever `isAutoplayOn` is requested to be toggled on or off. Use with `isAutoplayOn` prop for a controlled carousel component. Passes an event object and a data object with the current `isAutoplayOn` value as an attribute.
*/
onRequestAutoplayToggle: PropTypes.func,
/**
* Called whenever the panel is requested to change due to user interaction or auto-play. Use with `currentPanel` for a controlled carousel component. Passes an event object and a data object with `currentPanel` and `requestedPanel` attributes.
*/
onRequestPanelChange: PropTypes.func,
/**
* Handler for clicking on a carousel item
*/
onItemClick: PropTypes.func
});
_defineProperty(Carousel, "defaultProps", defaultProps);
export default Carousel;
//# sourceMappingURL=index.js.map