2019-06-13 02:40:58 +08:00
|
|
|
import React, { PureComponent } from 'react';
|
2018-08-23 01:49:33 +08:00
|
|
|
import PropTypes from 'prop-types';
|
2020-05-26 04:00:13 +08:00
|
|
|
import { defineMessages, injectIntl } from 'react-intl';
|
2021-11-08 21:09:56 +08:00
|
|
|
import Styled from './styles';
|
2018-09-28 22:42:07 +08:00
|
|
|
import HoldButton from './holdButton/component';
|
2018-08-23 01:49:33 +08:00
|
|
|
|
2018-08-29 00:09:52 +08:00
|
|
|
const DELAY_MILLISECONDS = 200;
|
2018-08-23 01:49:33 +08:00
|
|
|
const STEP_TIME = 100;
|
2022-05-26 07:25:53 +08:00
|
|
|
const ZOOM_INCREMENT = .1;
|
2018-08-23 01:49:33 +08:00
|
|
|
|
2019-02-07 00:42:12 +08:00
|
|
|
const intlMessages = defineMessages({
|
|
|
|
resetZoomLabel: {
|
|
|
|
id: 'app.presentation.presentationToolbar.zoomReset',
|
|
|
|
description: 'Reset zoom button label',
|
|
|
|
},
|
2019-02-07 00:51:40 +08:00
|
|
|
zoomInLabel: {
|
|
|
|
id: 'app.presentation.presentationToolbar.zoomInLabel',
|
2019-01-28 21:33:50 +08:00
|
|
|
description: 'Aria label for increment zoom level',
|
|
|
|
},
|
|
|
|
zoomInDesc: {
|
|
|
|
id: 'app.presentation.presentationToolbar.zoomInDesc',
|
|
|
|
description: 'Aria description for increment zoom level',
|
2019-02-07 00:51:40 +08:00
|
|
|
},
|
|
|
|
zoomOutLabel: {
|
|
|
|
id: 'app.presentation.presentationToolbar.zoomOutLabel',
|
|
|
|
description: 'Aria label for decrement zoom level',
|
|
|
|
},
|
2019-01-28 21:33:50 +08:00
|
|
|
zoomOutDesc: {
|
|
|
|
id: 'app.presentation.presentationToolbar.zoomOutDesc',
|
|
|
|
description: 'Aria description for decrement zoom level',
|
|
|
|
},
|
2019-06-06 23:52:05 +08:00
|
|
|
currentValue: {
|
|
|
|
id: 'app.submenu.application.currentSize',
|
|
|
|
description: 'current presentation zoom percentage aria description',
|
2019-01-28 21:33:50 +08:00
|
|
|
},
|
2019-02-07 00:42:12 +08:00
|
|
|
});
|
|
|
|
|
2019-06-13 02:40:58 +08:00
|
|
|
class ZoomTool extends PureComponent {
|
2018-08-23 01:49:33 +08:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.increment = this.increment.bind(this);
|
|
|
|
this.decrement = this.decrement.bind(this);
|
|
|
|
this.mouseDownHandler = this.mouseDownHandler.bind(this);
|
|
|
|
this.mouseUpHandler = this.mouseUpHandler.bind(this);
|
|
|
|
this.execInterval = this.execInterval.bind(this);
|
|
|
|
this.onChanger = this.onChanger.bind(this);
|
|
|
|
this.setInt = 0;
|
|
|
|
this.state = {
|
2019-02-07 00:05:24 +08:00
|
|
|
stateZoomValue: props.zoomValue,
|
|
|
|
initialstateZoomValue: props.zoomValue,
|
2018-08-23 01:49:33 +08:00
|
|
|
mouseHolding: false,
|
|
|
|
};
|
|
|
|
}
|
2019-02-06 22:03:30 +08:00
|
|
|
|
2018-08-23 01:49:33 +08:00
|
|
|
componentDidUpdate() {
|
2022-06-13 23:39:34 +08:00
|
|
|
const { zoomValue, tldrawAPI } = this.props;
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue } = this.state;
|
|
|
|
const isDifferent = zoomValue !== stateZoomValue;
|
2022-06-13 23:39:34 +08:00
|
|
|
if (isDifferent) {
|
|
|
|
this.onChanger(zoomValue);
|
|
|
|
if (tldrawAPI && zoomValue === 1 && tldrawAPI?.getPageState()?.camera?.zoom === 1) {
|
|
|
|
tldrawAPI?.zoomToFit();
|
|
|
|
}
|
|
|
|
}
|
2018-08-23 01:49:33 +08:00
|
|
|
}
|
|
|
|
|
2019-02-07 00:05:24 +08:00
|
|
|
onChanger(value) {
|
2018-08-23 01:49:33 +08:00
|
|
|
const {
|
|
|
|
maxBound,
|
|
|
|
minBound,
|
|
|
|
change,
|
2019-02-07 00:05:24 +08:00
|
|
|
zoomValue,
|
2018-08-23 01:49:33 +08:00
|
|
|
} = this.props;
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue } = this.state;
|
|
|
|
let newValue = value;
|
|
|
|
const isDifferent = newValue !== stateZoomValue;
|
2018-08-23 01:49:33 +08:00
|
|
|
|
|
|
|
if (newValue <= minBound) {
|
|
|
|
newValue = minBound;
|
|
|
|
} else if (newValue >= maxBound) {
|
|
|
|
newValue = maxBound;
|
|
|
|
}
|
|
|
|
|
2019-02-07 00:05:24 +08:00
|
|
|
const propsIsDifferente = zoomValue !== newValue;
|
2018-08-23 01:49:33 +08:00
|
|
|
if (isDifferent && propsIsDifferente) {
|
2019-02-07 00:05:24 +08:00
|
|
|
this.setState({ stateZoomValue: newValue }, () => {
|
2018-08-23 01:49:33 +08:00
|
|
|
change(newValue);
|
|
|
|
});
|
|
|
|
}
|
2019-02-07 00:05:24 +08:00
|
|
|
if (isDifferent && !propsIsDifferente) this.setState({ stateZoomValue: newValue });
|
2018-08-23 01:49:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
increment() {
|
|
|
|
const {
|
|
|
|
step,
|
|
|
|
} = this.props;
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue } = this.state;
|
|
|
|
const increaseZoom = stateZoomValue + step;
|
2018-08-23 01:49:33 +08:00
|
|
|
this.onChanger(increaseZoom);
|
|
|
|
}
|
2019-02-06 22:03:30 +08:00
|
|
|
|
2018-08-23 01:49:33 +08:00
|
|
|
decrement() {
|
|
|
|
const {
|
|
|
|
step,
|
|
|
|
} = this.props;
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue } = this.state;
|
|
|
|
const decreaseZoom = stateZoomValue - step;
|
2018-08-23 01:49:33 +08:00
|
|
|
this.onChanger(decreaseZoom);
|
|
|
|
}
|
|
|
|
|
|
|
|
execInterval(inc) {
|
2019-02-06 22:03:30 +08:00
|
|
|
const { mouseHolding } = this.state;
|
2018-08-23 01:49:33 +08:00
|
|
|
const exec = inc ? this.increment : this.decrement;
|
|
|
|
|
|
|
|
const interval = () => {
|
|
|
|
clearInterval(this.setInt);
|
|
|
|
this.setInt = setInterval(exec, STEP_TIME);
|
|
|
|
};
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2019-02-06 22:03:30 +08:00
|
|
|
if (mouseHolding) {
|
2018-08-23 01:49:33 +08:00
|
|
|
interval();
|
|
|
|
}
|
|
|
|
}, DELAY_MILLISECONDS);
|
|
|
|
}
|
|
|
|
|
|
|
|
mouseDownHandler(bool) {
|
|
|
|
this.setState({
|
|
|
|
mouseHolding: true,
|
|
|
|
}, () => {
|
|
|
|
this.execInterval(bool);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
mouseUpHandler() {
|
|
|
|
this.setState({
|
|
|
|
mouseHolding: false,
|
|
|
|
}, () => clearInterval(this.setInt));
|
|
|
|
}
|
|
|
|
|
2019-02-06 22:03:30 +08:00
|
|
|
resetZoom() {
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue, initialstateZoomValue } = this.state;
|
|
|
|
if (stateZoomValue !== initialstateZoomValue) this.onChanger(initialstateZoomValue);
|
2019-02-06 22:03:30 +08:00
|
|
|
}
|
|
|
|
|
2018-08-23 01:49:33 +08:00
|
|
|
render() {
|
|
|
|
const {
|
2019-02-07 00:05:24 +08:00
|
|
|
zoomValue,
|
2018-08-23 01:49:33 +08:00
|
|
|
minBound,
|
|
|
|
maxBound,
|
2019-02-07 00:42:12 +08:00
|
|
|
intl,
|
2019-06-27 00:29:34 +08:00
|
|
|
isMeteorConnected,
|
2019-06-06 23:04:02 +08:00
|
|
|
step,
|
2022-05-16 10:35:17 +08:00
|
|
|
tldrawAPI,
|
2022-06-07 04:30:34 +08:00
|
|
|
slidePosition,
|
2018-08-23 01:49:33 +08:00
|
|
|
} = this.props;
|
2019-02-07 00:05:24 +08:00
|
|
|
const { stateZoomValue } = this.state;
|
2019-06-06 23:52:05 +08:00
|
|
|
|
|
|
|
let zoomOutAriaLabel = intl.formatMessage(intlMessages.zoomOutLabel);
|
|
|
|
if (zoomValue > minBound) {
|
|
|
|
zoomOutAriaLabel += ` ${intl.formatNumber(((zoomValue - step) / 100), { style: 'percent' })}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
let zoomInAriaLabel = intl.formatMessage(intlMessages.zoomInLabel);
|
|
|
|
if (zoomValue < maxBound) {
|
|
|
|
zoomInAriaLabel += ` ${intl.formatNumber(((zoomValue + step) / 100), { style: 'percent' })}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
const stateZoomPct = intl.formatNumber((stateZoomValue / 100), { style: 'percent' });
|
|
|
|
|
2018-08-23 01:49:33 +08:00
|
|
|
return (
|
|
|
|
[
|
2018-09-28 22:42:07 +08:00
|
|
|
(
|
|
|
|
<HoldButton
|
|
|
|
key="zoom-tool-1"
|
|
|
|
exec={this.decrement}
|
2019-02-07 00:05:24 +08:00
|
|
|
value={zoomValue}
|
2018-09-28 22:42:07 +08:00
|
|
|
minBound={minBound}
|
|
|
|
>
|
2021-11-08 21:09:56 +08:00
|
|
|
<Styled.DecreaseZoomButton
|
2018-09-28 22:42:07 +08:00
|
|
|
key="zoom-tool-1"
|
2019-06-06 23:04:02 +08:00
|
|
|
aria-describedby="zoomOutDescription"
|
2019-06-06 23:52:05 +08:00
|
|
|
aria-label={zoomOutAriaLabel}
|
2019-02-07 00:51:40 +08:00
|
|
|
label={intl.formatMessage(intlMessages.zoomOutLabel)}
|
2019-02-12 21:35:52 +08:00
|
|
|
icon="substract"
|
2022-05-26 07:25:53 +08:00
|
|
|
onClick={() => {
|
|
|
|
tldrawAPI.zoomTo(tldrawAPI?.getPageState()?.camera?.zoom - ZOOM_INCREMENT);
|
|
|
|
}}
|
2019-06-27 00:29:34 +08:00
|
|
|
disabled={(zoomValue <= minBound) || !isMeteorConnected}
|
2018-09-28 22:42:07 +08:00
|
|
|
hideLabel
|
|
|
|
/>
|
2019-06-06 23:04:02 +08:00
|
|
|
<div id="zoomOutDescription" hidden>{intl.formatMessage(intlMessages.zoomOutDesc)}</div>
|
2018-09-28 22:42:07 +08:00
|
|
|
</HoldButton>
|
|
|
|
),
|
2018-08-23 01:49:33 +08:00
|
|
|
(
|
2019-06-06 23:52:05 +08:00
|
|
|
<span key="zoom-tool-2">
|
2021-11-08 21:09:56 +08:00
|
|
|
<Styled.ResetZoomButton
|
2019-06-06 23:52:05 +08:00
|
|
|
aria-label={intl.formatMessage(intlMessages.resetZoomLabel)}
|
|
|
|
aria-describedby="resetZoomDescription"
|
2019-06-27 00:29:34 +08:00
|
|
|
disabled={(stateZoomValue === minBound) || !isMeteorConnected}
|
2019-06-06 23:52:05 +08:00
|
|
|
color="default"
|
2022-06-07 04:30:34 +08:00
|
|
|
customIcon={`${parseInt(slidePosition?.zoom * 100)}%`}
|
2019-06-06 23:52:05 +08:00
|
|
|
size="md"
|
2022-05-16 10:35:17 +08:00
|
|
|
onClick={() => tldrawAPI?.zoomTo(1)}
|
2019-06-06 23:52:05 +08:00
|
|
|
label={intl.formatMessage(intlMessages.resetZoomLabel)}
|
|
|
|
hideLabel
|
|
|
|
/>
|
|
|
|
<div id="resetZoomDescription" hidden>
|
|
|
|
{intl.formatMessage(intlMessages.currentValue, ({ 0: stateZoomPct }))}
|
|
|
|
</div>
|
|
|
|
</span>
|
2018-08-23 01:49:33 +08:00
|
|
|
),
|
2018-09-28 22:42:07 +08:00
|
|
|
(
|
|
|
|
<HoldButton
|
|
|
|
key="zoom-tool-3"
|
|
|
|
exec={this.increment}
|
2019-02-07 00:05:24 +08:00
|
|
|
value={zoomValue}
|
2018-09-28 22:42:07 +08:00
|
|
|
maxBound={maxBound}
|
|
|
|
>
|
2021-11-08 21:09:56 +08:00
|
|
|
<Styled.IncreaseZoomButton
|
2018-09-28 22:42:07 +08:00
|
|
|
key="zoom-tool-3"
|
2019-06-06 23:04:02 +08:00
|
|
|
aria-describedby="zoomInDescription"
|
2019-06-06 23:52:05 +08:00
|
|
|
aria-label={zoomInAriaLabel}
|
2019-02-07 00:51:40 +08:00
|
|
|
label={intl.formatMessage(intlMessages.zoomInLabel)}
|
2022-01-20 21:03:18 +08:00
|
|
|
data-test="zoomInBtn"
|
2019-02-12 21:35:52 +08:00
|
|
|
icon="add"
|
2022-05-26 07:25:53 +08:00
|
|
|
onClick={() => {
|
|
|
|
tldrawAPI.zoomTo(tldrawAPI?.getPageState()?.camera?.zoom + ZOOM_INCREMENT);
|
|
|
|
}}
|
2019-06-27 00:29:34 +08:00
|
|
|
disabled={(zoomValue >= maxBound) || !isMeteorConnected}
|
2018-09-28 22:42:07 +08:00
|
|
|
hideLabel
|
|
|
|
/>
|
2019-06-06 23:04:02 +08:00
|
|
|
<div id="zoomInDescription" hidden>{intl.formatMessage(intlMessages.zoomInDesc)}</div>
|
2018-09-28 22:42:07 +08:00
|
|
|
</HoldButton>
|
|
|
|
),
|
2018-08-23 01:49:33 +08:00
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const propTypes = {
|
2020-05-26 04:00:13 +08:00
|
|
|
intl: PropTypes.object.isRequired,
|
2019-02-07 00:05:24 +08:00
|
|
|
zoomValue: PropTypes.number.isRequired,
|
2018-08-23 01:49:33 +08:00
|
|
|
change: PropTypes.func.isRequired,
|
|
|
|
minBound: PropTypes.number.isRequired,
|
|
|
|
maxBound: PropTypes.number.isRequired,
|
|
|
|
step: PropTypes.number.isRequired,
|
2019-06-27 00:29:34 +08:00
|
|
|
isMeteorConnected: PropTypes.bool.isRequired,
|
2018-08-23 01:49:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
ZoomTool.propTypes = propTypes;
|
2019-02-07 00:42:12 +08:00
|
|
|
|
|
|
|
export default injectIntl(ZoomTool);
|