Skip to content

Commit 11feb73

Browse files
committed
finish
1 parent f5d870c commit 11feb73

File tree

2 files changed

+50
-53
lines changed

2 files changed

+50
-53
lines changed

templates/shared/avatar_upload_crop.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
{{- /* the cropper-panel must be next sibling of the input "avatar" */ -}}
55
<div class="cropper-panel tw-hidden">
66
<div class="tw-my-2">{{ctx.Locale.Tr "settings.cropper_prompt"}}</div>
7-
<div class="cropper-wrapper"><img class="cropper-source" src alt></div>
7+
<div class="cropper-wrapper"></div>
88
</div>
Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,67 @@
1-
import {createElementFromHTML, showElem, type DOMEvent} from '../../utils/dom.ts';
2-
import type {CropperCanvas, CropperImage} from 'cropperjs';
1+
import {createElementFromHTML, hideElem, showElem, type DOMEvent} from '../../utils/dom.ts';
2+
import {debounce} from 'perfect-debounce';
3+
import type {CropperCanvas, CropperSelection} from 'cropperjs';
34

45
type CropperOpts = {
56
container: HTMLElement,
6-
imageSource: HTMLImageElement,
7+
wrapper: HTMLDivElement,
78
fileInput: HTMLInputElement,
89
}
910

10-
async function initCompCropper({container, fileInput, imageSource}: CropperOpts) {
11+
async function initCompCropper({container, fileInput, wrapper}: CropperOpts) {
1112
await import(/* webpackChunkName: "cropperjs" */'cropperjs');
12-
let currentFileName = '';
13-
let currentFileLastModified = 0;
1413

15-
const canvasEl = createElementFromHTML<CropperCanvas>(`
16-
<cropper-canvas background theme-color="var(--color-primary)">
17-
<cropper-image src="${imageSource.src}" scalable skewable translatable></cropper-image>
18-
<cropper-shade hidden></cropper-shade>
19-
<cropper-handle action="select" plain></cropper-handle>
20-
<cropper-selection initial-coverage="0.5" initial-aspect-ratio="1" movable resizable outlined>
21-
<cropper-grid role="grid" covered></cropper-grid>
22-
<cropper-crosshair centered></cropper-crosshair>
23-
<cropper-handle action="move" theme-color="#ffffff23"></cropper-handle>
24-
<cropper-handle action="n-resize"></cropper-handle>
25-
<cropper-handle action="e-resize"></cropper-handle>
26-
<cropper-handle action="s-resize"></cropper-handle>
27-
<cropper-handle action="w-resize"></cropper-handle>
28-
<cropper-handle action="ne-resize"></cropper-handle>
29-
<cropper-handle action="nw-resize"></cropper-handle>
30-
<cropper-handle action="se-resize"></cropper-handle>
31-
<cropper-handle action="sw-resize"></cropper-handle>
32-
</cropper-selection>
33-
</cropper-canvas>
34-
`);
35-
36-
const imgEl = canvasEl.querySelector<CropperImage>('cropper-image');
14+
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
15+
if (!e.target.files?.length) {
16+
wrapper.replaceChildren();
17+
hideElem(container);
18+
return;
19+
}
3720

38-
canvasEl.addEventListener('action', async (e) => {
39-
const canvas = await (e.target as CropperCanvas).$toCanvas();
40-
canvas.toBlob((blob) => {
41-
const croppedFileName = currentFileName.replace(/\.[^.]{3,4}$/, '.png');
42-
const croppedFile = new File([blob], croppedFileName, {type: 'image/png', lastModified: currentFileLastModified});
43-
const dataTransfer = new DataTransfer();
44-
dataTransfer.items.add(croppedFile);
45-
fileInput.files = dataTransfer.files;
46-
});
47-
});
21+
const [file] = e.target.files;
22+
const objectUrl = URL.createObjectURL(file);
23+
const canvasEl = createElementFromHTML<CropperCanvas>(`
24+
<cropper-canvas theme-color="var(--color-primary)">
25+
<cropper-image src="${objectUrl}" scalable skewable translatable></cropper-image>
26+
<cropper-shade hidden></cropper-shade>
27+
<cropper-handle action="select" plain></cropper-handle>
28+
<cropper-selection aspect-ratio="1" movable resizable>
29+
<cropper-handle action="move" theme-color="transparent"></cropper-handle>
30+
<cropper-handle action="n-resize"></cropper-handle>
31+
<cropper-handle action="e-resize"></cropper-handle>
32+
<cropper-handle action="s-resize"></cropper-handle>
33+
<cropper-handle action="w-resize"></cropper-handle>
34+
<cropper-handle action="ne-resize"></cropper-handle>
35+
<cropper-handle action="nw-resize"></cropper-handle>
36+
<cropper-handle action="se-resize"></cropper-handle>
37+
<cropper-handle action="sw-resize"></cropper-handle>
38+
</cropper-selection>
39+
</cropper-canvas>
40+
`);
41+
canvasEl.querySelector<CropperSelection>('cropper-selection').addEventListener('change', debounce(async (e) => {
42+
const selection = e.target as CropperSelection;
43+
if (!selection.width || !selection.height) return;
44+
const canvas = await selection.$toCanvas();
4845

49-
imageSource.replaceWith(canvasEl);
46+
canvas.toBlob((blob) => {
47+
const dataTransfer = new DataTransfer();
48+
dataTransfer.items.add(new File(
49+
[blob],
50+
file.name.replace(/\.[^.]{3,4}$/, '.png'),
51+
{type: 'image/png', lastModified: file.lastModified},
52+
));
53+
fileInput.files = dataTransfer.files;
54+
});
55+
}, 200));
5056

51-
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
52-
const files = e.target.files;
53-
if (files?.length > 0) {
54-
currentFileName = files[0].name;
55-
currentFileLastModified = files[0].lastModified;
56-
const fileURL = URL.createObjectURL(files[0]);
57-
imageSource.src = fileURL;
58-
// @ts-expect-error - https://github.com/go-gitea/gitea/pull/33827
59-
imgEl.src = fileURL;
60-
showElem(container);
61-
}
57+
wrapper.replaceChildren(canvasEl);
58+
showElem(container);
6259
});
6360
}
6461

6562
export async function initAvatarUploaderWithCropper(fileInput: HTMLInputElement) {
6663
const panel = fileInput.nextElementSibling as HTMLElement;
6764
if (!panel?.matches('.cropper-panel')) throw new Error('Missing cropper panel for avatar uploader');
68-
const imageSource = panel.querySelector<HTMLImageElement>('.cropper-source');
69-
await initCompCropper({container: panel, fileInput, imageSource});
65+
const wrapper = panel.querySelector<HTMLImageElement>('.cropper-wrapper');
66+
await initCompCropper({container: panel, fileInput, wrapper});
7067
}

0 commit comments

Comments
 (0)