mirror of
https://github.com/infeng/react-viewer.git
synced 2025-12-08 17:36:40 +00:00
feat: support customToolbar
This commit is contained in:
parent
ce11356391
commit
c32bc0c7ba
@ -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">
|
||||
|
||||
@ -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 || (
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user