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',