diff --git a/README.md b/README.md index c3b969b..d995989 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ See [#428](https://github.com/ValentinH/react-easy-crop/issues/428), [#409](http | `setMediaSize` | `(size: MediaSize) => void` | | [Advanced Usage] Used to expose the `mediaSize` value for use with the `getInitialCropFromCroppedAreaPixels` and `getInitialCropFromCroppedAreaPercentages` functions. See [this CodeSandbox instance](https://codesandbox.io/s/react-easy-crop-forked-3v0hi3) for a simple example. | | `setCropSize` | `(size: Size) => void` | | [Advanced Usage] Used to expose the `cropSize` value for use with the `getInitialCropFromCroppedAreaPixels` and `getInitialCropFromCroppedAreaPercentages` functions. See [this CodeSandbox instance](https://codesandbox.io/s/react-easy-crop-forked-3v0hi3) for a simple example. | | `nonce` | string | | The nonce to add to the style tag when the styles are auto injected. | +| `keyboardStep` | number | | The keyboardStep prop controls the number of pixels the crop area moves with each press of an arrow key when using keyboard navigation. Defaults to 1. | diff --git a/src/Cropper.tsx b/src/Cropper.tsx index 912f142..93d2660 100644 --- a/src/Cropper.tsx +++ b/src/Cropper.tsx @@ -62,6 +62,7 @@ export type CropperProps = { setMediaSize?: (size: MediaSize) => void setCropSize?: (size: Size) => void nonce?: string + keyboardStep: number } type State = { @@ -72,6 +73,7 @@ type State = { const MIN_ZOOM = 1 const MAX_ZOOM = 3 +const KEYBOARD_STEP = 1 type GestureEvent = UIEvent & { rotation: number @@ -96,6 +98,7 @@ class Cropper extends React.Component { zoomSpeed: 1, restrictPosition: true, zoomWithScroll: true, + keyboardStep: KEYBOARD_STEP, } imageRef: React.RefObject = React.createRef() @@ -727,6 +730,38 @@ class Cropper extends React.Component { this.emitCropData() } + onKeyDown = (event: React.KeyboardEvent) => { + const { crop, onCropChange, keyboardStep, zoom, rotation } = this.props + const step = keyboardStep + + if (!this.state.cropSize) return + + let newCrop = { ...crop } + + switch (event.key) { + case 'ArrowUp': + newCrop.y -= step + break + case 'ArrowDown': + newCrop.y += step + break + case 'ArrowLeft': + newCrop.x -= step + break + case 'ArrowRight': + newCrop.x += step + break + default: + return + } + + if (this.props.restrictPosition) { + newCrop = restrictPosition(newCrop, this.mediaSize, this.state.cropSize, zoom, rotation) + } + + onCropChange(newCrop) + } + render() { const { image, @@ -810,6 +845,8 @@ class Cropper extends React.Component { width: this.state.cropSize.width, height: this.state.cropSize.height, }} + tabIndex={0} + onKeyDown={this.onKeyDown} data-testid="cropper" className={classNames( 'reactEasyCrop_CropArea',