uxcore-album
Version:
uxcore-album component for uxcore.
415 lines (385 loc) • 9.51 kB
JSX
/* eslint-disable react/no-did-update-set-state */
/**
* Album Component for uxcore
* @author vincent.bian
*
* Copyright 2015-2016, Uxcore Team, Alinw.
* All rights reserved.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import assign from 'object-assign';
import Animate from 'uxcore-animate';
import { polyfill } from 'react-lifecycles-compat';
import { supportRGBA } from './rgba-detect';
import Viewer from './Viewer';
import Photo from './Photo';
import Carousel from './Carousel';
class Album extends React.Component {
static getDerivedStateFromProps(props, state) {
if (props.current !== state.lastIndex) {
return {
...state,
current: props.current,
lastIndex: props.current,
};
}
return null;
}
constructor(props) {
super(props);
this.state = {
open: false,
current: props.current,
left: 0,
top: 0,
lastIndex: props.current,
};
this.openAlbum = this.openAlbum.bind(this);
this.prev = this.prev.bind(this);
this.next = this.next.bind(this);
this.setCurrent = this.setCurrent.bind(this);
}
setCurrent(i) {
this.setState({
current: i,
});
}
/**
* 切换图片
*/
handleChange() {
const { onChange } = this.props;
const { current } = this.state;
if (typeof onChange === 'function') {
onChange(current);
}
}
/**
* 打开大图
*/
handleOpen() {
const { onOpen } = this.props;
const { current } = this.state;
if (typeof onOpen === 'function') {
onOpen(current);
}
}
/**
* 关闭大图
*/
handleCloce() {
const { onClose } = this.props;
if (typeof onClose === 'function') {
onClose();
}
}
prev() {
const current = this.state.current;
if (current === 0) return;
this.setState({
current: current - 1,
}, () => {
this.handleChange();
});
}
next() {
const current = this.state.current;
let { children } = this.props;
if (!Array.isArray(children)) {
children = [children];
}
if (current === children.length - 1) return;
this.setState({
current: current + 1,
}, () => {
this.handleChange();
});
}
openAlbum() {
this.setState({
open: true,
});
this.handleOpen();
}
renderAlbum() {
const { current, open } = this.state;
const {
enableKeyBoardControl, showButton, customButtons, maskClosable,
} = this.props;
let { children } = this.props;
if (!Array.isArray(children)) {
children = [children];
}
let rect;
let coordinate;
if (this.cover && this.cover.img && this.cover.img.getBoundingClientRect) {
rect = this.cover.img.getBoundingClientRect();
}
if (rect) {
coordinate = {
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
};
}
return (
<Animate
component=""
transitionName="album-overlay"
transitionAppear
transitionEnter
transitionLeave
showProp="open"
>
<Viewer
key="viewer"
prev={this.prev}
next={this.next}
onChange={this.handleChange.bind(this)}
onClose={(e) => {
if (e) {
e.preventDefault();
this.setState({
open: false,
});
this.handleCloce();
}
}}
maskClosable={maskClosable}
enableKeyBoardControl={enableKeyBoardControl}
ref={(node) => { this.viewer = node; }}
coordinate={coordinate}
showButton={showButton}
customButtons={customButtons}
current={current}
open={open}
onSetCurrent={(c) => {
this.setState({ current: c });
}}
>
{children}
</Viewer>
</Animate>
);
}
renderCover() {
const {
width, height, children, enableThumbs, thumbBackground,
} = this.props;
const { current } = this.state;
const coverStyle = {};
if (width) {
coverStyle.width = width;
}
if (height) {
coverStyle.height = height;
}
if (enableThumbs) {
coverStyle.background = thumbBackground;
}
return (
<div>
<div
className="album-cover album-icon"
onClick={this.openAlbum}
style={coverStyle}
>
{React.Children.map(children, (child, index) => {
if (index === current) {
return React.cloneElement(child, {
ref: (cover) => { this.cover = cover; },
});
}
return null;
})}
</div>
{enableThumbs ? this.renderThumbs() : ''}
</div>
);
}
renderThumbs() {
const {
thumbPlacement, children, width, height,
} = this.props;
const { current } = this.state;
const isHorizontal = thumbPlacement === 'right' || thumbPlacement === 'left';
const thumbs = React.Children.map(children, (o) => {
const src = o.props['thumb-src'] || o.props.src;
return (
<div key={src} className="album-item">
<img src={src} alt="" />
</div>
);
});
const carouselStyle = isHorizontal ? {
height,
width: 122,
} : {
height: 72,
width,
};
const containerStyle = isHorizontal ? {
height: height - 50,
width: 122,
} : {
height: 72,
width: width - 50,
};
return (
<Carousel
current={current}
placement={thumbPlacement}
onPrev={this.prev}
onNext={this.next}
onSetCurrent={this.setCurrent}
itemSize={isHorizontal ? 78 : 126}
className="thumbs-preview"
onChange={this.handleChange.bind(this)}
carouselStyle={carouselStyle}
containerStyle={containerStyle}
>
{thumbs}
</Carousel>
);
}
render() {
const {
prefixCls, enableThumbs, thumbPlacement, width, height,
} = this.props;
const isHorizontal = thumbPlacement === 'right' || thumbPlacement === 'left';
const style = isHorizontal ? {
width: width + (enableThumbs ? 140 : 10),
} : {
height: height + (enableThumbs ? 90 : 10),
};
return (
<div
className={classnames(prefixCls, {
'no-rgba': !supportRGBA,
'has-thumb': enableThumbs,
[`thumb-placement-${thumbPlacement}`]: enableThumbs,
})}
style={style}
ref={(node) => { this.wrap = node; }}
>
{
this.renderCover()
}
{
this.renderAlbum()
}
</div>
);
}
}
Album.defaultProps = {
prefixCls: 'kuma-uxcore-album',
width: '',
height: '',
thumbPlacement: 'right',
thumbBackground: '#000',
enableThumbs: false,
enableKeyBoardControl: true,
showButton: false,
customButtons: Viewer.defaultProps.customButtons,
current: 0,
};
// http://facebook.github.io/react/docs/reusable-components.html
Album.propTypes = {
prefixCls: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
thumbPlacement: PropTypes.string,
thumbBackground: PropTypes.string,
enableThumbs: PropTypes.bool,
enableKeyBoardControl: PropTypes.bool,
showButton: PropTypes.bool,
customButtons: Viewer.propTypes.customButtons,
children: PropTypes.node,
current: PropTypes.number,
};
Album.displayName = 'Album';
Album.show = (option = {}) => {
const config = {
src: null,
photos: [],
current: 0,
getContainer() {
const container = document.createElement('div');
document.body.appendChild(container);
return container;
},
};
assign(config, option);
if (!config.src && config.photos.length === 0) {
// eslint-disable-next-line
console.warn('You must provide valid parameters: "src" or "photos"!');
}
const container = config.getContainer();
let photos;
if (config.src) {
photos = [<Photo src={config.src} key={0} />];
} else {
photos = config.photos;
}
let hasControl = false;
if (photos.length > 1) {
hasControl = true;
}
/**
* 切换
* @param {Number} index
*/
const handleChange = (index) => {
if (typeof config.onChange === 'function') {
config.onChange(index);
}
};
/**
* 关闭
*/
const handleCloce = () => {
if (typeof config.onClose === 'function') {
config.onClose();
}
};
const prefixCls = option.prefixCls || 'kuma-uxcore-album';
ReactDOM.render(
<div
className={classnames(prefixCls, {
'no-rgba': !supportRGBA,
})}
tabIndex="-1"
>
<Animate
component=""
transitionName="album-overlay"
transitionAppear
transitionEnter
transitionLeave
>
<Viewer
onClose={() => {
handleCloce();
document.body.removeChild(container);
}}
current={config.current}
hasControl={hasControl}
showButton={config.showButton}
customButtons={config.customButtons}
onChange={handleChange}
maskClosable={config.maskClosable}
>
{photos}
</Viewer>
</Animate>
</div>,
container,
);
};
polyfill(Album);
export default Album;