uxcore-album
Version:
uxcore-album component for uxcore.
204 lines (190 loc) • 5.24 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import assign from 'object-assign';
import { polyfill } from 'react-lifecycles-compat';
import { transformProperty, vendorSupport } from './transform-detect';
class Carousel extends React.Component {
static defaultProps = {
current: 0,
onPrev() { },
onNext() { },
onSetCurrent() { },
placement: 'bottom',
itemSize: 132,
className: '',
carouselStyle: {},
containerStyle: {},
inView: false,
}
static propTypes = {
children: PropTypes.any,
current: PropTypes.number,
onPrev: PropTypes.func,
onNext: PropTypes.func,
onSetCurrent: PropTypes.func,
placement: PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
itemSize: PropTypes.number,
className: PropTypes.string,
carouselStyle: PropTypes.object,
containerStyle: PropTypes.object,
inView: PropTypes.bool,
}
static container = undefined;
static getDerivedStateFromProps(props, state) {
let stateFromProps = null;
if (props.current !== state.lastIndex) {
const { itemSize } = props;
const activeOffset = props.current * itemSize;
const viewWidth = Carousel.container.clientWidth;
const viewHeight = Carousel.container.clientHeight;
let { left, top } = state;
switch (props.placement) {
case 'top':
case 'bottom':
if (activeOffset < left) {
left -= itemSize;
} else if (activeOffset - left > viewWidth - itemSize) {
left += itemSize;
}
stateFromProps = {
...state,
left,
lastIndex: props.current,
};
break;
case 'left':
case 'right':
if (activeOffset < top) {
top -= itemSize;
} else if (activeOffset - top > viewHeight - itemSize) {
top += itemSize;
}
stateFromProps = {
...state,
top,
lastIndex: props.current,
};
break;
default:
break;
}
}
return stateFromProps;
}
constructor(props) {
super(props);
this.state = {
left: 0,
top: 0,
lastIndex: props.current,
};
}
render() {
const {
children,
current,
onPrev,
onNext,
placement,
onSetCurrent,
className,
carouselStyle,
containerStyle,
inView,
itemSize,
onChange,
} = this.props;
const listStyle = {};
const isHorizontal = placement === 'right' || placement === 'left';
const activeOffset = {};
if (isHorizontal) {
if (vendorSupport) {
listStyle[transformProperty] = `translateY(-${this.state.top}px)`;
} else {
listStyle.top = `-${this.state.top}px`;
}
} else if (vendorSupport) {
listStyle[transformProperty] = `translateX(-${this.state.left}px)`;
assign(activeOffset, {
transform: `translateX(${(current * itemSize) + 6}px)`,
});
} else {
listStyle.left = `-${this.state.left}px`;
assign(activeOffset, {
left: `${(current * itemSize) + 6}px`,
});
}
return (
<div className={classnames('album-carousel', className)} style={carouselStyle}>
<span
className={
classnames(
'album-carousel-control',
'album-icon',
{
'control-prev': inView,
'control-up': !inView,
disabled: current === 0,
},
)
}
onClick={onPrev}
ref={(c) => { this.prevControl = c; }}
/>
<div
className="album-carousel-container"
ref={node => (Carousel.container = node)}
style={containerStyle}
>
<ul
className="album-carousel-list"
style={listStyle}
ref={(c) => {
this.list = c;
}}
>
{
React.Children.map(children, (el, i) => el && (
<li
className={classnames('item', current === i ? 'active' : '')}
key={`c-${i}`}
onMouseDown={() => {
if (typeof onChange === 'function') {
onChange(i);
}
onSetCurrent(i);
}}
>
{React.cloneElement(el)}
</li>
))
}
<li
className="item-active"
key="active"
style={activeOffset}
/>
</ul>
</div>
<span
className={
classnames(
'album-carousel-control',
'album-icon',
{
'control-next': inView,
'control-down': !inView,
disabled: current === children.length - 1,
},
)
}
onClick={onNext}
ref={(c) => { this.nextControl = c; }}
/>
</div>
);
}
}
polyfill(Carousel);
export default Carousel;