feat: support customToolbar

This commit is contained in:
infeng 2018-04-24 16:01:49 +08:00
parent ce11356391
commit c32bc0c7ba
5 changed files with 130 additions and 95 deletions

View File

@ -129,9 +129,17 @@ class App extends React.Component<any, Partial<State>> {
onClose={() => { this.setState({ visible: false }); } }
images={images}
activeIndex={this.state.activeIndex}
attribute={false}
container={inline ? this.container : null}
downloadable
customToolbar={(toolbars) => {
return toolbars.concat([{
key: 'test',
render: <div>C</div>,
onClick: (activeImage) => {
console.log(activeImage);
},
}]);
}}
/>
</div>
<div className="footer">

View File

@ -2,8 +2,8 @@ import * as React from 'react';
import './style/index.less';
import ViewerCanvas from './ViewerCanvas';
import ViewerNav from './ViewerNav';
import ViewerToolbar from './ViewerToolbar';
import ViewerProps, { ImageDecorator } from './ViewerProps';
import ViewerToolbar, { defaultToolbars } from './ViewerToolbar';
import ViewerProps, { ImageDecorator, ToolbarConfig } from './ViewerProps';
import Icon, { ActionType } from './Icon';
function noop() {}
@ -41,6 +41,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
scalable: true,
onMaskClick: noop,
changeable: true,
customToolbar: (toolbars) => toolbars,
};
private prefixCls: string;
@ -228,7 +229,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
});
}
handleAction(type: ActionType) {
handleDefaultAction = (type: ActionType) => {
switch (type) {
case ActionType.prev:
if (this.state.activeIndex - 1 >= 0) {
@ -271,6 +272,15 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
}
}
handleAction(config: ToolbarConfig) {
this.handleDefaultAction(config.actionType);
if (config.onClick) {
const activeImage = this.getActiveImage();
config.onClick(activeImage);
}
}
handleDownload = () => {
const activeImage = this.getActiveImage();
if (activeImage.downloadUrl) {
@ -380,29 +390,29 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
// key: ←
case 37:
if (e.ctrlKey) {
this.handleAction(ActionType.rotateLeft);
this.handleDefaultAction(ActionType.rotateLeft);
} else {
this.handleAction(ActionType.prev);
this.handleDefaultAction(ActionType.prev);
}
isFeatrue = true;
break;
// key: →
case 39:
if (e.ctrlKey) {
this.handleAction(ActionType.rotateRight);
this.handleDefaultAction(ActionType.rotateRight);
} else {
this.handleAction(ActionType.next);
this.handleDefaultAction(ActionType.next);
}
isFeatrue = true;
break;
// key: ↑
case 38:
this.handleAction(ActionType.zoomIn);
this.handleDefaultAction(ActionType.zoomIn);
isFeatrue = true;
break;
// key: ↓
case 40:
this.handleAction(ActionType.zoomOut);
this.handleDefaultAction(ActionType.zoomOut);
isFeatrue = true;
break;
// key: Ctrl + 1
@ -575,6 +585,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
changeable={this.props.changeable}
downloadable={this.props.downloadable}
noImgDetails={this.props.noImgDetails}
toolbars={this.props.customToolbar(defaultToolbars)}
/>
)}
{this.props.noNavbar || (

View File

@ -4,6 +4,13 @@ export interface ImageDecorator {
downloadUrl?: string;
}
export interface ToolbarConfig {
key: string;
actionType?: number;
render?: React.ReactNode;
onClick?: (activeImage: ImageDecorator) => void;
}
interface ViewerProps {
/** viewer是否可见 */
visible?: boolean;
@ -49,6 +56,9 @@ interface ViewerProps {
// wheather to show change button
changeable?: boolean;
// custom toolbar
customToolbar?: (toolbars: ToolbarConfig[]) => ToolbarConfig[];
}
export default ViewerProps;

View File

@ -1,9 +1,10 @@
import * as React from 'react';
import Icon, { ActionType } from './Icon';
import { ToolbarConfig } from './ViewerProps';
export interface ViewerToolbarProps {
prefixCls: string;
onAction: (type: ActionType) => void;
onAction: (config: ToolbarConfig) => void;
alt: string;
width: number;
height: number;
@ -14,6 +15,56 @@ export interface ViewerToolbarProps {
changeable: boolean;
downloadable: boolean;
noImgDetails: boolean;
toolbars: ToolbarConfig[];
}
export const defaultToolbars: ToolbarConfig[] = [
{
key: 'zoomIn',
actionType: ActionType.zoomIn,
},
{
key: 'zoomOut',
actionType: ActionType.zoomOut,
},
{
key: 'prev',
actionType: ActionType.prev,
},
{
key: 'reset',
actionType: ActionType.reset,
},
{
key: 'next',
actionType: ActionType.next,
},
{
key: 'rotateLeft',
actionType: ActionType.rotateLeft,
},
{
key: 'rotateRight',
actionType: ActionType.rotateRight,
},
{
key: 'scaleX',
actionType: ActionType.scaleX,
},
{
key: 'scaleY',
actionType: ActionType.scaleY,
},
{
key: 'download',
actionType: ActionType.download,
},
];
function deleteToolbarFromKey(toolbars: ToolbarConfig[], keys: string[]) {
const targetToolbar = toolbars.filter(item => keys.indexOf(item.key) < 0);
return targetToolbar;
}
export default class ViewerToolbar extends React.Component<ViewerToolbarProps, any> {
@ -22,8 +73,29 @@ export default class ViewerToolbar extends React.Component<ViewerToolbarProps, a
super();
}
handleAction(type: ActionType) {
this.props.onAction(type);
handleAction(config: ToolbarConfig) {
this.props.onAction(config);
}
renderAction = (config: ToolbarConfig) => {
let content = null;
// default toolbar
if (typeof ActionType[config.actionType] !== 'undefined') {
content = <Icon type={config.actionType}/>;
}
// extra toolbar
if (config.render) {
content = config.render;
}
return (
<li
key={config.key}
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(config);}}
>
{content}
</li>
);
}
render() {
@ -33,96 +105,29 @@ export default class ViewerToolbar extends React.Component<ViewerToolbarProps, a
{this.props.noImgDetails || `(${this.props.width} x ${this.props.height})`}
</p>
) : null;
let featureNodeArr = [];
if (this.props.zoomable) {
featureNodeArr = featureNodeArr.concat([
<li
key="zoomIn"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.zoomIn);}}>
<Icon type={ActionType.zoomIn}/>
</li>,
<li
key="zoomOut"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.zoomOut);}}>
<Icon type={ActionType.zoomOut}/>
</li>,
]);
let toolbars = this.props.toolbars;
if (!this.props.zoomable) {
toolbars = deleteToolbarFromKey(toolbars, ['zoomIn', 'zoomOut']);
}
const resetTool = (
<li
key="reset"
className={`${this.props.prefixCls}-btn`} onClick={() => {this.handleAction(ActionType.reset);}}>
<Icon type={ActionType.reset}/>
</li>
);
if (this.props.changeable) {
featureNodeArr = featureNodeArr.concat([
<li
key="prev"
className={`${this.props.prefixCls}-btn`} onClick={() => {this.handleAction(ActionType.prev);}}>
<Icon type={ActionType.prev}/>
</li>,
resetTool,
<li
key="next"
className={`${this.props.prefixCls}-btn`} onClick={() => {this.handleAction(ActionType.next);}}>
<Icon type={ActionType.next}/>
</li>,
]);
} else {
featureNodeArr = featureNodeArr.concat([
resetTool,
]);
if (!this.props.changeable) {
toolbars = deleteToolbarFromKey(toolbars, ['prev', 'next']);
}
if (this.props.rotatable) {
featureNodeArr = featureNodeArr.concat([
<li
key="rotateLeft"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.rotateLeft);}}>
<Icon type={ActionType.rotateLeft}/>
</li>,
<li
key="rotateRight"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.rotateRight);}}>
<Icon type={ActionType.rotateRight}/>
</li>,
]);
if (!this.props.rotatable) {
toolbars = deleteToolbarFromKey(toolbars, ['rotateLeft', 'rotateRight']);
}
if (this.props.scalable) {
featureNodeArr = featureNodeArr.concat([
<li
key="scaleX"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.scaleX);}}>
<Icon type={ActionType.scaleX}/>
</li>,
<li
key="scaleY"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.scaleY);}}>
<Icon type={ActionType.scaleY}/>
</li>,
]);
if (!this.props.scalable) {
toolbars = deleteToolbarFromKey(toolbars, ['scaleX', 'scaleY']);
}
if (this.props.downloadable) {
featureNodeArr = featureNodeArr.concat([
<li
key="download"
className={`${this.props.prefixCls}-btn`}
onClick={() => {this.handleAction(ActionType.download);}}>
<Icon type={ActionType.download}/>
</li>,
]);
if (!this.props.downloadable) {
toolbars = deleteToolbarFromKey(toolbars, ['download']);
}
return (
<div>
{attributeNode}
<ul className={`${this.props.prefixCls}-toolbar`}>
{featureNodeArr}
{toolbars.map(item => {
return this.renderAction(item);
})}
</ul>
</div>
);

View File

@ -52,6 +52,7 @@
&-btn {
background-color: @btn-background-color;
color: white;
}
&-btn:hover{
@ -132,7 +133,7 @@
border-radius: @toolbarHeight;
margin-right: 3px;
cursor: pointer;
line-height: 29px;
line-height: 28px;
}
&-toolbar li:hover {