From 5f6a353ead6afa3ad04c0782779b64ffc0d1d110 Mon Sep 17 00:00:00 2001 From: Jose Guardiola Argumedo Date: Thu, 10 Oct 2024 17:12:14 -0600 Subject: [PATCH 1/3] feat: add keyboard accessibility to move crop area with arrow keys --- examples/src/index.tsx | 2 +- src/Cropper.tsx | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/examples/src/index.tsx b/examples/src/index.tsx index d56079f..af3f654 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -74,7 +74,7 @@ class App extends React.Component<{}, State> { const hashArray = window.location.hash.slice(1).split(',') if (hashArray.length === hashNames.length) { - const hashInfo = {} as Record + const hashInfo = {} as Record<(typeof hashNames)[number], string> hashNames.forEach((key, index) => (hashInfo[key] = hashArray[index])) const { 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', From a7510e1b83ac03411391882f6e55431eb489444c Mon Sep 17 00:00:00 2001 From: Jose Guardiola Argumedo Date: Thu, 10 Oct 2024 18:26:01 -0600 Subject: [PATCH 2/3] chore: remove uneeded formated line --- examples/src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/index.tsx b/examples/src/index.tsx index af3f654..d56079f 100644 --- a/examples/src/index.tsx +++ b/examples/src/index.tsx @@ -74,7 +74,7 @@ class App extends React.Component<{}, State> { const hashArray = window.location.hash.slice(1).split(',') if (hashArray.length === hashNames.length) { - const hashInfo = {} as Record<(typeof hashNames)[number], string> + const hashInfo = {} as Record hashNames.forEach((key, index) => (hashInfo[key] = hashArray[index])) const { From 580411fe503daf132784a886d2a69d3a7f1ffdde Mon Sep 17 00:00:00 2001 From: Jose Guardiola Argumedo Date: Fri, 11 Oct 2024 14:38:06 -0600 Subject: [PATCH 3/3] chore: add keyboardStep prop reference to README file --- README.md | 1 + 1 file changed, 1 insertion(+) 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. |