import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; import Styled from './styles'; import HoldButton from './holdButton/component'; const DELAY_MILLISECONDS = 200; const STEP_TIME = 100; const ZOOM_INCREMENT = .1; const intlMessages = defineMessages({ resetZoomLabel: { id: 'app.presentation.presentationToolbar.zoomReset', description: 'Reset zoom button label', }, zoomInLabel: { id: 'app.presentation.presentationToolbar.zoomInLabel', description: 'Aria label for increment zoom level', }, zoomInDesc: { id: 'app.presentation.presentationToolbar.zoomInDesc', description: 'Aria description for increment zoom level', }, zoomOutLabel: { id: 'app.presentation.presentationToolbar.zoomOutLabel', description: 'Aria label for decrement zoom level', }, zoomOutDesc: { id: 'app.presentation.presentationToolbar.zoomOutDesc', description: 'Aria description for decrement zoom level', }, currentValue: { id: 'app.submenu.application.currentSize', description: 'current presentation zoom percentage aria description', }, }); class ZoomTool extends PureComponent { 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 = { stateZoomValue: props.zoomValue, initialstateZoomValue: props.zoomValue, mouseHolding: false, }; } componentDidUpdate() { const { zoomValue, tldrawAPI } = this.props; const { stateZoomValue } = this.state; const isDifferent = zoomValue !== stateZoomValue; if (isDifferent) { this.onChanger(zoomValue); if (tldrawAPI && zoomValue === 1 && tldrawAPI?.getPageState()?.camera?.zoom === 1) { tldrawAPI?.zoomToFit(); } } } onChanger(value) { const { maxBound, minBound, change, zoomValue, } = this.props; const { stateZoomValue } = this.state; let newValue = value; const isDifferent = newValue !== stateZoomValue; if (newValue <= minBound) { newValue = minBound; } else if (newValue >= maxBound) { newValue = maxBound; } const propsIsDifferente = zoomValue !== newValue; if (isDifferent && propsIsDifferente) { this.setState({ stateZoomValue: newValue }, () => { change(newValue); }); } if (isDifferent && !propsIsDifferente) this.setState({ stateZoomValue: newValue }); } increment() { const { step, } = this.props; const { stateZoomValue } = this.state; const increaseZoom = stateZoomValue + step; this.onChanger(increaseZoom); } decrement() { const { step, } = this.props; const { stateZoomValue } = this.state; const decreaseZoom = stateZoomValue - step; this.onChanger(decreaseZoom); } execInterval(inc) { const { mouseHolding } = this.state; const exec = inc ? this.increment : this.decrement; const interval = () => { clearInterval(this.setInt); this.setInt = setInterval(exec, STEP_TIME); }; setTimeout(() => { if (mouseHolding) { interval(); } }, DELAY_MILLISECONDS); } mouseDownHandler(bool) { this.setState({ mouseHolding: true, }, () => { this.execInterval(bool); }); } mouseUpHandler() { this.setState({ mouseHolding: false, }, () => clearInterval(this.setInt)); } resetZoom() { const { stateZoomValue, initialstateZoomValue } = this.state; if (stateZoomValue !== initialstateZoomValue) this.onChanger(initialstateZoomValue); } render() { const { zoomValue, minBound, maxBound, intl, isMeteorConnected, step, tldrawAPI, slidePosition, } = this.props; const { stateZoomValue } = this.state; 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' }); return ( [ ( { tldrawAPI.zoomTo(tldrawAPI?.getPageState()?.camera?.zoom - ZOOM_INCREMENT); }} disabled={(zoomValue <= minBound) || !isMeteorConnected} hideLabel /> {intl.formatMessage(intlMessages.zoomOutDesc)} ), ( tldrawAPI?.zoomTo(1)} label={intl.formatMessage(intlMessages.resetZoomLabel)} hideLabel /> {intl.formatMessage(intlMessages.currentValue, ({ 0: stateZoomPct }))} ), ( { tldrawAPI.zoomTo(tldrawAPI?.getPageState()?.camera?.zoom + ZOOM_INCREMENT); }} disabled={(zoomValue >= maxBound) || !isMeteorConnected} hideLabel /> {intl.formatMessage(intlMessages.zoomInDesc)} ), ] ); } } const propTypes = { intl: PropTypes.object.isRequired, zoomValue: PropTypes.number.isRequired, change: PropTypes.func.isRequired, minBound: PropTypes.number.isRequired, maxBound: PropTypes.number.isRequired, step: PropTypes.number.isRequired, isMeteorConnected: PropTypes.bool.isRequired, }; ZoomTool.propTypes = propTypes; export default injectIntl(ZoomTool);