mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-30 02:58:05 +08:00
feat: ImageGallery支持工具栏 (#5005)
This commit is contained in:
parent
4be87d2373
commit
9b8e212a9d
@ -59,6 +59,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
transition: transform 0.3s cubic-bezier(0, 0, 0.25, 1) 0s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,3 +204,64 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.#{$ns}ImageGallery-toolbar {
|
||||||
|
background-color: var(--white);
|
||||||
|
border-radius: px2rem(4px);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: px2rem(4px) px2rem(16px);
|
||||||
|
position: absolute;
|
||||||
|
bottom: px2rem(20px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
padding: px2rem(13px);
|
||||||
|
border-radius: px2rem(4px);
|
||||||
|
width: px2rem(40px);
|
||||||
|
height: px2rem(40px);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
display: block;
|
||||||
|
color: var(--black);
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
fill: var(--black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: var(--icon-onDisabled-color);
|
||||||
|
|
||||||
|
.#{$ns}ImageGallery-toolbar-action-icon {
|
||||||
|
color: var(--icon-onDisabled-color);
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
color: var(--icon-onDisabled-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f2f3f5;
|
||||||
|
|
||||||
|
.#{$ns}ImageGallery-toolbar-action-icon {
|
||||||
|
color: var(--primary);
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
fill: var(--primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,13 +1,37 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
import {themeable, ClassNamesFn, ThemeProps} from 'amis-core';
|
import {themeable, ClassNamesFn, ThemeProps} from 'amis-core';
|
||||||
import {autobind} from 'amis-core';
|
import {autobind} from 'amis-core';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import {Icon} from './icons';
|
import {Icon} from './icons';
|
||||||
import {LocaleProps, localeable} from 'amis-core';
|
import {LocaleProps, localeable} from 'amis-core';
|
||||||
|
|
||||||
|
export enum ImageActionKey {
|
||||||
|
/** 右旋转 */
|
||||||
|
ROTATE_RIGHT = 'rotateRight',
|
||||||
|
/** 左旋转 */
|
||||||
|
ROTATE_LEFT = 'rotateLeft',
|
||||||
|
/** 等比例放大 */
|
||||||
|
ZOOM_IN = 'zoomIn',
|
||||||
|
/** 等比例缩小 */
|
||||||
|
ZOOM_OUT = 'zoomOut',
|
||||||
|
/** 恢复原图缩放比例尺 */
|
||||||
|
SCALE_ORIGIN = 'scaleOrigin'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageAction {
|
||||||
|
key: ImageActionKey;
|
||||||
|
label?: string;
|
||||||
|
icon?: string | React.ReactNode;
|
||||||
|
iconClassName?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick?: (context: ImageGallery) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ImageGalleryProps extends ThemeProps, LocaleProps {
|
export interface ImageGalleryProps extends ThemeProps, LocaleProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
modalContainer?: () => HTMLElement;
|
modalContainer?: () => HTMLElement;
|
||||||
|
actions?: ImageAction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImageGalleryState {
|
export interface ImageGalleryState {
|
||||||
@ -19,16 +43,44 @@ export interface ImageGalleryState {
|
|||||||
title?: string;
|
title?: string;
|
||||||
caption?: string;
|
caption?: string;
|
||||||
}>;
|
}>;
|
||||||
|
/** 图片缩放比例尺 */
|
||||||
|
scale: number;
|
||||||
|
/** 图片旋转角度 */
|
||||||
|
rotate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageGallery extends React.Component<
|
export class ImageGallery extends React.Component<
|
||||||
ImageGalleryProps,
|
ImageGalleryProps,
|
||||||
ImageGalleryState
|
ImageGalleryState
|
||||||
> {
|
> {
|
||||||
|
static defaultProps: Pick<ImageGalleryProps, 'actions'> = {
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
key: ImageActionKey.ROTATE_LEFT,
|
||||||
|
icon: 'rotate-left',
|
||||||
|
label: 'rotate.left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: ImageActionKey.ROTATE_RIGHT,
|
||||||
|
icon: 'rotate-right',
|
||||||
|
label: 'rotate.right'
|
||||||
|
},
|
||||||
|
{key: ImageActionKey.ZOOM_IN, icon: 'zoom-in', label: 'zoomIn'},
|
||||||
|
{key: ImageActionKey.ZOOM_OUT, icon: 'zoom-out', label: 'zoomOut'},
|
||||||
|
{
|
||||||
|
key: ImageActionKey.SCALE_ORIGIN,
|
||||||
|
icon: 'scale-origin',
|
||||||
|
label: 'scale.origin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
state: ImageGalleryState = {
|
state: ImageGalleryState = {
|
||||||
isOpened: false,
|
isOpened: false,
|
||||||
index: -1,
|
index: -1,
|
||||||
items: []
|
items: [],
|
||||||
|
scale: 1,
|
||||||
|
rotate: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
@ -52,40 +104,116 @@ export class ImageGallery extends React.Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetImageAction() {
|
||||||
|
this.setState({scale: 1, rotate: 0});
|
||||||
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
close() {
|
close() {
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpened: false
|
isOpened: false
|
||||||
});
|
});
|
||||||
|
this.resetImageAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
prev() {
|
prev() {
|
||||||
const index = this.state.index;
|
const index = this.state.index;
|
||||||
this.setState({
|
this.setState({index: index - 1});
|
||||||
index: index - 1
|
this.resetImageAction();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
next() {
|
next() {
|
||||||
const index = this.state.index;
|
const index = this.state.index;
|
||||||
this.setState({
|
this.setState({index: index + 1});
|
||||||
index: index + 1
|
this.resetImageAction();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
handleItemClick(e: React.MouseEvent<HTMLDivElement>) {
|
handleItemClick(e: React.MouseEvent<HTMLDivElement>) {
|
||||||
const index = parseInt(e.currentTarget.getAttribute('data-index')!, 10);
|
const index = parseInt(e.currentTarget.getAttribute('data-index')!, 10);
|
||||||
this.setState({
|
this.setState({index});
|
||||||
index
|
this.resetImageAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToolbarAction = debounce(
|
||||||
|
(action: ImageAction) => {
|
||||||
|
if (action.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action.key) {
|
||||||
|
case ImageActionKey.ROTATE_LEFT:
|
||||||
|
this.setState(prevState => ({rotate: prevState.rotate - 90}));
|
||||||
|
break;
|
||||||
|
case ImageActionKey.ROTATE_RIGHT:
|
||||||
|
this.setState(prevState => ({rotate: prevState.rotate + 90}));
|
||||||
|
break;
|
||||||
|
case ImageActionKey.ZOOM_IN:
|
||||||
|
this.setState(prevState => ({scale: prevState.scale + 0.5}));
|
||||||
|
break;
|
||||||
|
case ImageActionKey.ZOOM_OUT:
|
||||||
|
this.setState(prevState => {
|
||||||
|
return prevState.scale - 0.5 > 0
|
||||||
|
? {scale: prevState.scale - 0.5}
|
||||||
|
: null;
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case ImageActionKey.SCALE_ORIGIN:
|
||||||
|
this.setState(() => ({scale: 1}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.onClick && typeof action.onClick === 'function') {
|
||||||
|
action.onClick(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
250,
|
||||||
|
{leading: true, trailing: false}
|
||||||
|
);
|
||||||
|
|
||||||
|
renderToolbar(actions: ImageAction[]) {
|
||||||
|
const {classnames: cx, translate: __, className} = this.props;
|
||||||
|
const scale = this.state.scale;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('ImageGallery-toolbar', className)}>
|
||||||
|
{actions.map(action => (
|
||||||
|
<div
|
||||||
|
className={cx('ImageGallery-toolbar-action', {
|
||||||
|
'is-disabled':
|
||||||
|
action.disabled ||
|
||||||
|
(action.key === ImageActionKey.ZOOM_OUT && scale - 0.5 <= 0)
|
||||||
|
})}
|
||||||
|
key={action.key}
|
||||||
|
onClick={() => this.handleToolbarAction(action)}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
className={cx('ImageGallery-toolbar-action-icon')}
|
||||||
|
data-tooltip={__(action.label)}
|
||||||
|
data-position="top"
|
||||||
|
>
|
||||||
|
{React.isValidElement(action.icon) ? (
|
||||||
|
React.cloneElement(action.icon, {
|
||||||
|
className: cx('icon', action.iconClassName)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
icon={action.icon}
|
||||||
|
className={cx('icon', action.iconClassName)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {children, classnames: cx, modalContainer} = this.props;
|
const {children, classnames: cx, modalContainer, actions} = this.props;
|
||||||
const {index, items} = this.state;
|
const {index, items, rotate, scale} = this.state;
|
||||||
const __ = this.props.translate;
|
const __ = this.props.translate;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -116,7 +244,14 @@ export class ImageGallery extends React.Component<
|
|||||||
{items[index].title}
|
{items[index].title}
|
||||||
</div>
|
</div>
|
||||||
<div className={cx('ImageGallery-main')}>
|
<div className={cx('ImageGallery-main')}>
|
||||||
<img src={items[index].originalSrc} />
|
<img
|
||||||
|
src={items[index].originalSrc}
|
||||||
|
style={{transform: `scale(${scale}) rotate(${rotate}deg)`}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{Array.isArray(actions) && actions.length > 0
|
||||||
|
? this.renderToolbar(actions)
|
||||||
|
: null}
|
||||||
|
|
||||||
{items.length > 1 ? (
|
{items.length > 1 ? (
|
||||||
<>
|
<>
|
||||||
@ -143,6 +278,7 @@ export class ImageGallery extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{items.length > 1 ? (
|
{items.length > 1 ? (
|
||||||
<div className={cx('ImageGallery-footer')}>
|
<div className={cx('ImageGallery-footer')}>
|
||||||
<a className={cx('ImageGallery-prevList is-disabled')}>
|
<a className={cx('ImageGallery-prevList is-disabled')}>
|
||||||
|
@ -103,6 +103,9 @@ import InvisibleIcon from '../icons/invisible.svg';
|
|||||||
import DownIcon from '../icons/down.svg';
|
import DownIcon from '../icons/down.svg';
|
||||||
import RightDoubleArrowIcon from '../icons/right-double-arrow.svg';
|
import RightDoubleArrowIcon from '../icons/right-double-arrow.svg';
|
||||||
import NewEdit from '../icons/new-edit.svg';
|
import NewEdit from '../icons/new-edit.svg';
|
||||||
|
import RotateLeft from '../icons/rotate-left.svg';
|
||||||
|
import RotateRight from '../icons/rotate-right.svg';
|
||||||
|
import ScaleOrigin from '../icons/scale-origin.svg';
|
||||||
|
|
||||||
// 兼容原来的用法,后续不直接试用。
|
// 兼容原来的用法,后续不直接试用。
|
||||||
|
|
||||||
@ -233,6 +236,9 @@ registerIcon('invisible', InvisibleIcon);
|
|||||||
registerIcon('down', DownIcon);
|
registerIcon('down', DownIcon);
|
||||||
registerIcon('right-double-arrow', RightDoubleArrowIcon);
|
registerIcon('right-double-arrow', RightDoubleArrowIcon);
|
||||||
registerIcon('new-edit', NewEdit);
|
registerIcon('new-edit', NewEdit);
|
||||||
|
registerIcon('rotate-left', RotateLeft);
|
||||||
|
registerIcon('rotate-right', RotateRight);
|
||||||
|
registerIcon('scale-origin', ScaleOrigin);
|
||||||
|
|
||||||
export function Icon({
|
export function Icon({
|
||||||
icon,
|
icon,
|
||||||
|
1
packages/amis-ui/src/icons/rotate-left.svg
Normal file
1
packages/amis-ui/src/icons/rotate-left.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1658923319458" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9742" width="200" height="200"><path d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H188V494h440v326z" p-id="9743"></path><path d="M819.3 328.5c-78.8-100.7-196-153.6-314.6-154.2l-0.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7 0.4 12.6-6.1v-63.9c12.9 0.1 25.9 0.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-0.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z" p-id="9744"></path></svg>
|
After Width: | Height: | Size: 701 B |
1
packages/amis-ui/src/icons/rotate-right.svg
Normal file
1
packages/amis-ui/src/icons/rotate-right.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1658923268348" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9594" width="200" height="200"><path d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-0.4-12.6 6.1l-0.2 64c-118.6 0.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-0.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z" p-id="9595"></path><path d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H396V494h440v326z" p-id="9596"></path></svg>
|
After Width: | Height: | Size: 701 B |
1
packages/amis-ui/src/icons/scale-origin.svg
Normal file
1
packages/amis-ui/src/icons/scale-origin.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg t="1658924156367" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12727" width="200" height="200"><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="12728"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="12729"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="12730"></path></svg>
|
After Width: | Height: | Size: 735 B |
@ -290,6 +290,11 @@ register('de-DE', {
|
|||||||
'Year.placeholder': 'Wählen Sie ein Jahr',
|
'Year.placeholder': 'Wählen Sie ein Jahr',
|
||||||
'reload': 'Neu laden',
|
'reload': 'Neu laden',
|
||||||
'rotate': 'Drehen',
|
'rotate': 'Drehen',
|
||||||
|
'rotate.left': 'Nach links drehen',
|
||||||
|
'rotate.right': 'Drehe nach rechts',
|
||||||
|
'zoomIn': 'Vergrößern',
|
||||||
|
'zoomOut': 'Verkleinern',
|
||||||
|
'scale.origin': 'Originalmaße',
|
||||||
'Editor.fullscreen': 'Schirmfüllend Modus',
|
'Editor.fullscreen': 'Schirmfüllend Modus',
|
||||||
'Editor.exitFullscreen': 'Zurücktreten Schirmfüllend Modus',
|
'Editor.exitFullscreen': 'Zurücktreten Schirmfüllend Modus',
|
||||||
'Condition.not': 'nicht',
|
'Condition.not': 'nicht',
|
||||||
|
@ -280,6 +280,11 @@ register('en-US', {
|
|||||||
'Year.placeholder': 'Select a Year',
|
'Year.placeholder': 'Select a Year',
|
||||||
'reload': 'Reload',
|
'reload': 'Reload',
|
||||||
'rotate': 'Rotate',
|
'rotate': 'Rotate',
|
||||||
|
'rotate.left': 'Rotate left',
|
||||||
|
'rotate.right': 'Rotate right',
|
||||||
|
'zoomIn': 'Zoom in',
|
||||||
|
'zoomOut': 'Zoom out',
|
||||||
|
'scale.origin': 'Original scale',
|
||||||
'Editor.fullscreen': 'full screen',
|
'Editor.fullscreen': 'full screen',
|
||||||
'Editor.exitFullscreen': 'exit fullscreen mode',
|
'Editor.exitFullscreen': 'exit fullscreen mode',
|
||||||
'Condition.not': 'not',
|
'Condition.not': 'not',
|
||||||
|
@ -282,6 +282,11 @@ register('zh-CN', {
|
|||||||
'Year.placeholder': '请选择年',
|
'Year.placeholder': '请选择年',
|
||||||
'reload': '刷新',
|
'reload': '刷新',
|
||||||
'rotate': '旋转',
|
'rotate': '旋转',
|
||||||
|
'rotate.left': '向左旋转',
|
||||||
|
'rotate.right': '向右旋转',
|
||||||
|
'zoomIn': '放大',
|
||||||
|
'zoomOut': '缩小',
|
||||||
|
'scale.origin': '原始尺寸',
|
||||||
'Editor.fullscreen': '全屏',
|
'Editor.fullscreen': '全屏',
|
||||||
'Editor.exitFullscreen': '退出全屏',
|
'Editor.exitFullscreen': '退出全屏',
|
||||||
'Condition.not': '非',
|
'Condition.not': '非',
|
||||||
|
Loading…
Reference in New Issue
Block a user