@eagami/ui 5.15.0 → 5.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/eagami-ui.mjs +48 -20
- package/fesm2022/eagami-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/eagami-ui.d.ts +3 -1
package/fesm2022/eagami-ui.mjs
CHANGED
|
@@ -5827,6 +5827,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
5827
5827
|
}]
|
|
5828
5828
|
}], ctorParameters: () => [], propDecorators: { eaTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "eaTooltip", required: true }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }] } });
|
|
5829
5829
|
|
|
5830
|
+
const ZOOM_TOLERANCE = 1e-3;
|
|
5831
|
+
const OFFSET_TOLERANCE = 0.5;
|
|
5830
5832
|
/**
|
|
5831
5833
|
* Canvas-based image editor for cropping avatars. Supports drag-and-drop
|
|
5832
5834
|
* upload, mouse/touch panning, zoom via slider or scroll wheel, and exports
|
|
@@ -5883,7 +5885,7 @@ class AvatarEditorComponent {
|
|
|
5883
5885
|
...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
5884
5886
|
zoom = signal(1, /* @ts-ignore */
|
|
5885
5887
|
...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
5886
|
-
canRevert = computed(() => !this.isAtOriginal() && this.originalCaptured, /* @ts-ignore */
|
|
5888
|
+
canRevert = computed(() => !this.isAtOriginal() && this.originalCaptured(), /* @ts-ignore */
|
|
5887
5889
|
...(ngDevMode ? [{ debugName: "canRevert" }] : /* istanbul ignore next */ []));
|
|
5888
5890
|
/** True when the visible crop is darker than mid-grey; drives the `--on-light` class on the overlay so the "Change photo" affordance stays readable. */
|
|
5889
5891
|
isImageDark = signal(true, /* @ts-ignore */
|
|
@@ -5900,7 +5902,8 @@ class AvatarEditorComponent {
|
|
|
5900
5902
|
_suppressCropStateEmit = false;
|
|
5901
5903
|
isFetching = signal(false, /* @ts-ignore */
|
|
5902
5904
|
...(ngDevMode ? [{ debugName: "isFetching" }] : /* istanbul ignore next */ []));
|
|
5903
|
-
originalCaptured = false
|
|
5905
|
+
originalCaptured = signal(false, /* @ts-ignore */
|
|
5906
|
+
...(ngDevMode ? [{ debugName: "originalCaptured" }] : /* istanbul ignore next */ []));
|
|
5904
5907
|
originalImage = null;
|
|
5905
5908
|
originalCropState = null;
|
|
5906
5909
|
hostClasses = computed(() => ({
|
|
@@ -5916,15 +5919,15 @@ class AvatarEditorComponent {
|
|
|
5916
5919
|
const src = this.currentSrc();
|
|
5917
5920
|
if (!src) {
|
|
5918
5921
|
this.isFetching.set(false);
|
|
5919
|
-
if (!this.originalCaptured) {
|
|
5920
|
-
this.originalCaptured
|
|
5922
|
+
if (!this.originalCaptured()) {
|
|
5923
|
+
this.originalCaptured.set(true);
|
|
5921
5924
|
this.originalImage = null;
|
|
5922
5925
|
this.originalCropState = null;
|
|
5923
5926
|
this.isAtOriginal.set(true);
|
|
5924
5927
|
}
|
|
5925
5928
|
return;
|
|
5926
5929
|
}
|
|
5927
|
-
this.originalCaptured
|
|
5930
|
+
this.originalCaptured.set(false);
|
|
5928
5931
|
this.originalImage = null;
|
|
5929
5932
|
this.originalCropState = null;
|
|
5930
5933
|
this.loadFromUrl(src, untracked(() => this.cropState()) ?? null, true);
|
|
@@ -5935,6 +5938,7 @@ class AvatarEditorComponent {
|
|
|
5935
5938
|
// Offsets were computed against the previous frame, so a shrink would
|
|
5936
5939
|
// otherwise leave the image drawn outside it with a gap showing
|
|
5937
5940
|
this.clampOffset();
|
|
5941
|
+
untracked(() => this.syncAtOriginal());
|
|
5938
5942
|
this.draw();
|
|
5939
5943
|
}
|
|
5940
5944
|
});
|
|
@@ -6020,11 +6024,11 @@ class AvatarEditorComponent {
|
|
|
6020
6024
|
const dy = event.clientY - this.dragStartY;
|
|
6021
6025
|
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
|
|
6022
6026
|
this.hasDragged = true;
|
|
6023
|
-
this.isAtOriginal.set(false);
|
|
6024
6027
|
}
|
|
6025
6028
|
this.offsetX = this.initialOffsetX + dx;
|
|
6026
6029
|
this.offsetY = this.initialOffsetY + dy;
|
|
6027
6030
|
this.clampOffset();
|
|
6031
|
+
this.syncAtOriginal();
|
|
6028
6032
|
this.draw();
|
|
6029
6033
|
this.emitCropStateChange();
|
|
6030
6034
|
}
|
|
@@ -6046,11 +6050,11 @@ class AvatarEditorComponent {
|
|
|
6046
6050
|
const dy = touch.clientY - this.dragStartY;
|
|
6047
6051
|
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
|
|
6048
6052
|
this.hasDragged = true;
|
|
6049
|
-
this.isAtOriginal.set(false);
|
|
6050
6053
|
}
|
|
6051
6054
|
this.offsetX = this.initialOffsetX + dx;
|
|
6052
6055
|
this.offsetY = this.initialOffsetY + dy;
|
|
6053
6056
|
this.clampOffset();
|
|
6057
|
+
this.syncAtOriginal();
|
|
6054
6058
|
this.draw();
|
|
6055
6059
|
this.emitCropStateChange();
|
|
6056
6060
|
}
|
|
@@ -6111,18 +6115,17 @@ class AvatarEditorComponent {
|
|
|
6111
6115
|
}
|
|
6112
6116
|
if (handled) {
|
|
6113
6117
|
event.preventDefault();
|
|
6114
|
-
this.isAtOriginal.set(false);
|
|
6115
6118
|
this.clampOffset();
|
|
6119
|
+
this.syncAtOriginal();
|
|
6116
6120
|
this.draw();
|
|
6117
6121
|
this.emitCropStateChange();
|
|
6118
6122
|
}
|
|
6119
6123
|
}
|
|
6120
6124
|
/** Sets the zoom level, clamped to the configured `minZoom`/`maxZoom` range. */
|
|
6121
6125
|
setZoom(value) {
|
|
6122
|
-
this.
|
|
6123
|
-
const clamped = Math.min(this.maxZoom(), Math.max(this.minZoom(), value));
|
|
6124
|
-
this.zoom.set(Math.round(clamped * 100) / 100);
|
|
6126
|
+
this.applyZoom(value);
|
|
6125
6127
|
this.clampOffset();
|
|
6128
|
+
this.syncAtOriginal();
|
|
6126
6129
|
this.draw();
|
|
6127
6130
|
this.emitCropStateChange();
|
|
6128
6131
|
}
|
|
@@ -6139,12 +6142,31 @@ class AvatarEditorComponent {
|
|
|
6139
6142
|
this.offsetX = 0;
|
|
6140
6143
|
this.offsetY = 0;
|
|
6141
6144
|
this.clearCanvas();
|
|
6142
|
-
this.
|
|
6145
|
+
this.syncAtOriginal();
|
|
6143
6146
|
this.removed.emit();
|
|
6144
6147
|
}
|
|
6148
|
+
// Pan and zoom write to plain properties rather than signals, so the revert
|
|
6149
|
+
// control cannot derive its own state. Every handler that moves the crop calls
|
|
6150
|
+
// this instead, so returning to the committed values re-disables the control.
|
|
6151
|
+
// Offsets are recalculated on zoom and both values are floats, so the
|
|
6152
|
+
// comparison is a tolerance rather than equality
|
|
6153
|
+
syncAtOriginal() {
|
|
6154
|
+
if (!this.originalCaptured() || this.image !== this.originalImage) {
|
|
6155
|
+
this.isAtOriginal.set(false);
|
|
6156
|
+
return;
|
|
6157
|
+
}
|
|
6158
|
+
const crop = this.originalCropState;
|
|
6159
|
+
if (!crop) {
|
|
6160
|
+
this.isAtOriginal.set(!this.image);
|
|
6161
|
+
return;
|
|
6162
|
+
}
|
|
6163
|
+
this.isAtOriginal.set(Math.abs(this.zoom() - crop.zoom) < ZOOM_TOLERANCE &&
|
|
6164
|
+
Math.abs(this.offsetX - crop.offsetX) < OFFSET_TOLERANCE &&
|
|
6165
|
+
Math.abs(this.offsetY - crop.offsetY) < OFFSET_TOLERANCE);
|
|
6166
|
+
}
|
|
6145
6167
|
/** Marks the current image and crop state as the baseline for {@link revertImage}. */
|
|
6146
6168
|
captureOriginal() {
|
|
6147
|
-
this.originalCaptured
|
|
6169
|
+
this.originalCaptured.set(true);
|
|
6148
6170
|
this.originalImage = this.image;
|
|
6149
6171
|
this.originalCropState = this.image
|
|
6150
6172
|
? { zoom: this.zoom(), offsetX: this.offsetX, offsetY: this.offsetY }
|
|
@@ -6153,7 +6175,7 @@ class AvatarEditorComponent {
|
|
|
6153
6175
|
}
|
|
6154
6176
|
/** Restores the image and crop state captured by the most recent {@link captureOriginal}. */
|
|
6155
6177
|
revertImage() {
|
|
6156
|
-
if (!this.originalCaptured) {
|
|
6178
|
+
if (!this.originalCaptured()) {
|
|
6157
6179
|
return;
|
|
6158
6180
|
}
|
|
6159
6181
|
if (this.originalImage) {
|
|
@@ -6161,7 +6183,7 @@ class AvatarEditorComponent {
|
|
|
6161
6183
|
this.hasImage.set(true);
|
|
6162
6184
|
const crop = this.originalCropState;
|
|
6163
6185
|
if (crop) {
|
|
6164
|
-
this.
|
|
6186
|
+
this.applyZoom(crop.zoom);
|
|
6165
6187
|
this.offsetX = crop.offsetX;
|
|
6166
6188
|
this.offsetY = crop.offsetY;
|
|
6167
6189
|
this.clampOffset();
|
|
@@ -6231,7 +6253,7 @@ class AvatarEditorComponent {
|
|
|
6231
6253
|
this.image = img;
|
|
6232
6254
|
this.hasImage.set(true);
|
|
6233
6255
|
if (cropState) {
|
|
6234
|
-
this.
|
|
6256
|
+
this.applyZoom(cropState.zoom);
|
|
6235
6257
|
this.offsetX = cropState.offsetX;
|
|
6236
6258
|
this.offsetY = cropState.offsetY;
|
|
6237
6259
|
this.clampOffset();
|
|
@@ -6241,11 +6263,13 @@ class AvatarEditorComponent {
|
|
|
6241
6263
|
this.centerImage();
|
|
6242
6264
|
}
|
|
6243
6265
|
if (suppressEmit) {
|
|
6244
|
-
this.originalCaptured
|
|
6266
|
+
this.originalCaptured.set(true);
|
|
6245
6267
|
this.originalImage = img;
|
|
6246
|
-
this.originalCropState =
|
|
6247
|
-
|
|
6248
|
-
|
|
6268
|
+
this.originalCropState = {
|
|
6269
|
+
zoom: this.zoom(),
|
|
6270
|
+
offsetX: this.offsetX,
|
|
6271
|
+
offsetY: this.offsetY,
|
|
6272
|
+
};
|
|
6249
6273
|
this.isAtOriginal.set(true);
|
|
6250
6274
|
}
|
|
6251
6275
|
this.scheduleDrawAfterRender();
|
|
@@ -6324,6 +6348,10 @@ class AvatarEditorComponent {
|
|
|
6324
6348
|
drawH: this.image.height * scale,
|
|
6325
6349
|
};
|
|
6326
6350
|
}
|
|
6351
|
+
applyZoom(value) {
|
|
6352
|
+
const clamped = Math.min(this.maxZoom(), Math.max(this.minZoom(), value));
|
|
6353
|
+
this.zoom.set(Math.round(clamped * 100) / 100);
|
|
6354
|
+
}
|
|
6327
6355
|
clampOffset() {
|
|
6328
6356
|
if (!this.image) {
|
|
6329
6357
|
return;
|