prepare image crop component
This commit is contained in:
parent
439e27c603
commit
73854fe5be
1
src/components/_shared/ImageCropper/ImageCropper.tsx
Normal file
1
src/components/_shared/ImageCropper/ImageCropper.tsx
Normal file
|
@ -0,0 +1 @@
|
|||
export { ImageCropper } from './ImageCropper'
|
105
src/components/_shared/ImageCropper/index.tsx
Normal file
105
src/components/_shared/ImageCropper/index.tsx
Normal file
|
@ -0,0 +1,105 @@
|
|||
import { createSignal, Show } from 'solid-js'
|
||||
import { createStore } from 'solid-js/store'
|
||||
import Cropper from 'cropperjs'
|
||||
|
||||
import styles from './ImageCropper.module.scss'
|
||||
|
||||
export default function ImageCropper(props) {
|
||||
let cropImage
|
||||
const [state, setState] = createStore({
|
||||
error: null,
|
||||
loading: false,
|
||||
file: {},
|
||||
croppedImage: null,
|
||||
}),
|
||||
[dropZoneActive, setDropZoneActive] = createSignal(false),
|
||||
[uploading, setUploading] = createSignal(false),
|
||||
[preview, setPreview] = createSignal(null),
|
||||
[cropper, setCropper] = createSignal(null),
|
||||
noPropagate = (e) => {
|
||||
e.preventDefault()
|
||||
},
|
||||
uploadFile = async (file) => {
|
||||
if (!file) return
|
||||
setUploading(true)
|
||||
setState('loading', true)
|
||||
setState('file', file)
|
||||
try {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
setPreview(e.target.result)
|
||||
setCropper(
|
||||
new Cropper(cropImage, {
|
||||
aspectRatio: 1 / 1,
|
||||
viewMode: 1,
|
||||
rotatable: false,
|
||||
}),
|
||||
)
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
} catch (e) {
|
||||
console.error('upload failed', e)
|
||||
const message = e instanceof Error ? e.message : String(e)
|
||||
setState('error', message)
|
||||
}
|
||||
setState('loading', false)
|
||||
setUploading(false)
|
||||
},
|
||||
handleFileDrop = async (e) => {
|
||||
e.preventDefault()
|
||||
setDropZoneActive(false)
|
||||
uploadFile(e.dataTransfer.files[0])
|
||||
},
|
||||
handleFileInput = async (e) => {
|
||||
e.preventDefault()
|
||||
uploadFile(e.currentTarget.files[0])
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={preview() !== null}>
|
||||
<div>
|
||||
<div>
|
||||
<img ref={cropImage} src={preview()} alt="cropper" class="block max-w-full h-96 w-96" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-x-1.5 rounded-md bg-indigo-600 py-2 px-3 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 mt-2"
|
||||
onClick={() => {
|
||||
setState('croppedImage', cropper().getCroppedCanvas().toDataURL(state.file.type))
|
||||
props.saveImage(state)
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={preview() === null}>
|
||||
<form class="min-h-96 min-w-96">
|
||||
<div
|
||||
id="dropzone"
|
||||
class={`${dropZoneActive() ? 'bg-green-100' : ''} ${
|
||||
uploading() && 'opacity-50'
|
||||
} place-content-center place-items-center h-96 w-96 border-2 border-gray-300 border-dashed rounded-md sm:flex p-2 m-2`}
|
||||
onDragEnter={() => (uploading() ? undefined : setDropZoneActive(true))}
|
||||
onDragLeave={() => setDropZoneActive(false)}
|
||||
onDragOver={noPropagate}
|
||||
onDrop={(event) => (uploading() ? noPropagate(event) : handleFileDrop(event))}
|
||||
>
|
||||
<div class="">upload</div>
|
||||
<input
|
||||
id="image-upload"
|
||||
name="file"
|
||||
type="file"
|
||||
disabled={uploading()}
|
||||
multiple={false}
|
||||
onInput={handleFileInput}
|
||||
class="sr-only"
|
||||
/>
|
||||
</div>
|
||||
<div class="h-8" />
|
||||
</form>
|
||||
</Show>
|
||||
</>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user