add cropperjs
This commit is contained in:
parent
73854fe5be
commit
3429b36502
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cropperjs": "1.6.1",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"i18next": "22.4.15",
|
"i18next": "22.4.15",
|
||||||
"i18next-icu": "2.3.0",
|
"i18next-icu": "2.3.0",
|
||||||
|
@ -7865,6 +7866,11 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/cropperjs": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-F4wsi+XkDHCOMrHMYjrTEE4QBOrsHHN5/2VsVAaRq8P7E5z7xQpT75S+f/9WikmBEailas3+yo+6zPIomW+NOA=="
|
||||||
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"typecheck:watch": "tsc --noEmit --watch"
|
"typecheck:watch": "tsc --noEmit --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cropperjs": "1.6.1",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"i18next": "22.4.15",
|
"i18next": "22.4.15",
|
||||||
"i18next-icu": "2.3.0",
|
"i18next-icu": "2.3.0",
|
||||||
|
|
|
@ -1 +1,105 @@
|
||||||
export { ImageCropper } from './ImageCropper'
|
import { createSignal, Show } from 'solid-js'
|
||||||
|
import { createStore } from 'solid-js/store'
|
||||||
|
import Cropper from 'cropperjs'
|
||||||
|
|
||||||
|
import styles from './ImageCropper.module.scss'
|
||||||
|
|
||||||
|
export const 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,105 +1 @@
|
||||||
import { createSignal, Show } from 'solid-js'
|
export { ImageCropper } from './ImageCropper'
|
||||||
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