mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2024-11-22 12:58:06 +01:00
Change zoom icon in web UI (#29683)
This commit is contained in:
parent
04a939d640
commit
e7fd0985c9
9 changed files with 114 additions and 145 deletions
|
@ -148,7 +148,7 @@ class ModalRoot extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<div className='modal-root' ref={this.setRef}>
|
<div className='modal-root' ref={this.setRef}>
|
||||||
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||||
<div role='presentation' className='modal-root__overlay' onClick={onClose} style={{ backgroundColor: backgroundColor ? `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, 0.7)` : null }} />
|
<div role='presentation' className='modal-root__overlay' onClick={onClose} style={{ backgroundColor: backgroundColor ? `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, 0.9)` : null }} />
|
||||||
<div role='dialog' className='modal-root__container'>{children}</div>
|
<div role='dialog' className='modal-root__container'>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class ImageLoader extends PureComponent {
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
zoomButtonHidden: PropTypes.bool,
|
zoomedIn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -134,7 +134,7 @@ export default class ImageLoader extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { alt, lang, src, width, height, onClick } = this.props;
|
const { alt, lang, src, width, height, onClick, zoomedIn } = this.props;
|
||||||
const { loading } = this.state;
|
const { loading } = this.state;
|
||||||
|
|
||||||
const className = classNames('image-loader', {
|
const className = classNames('image-loader', {
|
||||||
|
@ -149,6 +149,7 @@ export default class ImageLoader extends PureComponent {
|
||||||
<div className='loading-bar__container' style={{ width: this.state.width || width }}>
|
<div className='loading-bar__container' style={{ width: this.state.width || width }}>
|
||||||
<LoadingBar className='loading-bar' loading={1} />
|
<LoadingBar className='loading-bar' loading={1} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
className='image-loader__preview-canvas'
|
className='image-loader__preview-canvas'
|
||||||
ref={this.setCanvasRef}
|
ref={this.setCanvasRef}
|
||||||
|
@ -164,7 +165,7 @@ export default class ImageLoader extends PureComponent {
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
zoomButtonHidden={this.props.zoomButtonHidden}
|
zoomedIn={zoomedIn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,6 +12,8 @@ import ReactSwipeableViews from 'react-swipeable-views';
|
||||||
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
import ChevronLeftIcon from '@/material-icons/400-24px/chevron_left.svg?react';
|
||||||
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
import FitScreenIcon from '@/material-icons/400-24px/fit_screen.svg?react';
|
||||||
|
import ActualSizeIcon from '@/svg-icons/actual_size.svg?react';
|
||||||
import { getAverageFromBlurhash } from 'mastodon/blurhash';
|
import { getAverageFromBlurhash } from 'mastodon/blurhash';
|
||||||
import { GIFV } from 'mastodon/components/gifv';
|
import { GIFV } from 'mastodon/components/gifv';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
@ -26,6 +28,8 @@ const messages = defineMessages({
|
||||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||||
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
||||||
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
||||||
|
zoomIn: { id: 'lightbox.zoom_in', defaultMessage: 'Zoom to actual size' },
|
||||||
|
zoomOut: { id: 'lightbox.zoom_out', defaultMessage: 'Zoom to fit' },
|
||||||
});
|
});
|
||||||
|
|
||||||
class MediaModal extends ImmutablePureComponent {
|
class MediaModal extends ImmutablePureComponent {
|
||||||
|
@ -46,30 +50,39 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
state = {
|
state = {
|
||||||
index: null,
|
index: null,
|
||||||
navigationHidden: false,
|
navigationHidden: false,
|
||||||
zoomButtonHidden: false,
|
zoomedIn: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleZoomClick = () => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
zoomedIn: !prevState.zoomedIn,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSwipe = (index) => {
|
handleSwipe = (index) => {
|
||||||
this.setState({ index: index % this.props.media.size });
|
this.setState({
|
||||||
|
index: index % this.props.media.size,
|
||||||
|
zoomedIn: false,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTransitionEnd = () => {
|
handleTransitionEnd = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
zoomButtonHidden: false,
|
zoomedIn: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleNextClick = () => {
|
handleNextClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
index: (this.getIndex() + 1) % this.props.media.size,
|
index: (this.getIndex() + 1) % this.props.media.size,
|
||||||
zoomButtonHidden: true,
|
zoomedIn: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handlePrevClick = () => {
|
handlePrevClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size,
|
index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size,
|
||||||
zoomButtonHidden: true,
|
zoomedIn: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +91,7 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
index: index % this.props.media.size,
|
index: index % this.props.media.size,
|
||||||
zoomButtonHidden: true,
|
zoomedIn: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,15 +143,22 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
return this.state.index !== null ? this.state.index : this.props.index;
|
return this.state.index !== null ? this.state.index : this.props.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleNavigation = () => {
|
handleToggleNavigation = () => {
|
||||||
this.setState(prevState => ({
|
this.setState(prevState => ({
|
||||||
navigationHidden: !prevState.navigationHidden,
|
navigationHidden: !prevState.navigationHidden,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.setState({
|
||||||
|
viewportWidth: c?.clientWidth,
|
||||||
|
viewportHeight: c?.clientHeight,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, statusId, lang, intl, onClose } = this.props;
|
const { media, statusId, lang, intl, onClose } = this.props;
|
||||||
const { navigationHidden } = this.state;
|
const { navigationHidden, zoomedIn, viewportWidth, viewportHeight } = this.state;
|
||||||
|
|
||||||
const index = this.getIndex();
|
const index = this.getIndex();
|
||||||
|
|
||||||
|
@ -160,8 +180,8 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
alt={description}
|
alt={description}
|
||||||
lang={lang}
|
lang={lang}
|
||||||
key={image.get('url')}
|
key={image.get('url')}
|
||||||
onClick={this.toggleNavigation}
|
onClick={this.handleToggleNavigation}
|
||||||
zoomButtonHidden={this.state.zoomButtonHidden}
|
zoomedIn={zoomedIn}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (image.get('type') === 'video') {
|
} else if (image.get('type') === 'video') {
|
||||||
|
@ -230,9 +250,12 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentMedia = media.get(index);
|
||||||
|
const zoomable = currentMedia.get('type') === 'image' && (currentMedia.getIn(['meta', 'original', 'width']) > viewportWidth || currentMedia.getIn(['meta', 'original', 'height']) > viewportHeight);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='modal-root__modal media-modal'>
|
<div className='modal-root__modal media-modal' ref={this.setRef}>
|
||||||
<div className='media-modal__closer' role='presentation' onClick={onClose} >
|
<div className='media-modal__closer' role='presentation' onClick={onClose}>
|
||||||
<ReactSwipeableViews
|
<ReactSwipeableViews
|
||||||
style={swipeableViewsStyle}
|
style={swipeableViewsStyle}
|
||||||
containerStyle={containerStyle}
|
containerStyle={containerStyle}
|
||||||
|
@ -246,7 +269,10 @@ class MediaModal extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={navigationClassName}>
|
<div className={navigationClassName}>
|
||||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' iconComponent={CloseIcon} onClick={onClose} size={40} />
|
<div className='media-modal__buttons'>
|
||||||
|
{zoomable && <IconButton title={intl.formatMessage(zoomedIn ? messages.zoomOut : messages.zoomIn)} iconComponent={zoomedIn ? FitScreenIcon : ActualSizeIcon} onClick={this.handleZoomClick} />}
|
||||||
|
<IconButton title={intl.formatMessage(messages.close)} icon='times' iconComponent={CloseIcon} onClick={onClose} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{leftNav}
|
{leftNav}
|
||||||
{rightNav}
|
{rightNav}
|
||||||
|
|
|
@ -1,17 +1,6 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import FullscreenExitIcon from '@/material-icons/400-24px/fullscreen_exit.svg?react';
|
|
||||||
import RectangleIcon from '@/material-icons/400-24px/rectangle.svg?react';
|
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
compress: { id: 'lightbox.compress', defaultMessage: 'Compress image view box' },
|
|
||||||
expand: { id: 'lightbox.expand', defaultMessage: 'Expand image view box' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const MIN_SCALE = 1;
|
const MIN_SCALE = 1;
|
||||||
const MAX_SCALE = 4;
|
const MAX_SCALE = 4;
|
||||||
const NAV_BAR_HEIGHT = 66;
|
const NAV_BAR_HEIGHT = 66;
|
||||||
|
@ -104,8 +93,7 @@ class ZoomableImage extends PureComponent {
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
zoomButtonHidden: PropTypes.bool,
|
zoomedIn: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -131,8 +119,6 @@ class ZoomableImage extends PureComponent {
|
||||||
translateX: null,
|
translateX: null,
|
||||||
translateY: null,
|
translateY: null,
|
||||||
},
|
},
|
||||||
zoomState: 'expand', // 'expand' 'compress'
|
|
||||||
navigationHidden: false,
|
|
||||||
dragPosition: { top: 0, left: 0, x: 0, y: 0 },
|
dragPosition: { top: 0, left: 0, x: 0, y: 0 },
|
||||||
dragged: false,
|
dragged: false,
|
||||||
lockScroll: { x: 0, y: 0 },
|
lockScroll: { x: 0, y: 0 },
|
||||||
|
@ -169,35 +155,20 @@ class ZoomableImage extends PureComponent {
|
||||||
this.container.addEventListener('DOMMouseScroll', handler);
|
this.container.addEventListener('DOMMouseScroll', handler);
|
||||||
this.removers.push(() => this.container.removeEventListener('DOMMouseScroll', handler));
|
this.removers.push(() => this.container.removeEventListener('DOMMouseScroll', handler));
|
||||||
|
|
||||||
this.initZoomMatrix();
|
this._initZoomMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
this.removeEventListeners();
|
this._removeEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate (prevProps) {
|
||||||
this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? 'compress' : 'expand' });
|
if (prevProps.zoomedIn !== this.props.zoomedIn) {
|
||||||
|
this._toggleZoom();
|
||||||
if (this.state.scale === MIN_SCALE) {
|
|
||||||
this.container.style.removeProperty('cursor');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps () {
|
_removeEventListeners () {
|
||||||
// reset when slide to next image
|
|
||||||
if (this.props.zoomButtonHidden) {
|
|
||||||
this.setState({
|
|
||||||
scale: MIN_SCALE,
|
|
||||||
lockTranslate: { x: 0, y: 0 },
|
|
||||||
}, () => {
|
|
||||||
this.container.scrollLeft = 0;
|
|
||||||
this.container.scrollTop = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListeners () {
|
|
||||||
this.removers.forEach(listeners => listeners());
|
this.removers.forEach(listeners => listeners());
|
||||||
this.removers = [];
|
this.removers = [];
|
||||||
}
|
}
|
||||||
|
@ -220,9 +191,6 @@ class ZoomableImage extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
mouseDownHandler = e => {
|
mouseDownHandler = e => {
|
||||||
this.container.style.cursor = 'grabbing';
|
|
||||||
this.container.style.userSelect = 'none';
|
|
||||||
|
|
||||||
this.setState({ dragPosition: {
|
this.setState({ dragPosition: {
|
||||||
left: this.container.scrollLeft,
|
left: this.container.scrollLeft,
|
||||||
top: this.container.scrollTop,
|
top: this.container.scrollTop,
|
||||||
|
@ -246,9 +214,6 @@ class ZoomableImage extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
mouseUpHandler = () => {
|
mouseUpHandler = () => {
|
||||||
this.container.style.cursor = 'grab';
|
|
||||||
this.container.style.removeProperty('user-select');
|
|
||||||
|
|
||||||
this.image.removeEventListener('mousemove', this.mouseMoveHandler);
|
this.image.removeEventListener('mousemove', this.mouseMoveHandler);
|
||||||
this.image.removeEventListener('mouseup', this.mouseUpHandler);
|
this.image.removeEventListener('mouseup', this.mouseUpHandler);
|
||||||
};
|
};
|
||||||
|
@ -276,13 +241,13 @@ class ZoomableImage extends PureComponent {
|
||||||
const _MAX_SCALE = Math.max(MAX_SCALE, this.state.zoomMatrix.rate);
|
const _MAX_SCALE = Math.max(MAX_SCALE, this.state.zoomMatrix.rate);
|
||||||
const scale = clamp(MIN_SCALE, _MAX_SCALE, this.state.scale * distance / this.lastDistance);
|
const scale = clamp(MIN_SCALE, _MAX_SCALE, this.state.scale * distance / this.lastDistance);
|
||||||
|
|
||||||
this.zoom(scale, midpoint);
|
this._zoom(scale, midpoint);
|
||||||
|
|
||||||
this.lastMidpoint = midpoint;
|
this.lastMidpoint = midpoint;
|
||||||
this.lastDistance = distance;
|
this.lastDistance = distance;
|
||||||
};
|
};
|
||||||
|
|
||||||
zoom(nextScale, midpoint) {
|
_zoom(nextScale, midpoint) {
|
||||||
const { scale, zoomMatrix } = this.state;
|
const { scale, zoomMatrix } = this.state;
|
||||||
const { scrollLeft, scrollTop } = this.container;
|
const { scrollLeft, scrollTop } = this.container;
|
||||||
|
|
||||||
|
@ -318,14 +283,13 @@ class ZoomableImage extends PureComponent {
|
||||||
if (dragged) return;
|
if (dragged) return;
|
||||||
const handler = this.props.onClick;
|
const handler = this.props.onClick;
|
||||||
if (handler) handler();
|
if (handler) handler();
|
||||||
this.setState({ navigationHidden: !this.state.navigationHidden });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseDown = e => {
|
handleMouseDown = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
initZoomMatrix = () => {
|
_initZoomMatrix = () => {
|
||||||
const { width, height } = this.props;
|
const { width, height } = this.props;
|
||||||
const { clientWidth, clientHeight } = this.container;
|
const { clientWidth, clientHeight } = this.container;
|
||||||
const { offsetWidth, offsetHeight } = this.image;
|
const { offsetWidth, offsetHeight } = this.image;
|
||||||
|
@ -357,10 +321,7 @@ class ZoomableImage extends PureComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleZoomClick = e => {
|
_toggleZoom () {
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const { scale, zoomMatrix } = this.state;
|
const { scale, zoomMatrix } = this.state;
|
||||||
|
|
||||||
if ( scale >= zoomMatrix.rate ) {
|
if ( scale >= zoomMatrix.rate ) {
|
||||||
|
@ -394,10 +355,7 @@ class ZoomableImage extends PureComponent {
|
||||||
this.container.scrollTop = zoomMatrix.scrollTop;
|
this.container.scrollTop = zoomMatrix.scrollTop;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.container.style.cursor = 'grab';
|
|
||||||
this.container.style.removeProperty('user-select');
|
|
||||||
};
|
|
||||||
|
|
||||||
setContainerRef = c => {
|
setContainerRef = c => {
|
||||||
this.container = c;
|
this.container = c;
|
||||||
|
@ -408,52 +366,37 @@ class ZoomableImage extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { alt, lang, src, width, height, intl } = this.props;
|
const { alt, lang, src, width, height } = this.props;
|
||||||
const { scale, lockTranslate } = this.state;
|
const { scale, lockTranslate, dragged } = this.state;
|
||||||
const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
|
const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
|
||||||
const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : '';
|
const cursor = scale === MIN_SCALE ? null : (dragged ? 'grabbing' : 'grab');
|
||||||
const zoomButtonTitle = this.state.zoomState === 'compress' ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<IconButton
|
className='zoomable-image'
|
||||||
className={`media-modal__zoom-button ${zoomButtonShouldHide}`}
|
ref={this.setContainerRef}
|
||||||
title={zoomButtonTitle}
|
style={{ overflow, cursor, userSelect: 'none' }}
|
||||||
icon={this.state.zoomState}
|
>
|
||||||
iconComponent={this.state.zoomState === 'compress' ? FullscreenExitIcon : RectangleIcon}
|
<img
|
||||||
onClick={this.handleZoomClick}
|
role='presentation'
|
||||||
size={40}
|
ref={this.setImageRef}
|
||||||
|
alt={alt}
|
||||||
|
title={alt}
|
||||||
|
lang={lang}
|
||||||
|
src={src}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
style={{
|
style={{
|
||||||
fontSize: '30px', /* Fontawesome's fa-compress fa-expand is larger than fa-close */
|
transform: `scale(${scale}) translate(-${lockTranslate.x}px, -${lockTranslate.y}px)`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
}}
|
}}
|
||||||
|
draggable={false}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
onMouseDown={this.handleMouseDown}
|
||||||
/>
|
/>
|
||||||
<div
|
</div>
|
||||||
className='zoomable-image'
|
|
||||||
ref={this.setContainerRef}
|
|
||||||
style={{ overflow }}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
role='presentation'
|
|
||||||
ref={this.setImageRef}
|
|
||||||
alt={alt}
|
|
||||||
title={alt}
|
|
||||||
lang={lang}
|
|
||||||
src={src}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
style={{
|
|
||||||
transform: `scale(${scale}) translate(-${lockTranslate.x}px, -${lockTranslate.y}px)`,
|
|
||||||
transformOrigin: '0 0',
|
|
||||||
}}
|
|
||||||
draggable={false}
|
|
||||||
onClick={this.handleClick}
|
|
||||||
onMouseDown={this.handleMouseDown}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectIntl(ZoomableImage);
|
export default ZoomableImage;
|
||||||
|
|
|
@ -432,10 +432,10 @@
|
||||||
"keyboard_shortcuts.unfocus": "Unfocus compose textarea/search",
|
"keyboard_shortcuts.unfocus": "Unfocus compose textarea/search",
|
||||||
"keyboard_shortcuts.up": "Move up in the list",
|
"keyboard_shortcuts.up": "Move up in the list",
|
||||||
"lightbox.close": "Close",
|
"lightbox.close": "Close",
|
||||||
"lightbox.compress": "Compress image view box",
|
|
||||||
"lightbox.expand": "Expand image view box",
|
|
||||||
"lightbox.next": "Next",
|
"lightbox.next": "Next",
|
||||||
"lightbox.previous": "Previous",
|
"lightbox.previous": "Previous",
|
||||||
|
"lightbox.zoom_in": "Zoom to actual size",
|
||||||
|
"lightbox.zoom_out": "Zoom to fit",
|
||||||
"limited_account_hint.action": "Show profile anyway",
|
"limited_account_hint.action": "Show profile anyway",
|
||||||
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
"limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
|
||||||
"link_preview.author": "By {name}",
|
"link_preview.author": "By {name}",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M800-600v-120H680v-80h120q33 0 56.5 23.5T880-720v120h-80Zm-720 0v-120q0-33 23.5-56.5T160-800h120v80H160v120H80Zm600 440v-80h120v-120h80v120q0 33-23.5 56.5T800-160H680Zm-520 0q-33 0-56.5-23.5T80-240v-120h80v120h120v80H160Zm80-160v-320h480v320H240Z"/></svg>
|
After Width: | Height: | Size: 352 B |
1
app/javascript/material-icons/400-24px/fit_screen.svg
Normal file
1
app/javascript/material-icons/400-24px/fit_screen.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M800-600v-120H680v-80h120q33 0 56.5 23.5T880-720v120h-80Zm-720 0v-120q0-33 23.5-56.5T160-800h120v80H160v120H80Zm600 440v-80h120v-120h80v120q0 33-23.5 56.5T800-160H680Zm-520 0q-33 0-56.5-23.5T80-240v-120h80v120h120v80H160Zm80-160v-320h480v320H240Zm80-80h320v-160H320v160Zm0 0v-160 160Z"/></svg>
|
After Width: | Height: | Size: 390 B |
|
@ -5764,19 +5764,34 @@ a.status-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&__close,
|
&__buttons {
|
||||||
&__zoom-button {
|
position: absolute;
|
||||||
color: rgba($white, 0.7);
|
inset-inline-end: 8px;
|
||||||
|
top: 8px;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&:hover,
|
.icon-button {
|
||||||
&:focus,
|
color: rgba($white, 0.7);
|
||||||
&:active {
|
padding: 8px;
|
||||||
color: $white;
|
|
||||||
background-color: rgba($white, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
.icon {
|
||||||
background-color: rgba($white, 0.3);
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
color: $white;
|
||||||
|
background-color: rgba($white, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: rgba($white, 0.3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5937,28 +5952,6 @@ a.status-card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-modal__close {
|
|
||||||
position: absolute;
|
|
||||||
inset-inline-end: 8px;
|
|
||||||
top: 8px;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-modal__zoom-button {
|
|
||||||
position: absolute;
|
|
||||||
inset-inline-end: 64px;
|
|
||||||
top: 8px;
|
|
||||||
z-index: 100;
|
|
||||||
pointer-events: auto;
|
|
||||||
transition: opacity 0.3s linear;
|
|
||||||
will-change: opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-modal__zoom-button--hidden {
|
|
||||||
pointer-events: none;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.onboarding-modal,
|
.onboarding-modal,
|
||||||
.error-modal,
|
.error-modal,
|
||||||
.embed-modal {
|
.embed-modal {
|
||||||
|
|
4
app/javascript/svg-icons/actual_size.svg
Normal file
4
app/javascript/svg-icons/actual_size.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3.1002 20.2C2.46686 20.2 1.9252 19.9833 1.4752 19.55C1.04186 19.1 0.825195 18.5583 0.825195 17.925V6.07499C0.825195 5.44165 1.04186 4.90832 1.4752 4.47499C1.9252 4.02499 2.46686 3.79999 3.1002 3.79999H20.9002C21.5335 3.79999 22.0669 4.02499 22.5002 4.47499C22.9502 4.90832 23.1752 5.44165 23.1752 6.07499V17.925C23.1752 18.5583 22.9502 19.1 22.5002 19.55C22.0669 19.9833 21.5335 20.2 20.9002 20.2H3.1002ZM3.1002 17.925H20.9002V6.07499H3.1002V17.925Z" fill="black"/>
|
||||||
|
<path d="M8.12522 16V9.85782H6.25043V8H10V16H8.12522ZM11.1461 16V14.1422H13.0209V16H11.1461ZM15.1252 16V9.85782H13.2313V8H17V16H15.1252ZM11.1461 12.8578V11H13.0209V12.8578H11.1461Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 776 B |
Loading…
Reference in a new issue