@alyle/ui
Version:
Minimal Design, a set of components for Angular
1,670 lines • 84 kB
JavaScript
import { __decorate } from 'tslib';
import * as i0 from '@angular/core';
import { EventEmitter, ElementRef, Output, Input, ViewChild, Inject, Directive, booleanAttribute, inject, ChangeDetectionStrategy, Component, ViewEncapsulation, NgModule } from '@angular/core';
import { st2c, LY_COMMON_STYLES, StyleCollection, StyleRenderer, Style } from '@alyle/ui';
import { DOCUMENT, NgIf, NgStyle } from '@angular/common';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { color } from '@alyle/ui/color';
import { BehaviorSubject, Subject, Observable } from 'rxjs';
import { takeUntil, take } from 'rxjs/operators';
import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
import * as i1 from '@angular/cdk/scrolling';
/*
* Hermite resize - fast image resize/resample using Hermite filter.
* https://github.com/viliusle/Hermite-resize
*/
function resizeCanvas(canvas, width, height) {
const width_source = canvas.width;
const height_source = canvas.height;
width = Math.round(width);
height = Math.round(height);
const ratio_w = width_source / width;
const ratio_h = height_source / height;
const ratio_w_half = Math.ceil(ratio_w / 2);
const ratio_h_half = Math.ceil(ratio_h / 2);
const ctx = canvas.getContext('2d');
const img = ctx.getImageData(0, 0, width_source, height_source);
const img2 = ctx.createImageData(width, height);
const data = img.data;
const data2 = img2.data;
for (let j = 0; j < height; j++) {
for (let i = 0; i < width; i++) {
const x2 = (i + j * width) * 4;
let weight = 0;
let weights = 0;
let weights_alpha = 0;
let gx_r = 0;
let gx_g = 0;
let gx_b = 0;
let gx_a = 0;
const center_y = j * ratio_h;
const xx_start = Math.floor(i * ratio_w);
let xx_stop = Math.ceil((i + 1) * ratio_w);
const yy_start = Math.floor(j * ratio_h);
let yy_stop = Math.ceil((j + 1) * ratio_h);
xx_stop = Math.min(xx_stop, width_source);
yy_stop = Math.min(yy_stop, height_source);
for (let yy = yy_start; yy < yy_stop; yy++) {
const dy = Math.abs(center_y - yy) / ratio_h_half;
const center_x = i * ratio_w;
const w0 = dy * dy; // pre-calc part of w
for (let xx = xx_start; xx < xx_stop; xx++) {
const dx = Math.abs(center_x - xx) / ratio_w_half;
const w = Math.sqrt(w0 + dx * dx);
if (w >= 1) {
// pixel too far
continue;
}
// hermite filter
weight = 2 * w * w * w - 3 * w * w + 1;
const pos_x = 4 * (xx + yy * width_source);
// alpha
gx_a += weight * data[pos_x + 3];
weights_alpha += weight;
// colors
if (data[pos_x + 3] < 255) {
weight = weight * data[pos_x + 3] / 250;
}
gx_r += weight * data[pos_x];
gx_g += weight * data[pos_x + 1];
gx_b += weight * data[pos_x + 2];
weights += weight;
}
}
data2[x2] = gx_r / weights;
data2[x2 + 1] = gx_g / weights;
data2[x2 + 2] = gx_b / weights;
data2[x2 + 3] = gx_a / weights_alpha;
}
}
// clear and resize canvas
canvas.width = width;
canvas.height = height;
// draw
ctx.putImageData(img2, 0, 0);
return ctx;
}
const activeEventOptions = normalizePassiveListenerOptions({ passive: false });
const DATA_IMAGE_SVG_PREFIX = 'data:image/svg+xml;base64,';
/** Image Cropper Config */
class ImgCropperConfig {
constructor() {
/** Cropper area width */
this.width = 250;
/** Cropper area height */
this.height = 200;
this.minWidth = 40;
this.minHeight = 40;
/**
* Set anti-aliased (default: true)
* @deprecated this is not necessary as the cropper will automatically resize the image
* to the best quality
*/
this.antiAliased = true;
this.output = ImgResolution.Default;
/**
* Pinch zoom gestures
* Default: true
*/
this.hasTouchEvents = true;
}
}
/** Image output */
var ImgResolution;
(function (ImgResolution) {
/**
* The output image will be equal to the initial size of the cropper area.
*/
ImgResolution[ImgResolution["Default"] = 0] = "Default";
/** Just crop the image without resizing */
ImgResolution[ImgResolution["OriginalImage"] = 1] = "OriginalImage";
})(ImgResolution || (ImgResolution = {}));
var PointerChange;
(function (PointerChange) {
PointerChange[PointerChange["Down"] = 0] = "Down";
PointerChange[PointerChange["Up"] = 1] = "Up";
})(PointerChange || (PointerChange = {}));
/** Image output */
var ImgCropperError;
(function (ImgCropperError) {
/** The loaded image exceeds the size limit set. */
ImgCropperError[ImgCropperError["Size"] = 0] = "Size";
/** The file loaded is not image. */
ImgCropperError[ImgCropperError["Type"] = 1] = "Type";
/** When the image has not been loaded. */
ImgCropperError[ImgCropperError["Other"] = 2] = "Other";
})(ImgCropperError || (ImgCropperError = {}));
class _LyImageCropperBase {
static { this.и = 'LyImageCropper'; }
get config() {
return this._config;
}
set config(val) {
this._config = { ...{}, ...new ImgCropperConfig(), ...val };
this._initialConfig = { ...{}, ...this._config };
this._initialAreaWidth = this.config.width;
this._initialAreaHeight = this.config.height;
if (this._config.round
&& this.config.width !== this.config.height) {
throw new Error(`${_LyImageCropperBase.и}: Both width and height must be equal when using \`ImgCropperConfig.round = true\``);
}
const maxFileSize = this._config.maxFileSize;
if (maxFileSize) {
this.maxFileSize = maxFileSize;
}
}
/** Set scale */
get scale() {
return this._scale;
}
set scale(val) {
this.setScale(val);
}
/** Get min scale */
get minScale() {
return this._minScale;
}
constructor(_renderer, _elementRef, cd, _ngZone, _document, viewPortRuler) {
this._renderer = _renderer;
this._elementRef = _elementRef;
this.cd = cd;
this._ngZone = _ngZone;
this._imgRect = {};
this._rotation = 0;
this._urlsToRevoke = [];
this._events = new Map();
/** @private */
this._isPointerUp = new BehaviorSubject(true);
this.scaleChange = new EventEmitter();
/** Emits minimum supported image scale */
this.minScaleChange = new EventEmitter();
/** Emits maximum supported image scale */
this.maxScaleChange = new EventEmitter();
/** @deprecated Emits when the image is loaded, instead use `ready` */
this.loaded = new EventEmitter();
/** Emits when the image is loaded */
this.imageLoaded = new EventEmitter();
/** Emits when the cropper is ready to be interacted */
this.ready = new EventEmitter();
/** On crop new image */
this.cropped = new EventEmitter();
/** Emits when the cropper is cleaned */
this.cleaned = new EventEmitter();
/** Emit an error when the loaded image is not valid */
// tslint:disable-next-line: no-output-native
this.error = new EventEmitter();
/** Emits whenever the component is destroyed. */
this._destroy = new Subject();
this._pendingRotation = 0;
this.areaGridActiveCssClass = '';
this._pointerDown = (event) => {
this._isPointerUp.next(false);
if (isTouchEvent(event)) {
if (event.touches.length === 2) {
this._isMultiTouching = isTouchEvent(event);
// Calculate where the fingers have started on the X and Y axis
this._startCenter = getCenterFromTouchEvent(event);
this._startDistance = calDistanceFromTouchEvent(event);
}
}
// Don't do anything if the
// user is using anything other than the main mouse button.
if (this._isSliding || (!isTouchEvent(event) && event.button !== 0)) {
return;
}
this._ngZone.run(() => {
this._isSliding = true;
this.startTransform = {
x: this._imgRect.x,
y: this._imgRect.y,
xOrigin: this._imgRect.xc,
yOrigin: this._imgRect.yc
};
this._lastPointerEvent = event;
this._startPointerEvent = getGesturePointFromEvent(event);
event.preventDefault(); // Prevent page scroll
this._bindGlobalEvents(event);
});
};
/**
* Called when the user has moved their pointer after
* starting to drag.
*/
this._pointerMove = (event) => {
if (this._isSliding) {
event.preventDefault();
this._lastPointerEvent = event;
let x, y;
let scaleFix = this._scal3Fix;
const config = this.config;
const startP = this.startTransform;
if (!scaleFix || !startP) {
return;
}
const point = getGesturePointFromEvent(event);
let deltaX = 0;
let deltaY = 0;
const imgContainer = this._imgCanvas.nativeElement;
/** Main image width */
const imgWidth = imgContainer.width;
/** Main image height */
const imgHeight = imgContainer.height;
if (this._isMultiTouching) {
const newScale = calDistanceFromTouchEvent(event) / this._startDistance * this._scale;
scaleFix = this._scal3Fix = Math.max(newScale, this.minScale);
const center = getCenterFromTouchEvent(event);
deltaX = center.x - this._startCenter.x;
deltaY = center.y - this._startCenter.y;
}
else {
deltaX = point.x - this._startPointerEvent.x;
deltaY = point.y - this._startPointerEvent.y;
}
if (!scaleFix || !startP) {
return;
}
const isMinScaleX = imgWidth * scaleFix < config.width && config.extraZoomOut;
const isMinScaleY = imgHeight * scaleFix < config.height && config.extraZoomOut;
const limitLeft = (config.width / 2 / scaleFix) >= startP.xOrigin - (deltaX / scaleFix);
const limitRight = (config.width / 2 / scaleFix) + (imgWidth) - (startP.xOrigin - (deltaX / scaleFix)) <= config.width / scaleFix;
const limitTop = ((config.height / 2 / scaleFix) >= (startP.yOrigin - (deltaY / scaleFix)));
const limitBottom = (((config.height / 2 / scaleFix) + (imgHeight) - (startP.yOrigin - (deltaY / scaleFix))) <= (config.height / scaleFix));
// Limit for left
if ((limitLeft && !isMinScaleX) || (!limitLeft && isMinScaleX)) {
x = startP.x + (startP.xOrigin) - (config.width / 2 / scaleFix);
}
// Limit for right
if ((limitRight && !isMinScaleX) || (!limitRight && isMinScaleX)) {
x = startP.x + (startP.xOrigin) + (config.width / 2 / scaleFix) - imgWidth;
}
// Limit for top
if ((limitTop && !isMinScaleY) || (!limitTop && isMinScaleY)) {
y = startP.y + (startP.yOrigin) - (config.height / 2 / scaleFix);
}
// Limit for bottom
if ((limitBottom && !isMinScaleY) || (!limitBottom && isMinScaleY)) {
y = startP.y + (startP.yOrigin) + (config.height / 2 / scaleFix) - imgHeight;
}
// When press shiftKey, deprecated
// if (event.srcEvent && event.srcEvent.shiftKey) {
// if (Math.abs(event.deltaX) === Math.max(Math.abs(event.deltaX), Math.abs(event.deltaY))) {
// y = this.offset.top;
// } else {
// x = this.offset.left;
// }
// }
if (x === void 0) {
x = (deltaX / scaleFix) + (startP.x);
}
if (y === void 0) {
y = (deltaY / scaleFix) + (startP.y);
}
this._setStylesForContImg({
x, y,
});
}
};
// _mainImageRect(): DOMRect {
// return this._mainImage.nativeElement.getBoundingClientRect();
// }
/** Called when the user has lifted their pointer. */
this._pointerUp = (event) => {
this._isPointerUp.next(true);
if (this._isSliding) {
event.preventDefault();
this._removeGlobalEvents();
this._isSliding = false;
this._isMultiTouching = false;
this._startPointerEvent = null;
this._scale = this._scal3Fix;
this._cropIfAutoCrop();
}
};
/** Called when the window has lost focus. */
this._windowBlur = () => {
// If the window is blurred while dragging we need to stop dragging because the
// browser won't dispatch the `mouseup` and `touchend` events anymore.
if (this._lastPointerEvent) {
this._pointerUp(this._lastPointerEvent);
}
};
this._onWheel = (event) => {
if (!this.isLoaded) {
return;
}
event.preventDefault();
this._ngZone.run(() => {
if (Math.sign(event.deltaY) < 0) {
this.zoomIn();
}
else {
this.zoomOut();
}
});
};
this._document = _document;
viewPortRuler.change()
.pipe(takeUntil(this._destroy))
.subscribe(() => this._ngZone.run(() => this.updateCropperPosition()));
this._isPointerUp.subscribe((UP) => {
if (!this.areaGridActiveCssClass) {
return;
}
if (UP) {
this._renderer.removeClass(this._elementRef.nativeElement, this.areaGridActiveCssClass);
}
else {
this._renderer.addClass(this._elementRef.nativeElement, this.areaGridActiveCssClass);
}
});
}
ngOnInit() {
this._ngZone.runOutsideAngular(() => {
const element = this._imgContainer.nativeElement;
element.addEventListener('mousedown', this._pointerDown, activeEventOptions);
element.addEventListener('touchstart', this._pointerDown, activeEventOptions);
});
}
ngAfterViewInit() { }
ngOnDestroy() {
this._destroy.next();
this._destroy.complete();
const element = this._imgContainer.nativeElement;
this._lastPointerEvent = null;
this._removeGlobalEvents();
element.removeEventListener('mousedown', this._pointerDown, activeEventOptions);
element.removeEventListener('touchstart', this._pointerDown, activeEventOptions);
this._removeEventsToScaleWithScroll();
}
/** Load image with canvas */
// private _imgLoaded(imgElement: HTMLImageElement) {
// if (imgElement) {
// this._img = imgElement;
// const canvas = this._imgCanvas.nativeElement;
// canvas.width = imgElement.width;
// canvas.height = imgElement.height;
// const ctx = canvas.getContext('2d')!;
// ctx.clearRect(0, 0, imgElement.width, imgElement.height);
// ctx.drawImage(imgElement, 0, 0);
// /** set min scale */
// this._updateMinScale(canvas);
// this._updateMaxScale();
// }
// }
/** Load image with canvas */
_loadImageToCanvas(imgElement) {
if (imgElement) {
this._img = imgElement;
const canvas = this._imgCanvas.nativeElement;
canvas.width = imgElement.width;
canvas.height = imgElement.height;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, imgElement.width, imgElement.height);
ctx.drawImage(imgElement, 0, 0);
}
}
_setStylesForContImg(values) {
const newStyles = {};
if (values.x != null && values.y != null) {
const rootRect = this._cropperContainerRect();
const x = rootRect.width / 2 - (values.x);
const y = rootRect.height / 2 - (values.y);
this._imgRect.x = (values.x);
this._imgRect.y = (values.y);
this._imgRect.xc = (x);
this._imgRect.yc = (y);
}
newStyles.transform = `translate3d(${(this._imgRect.x)}px,${(this._imgRect.y)}px, 0)`;
newStyles.transform += ` scale(${this._scal3Fix})`;
newStyles.transform += ` rotate(0)`;
newStyles.transformOrigin = `${this._imgRect.xc}px ${this._imgRect.yc}px 0`;
for (const key in newStyles) {
if (newStyles.hasOwnProperty(key)) {
this._renderer.setStyle(this._imgContainer.nativeElement, key, newStyles[key]);
}
}
}
/**
* Update area and image position only if needed,
* this is used when window resize
*/
updateCropperPosition() {
if (this.isLoaded) {
this.updatePosition();
this._updateAreaIfNeeded();
}
}
/** Load Image from input event */
selectInputEvent(img) {
this.clean();
this._currentInputElement = img.target;
const _img = img.target;
if (_img.files && _img.files.length !== 1) {
return;
}
const fileSize = _img.files[0].size;
const fileName = _img.value.replace(/.*(\/|\\)/, '');
if (this.maxFileSize && fileSize > this.maxFileSize) {
const cropEvent = {
name: fileName,
type: _img.files[0].type,
size: fileSize,
error: ImgCropperError.Size
};
this.clean();
this.error.emit(cropEvent);
return;
}
new Observable(observer => {
const reader = new FileReader();
reader.onerror = err => observer.error(err);
reader.onabort = err => observer.error(err);
reader.onload = (ev) => setTimeout(() => {
observer.next(ev);
observer.complete();
});
reader.readAsDataURL(_img.files[0]);
})
.pipe(take(1), takeUntil(this._destroy))
.subscribe((loadEvent) => {
const originalDataURL = loadEvent.target.result;
this.loadImage({
name: fileName,
size: _img.files[0].size, // size in bytes
type: this.config.type || _img.files[0].type,
originalDataURL,
file: _img.files[0]
});
this.cd.markForCheck();
}, () => {
const cropEvent = {
name: fileName,
size: fileSize,
error: ImgCropperError.Other,
errorMsg: 'The File could not be loaded.',
type: _img.files[0].type
};
this.clean();
this.error.emit(cropEvent);
});
}
/** Set the size of the image, the values can be 0 between 1, where 1 is the original size */
setScale(size, noAutoCrop) {
// fix min scale
// const newSize = size! >= this.minScale! && size! <= 1 ? size : this.minScale;
const newSize = (size >= this.minScale) ? size : this.minScale;
// check
const changed = size != null && size !== this.scale && newSize !== this.scale;
this._scale = size;
if (!changed) {
return;
}
this._scal3Fix = newSize;
this._updateAbsoluteScale();
// this._updateMinScale();
// this._updateMaxScale(); // TODO: check this
if (this.isLoaded) {
if (changed) {
const originPosition = { ...this._imgRect };
this.startTransform = {
x: originPosition.x,
y: originPosition.y,
xOrigin: originPosition.xc,
yOrigin: originPosition.yc
};
this._setStylesForContImg({});
this._simulatePointerMove();
}
else {
return;
}
}
else if (this.minScale) {
this._setStylesForContImg({
...this._getCenterPoints()
});
}
else {
return;
}
this.scaleChange.emit(size);
if (!noAutoCrop) {
this._cropIfAutoCrop();
}
}
_getCenterPoints() {
const root = this._cropperContainer.nativeElement;
const img = this._imgCanvas.nativeElement;
const x = (root.offsetWidth - (img.width)) / 2;
const y = (root.offsetHeight - (img.height)) / 2;
return {
x,
y
};
}
/**
* Fit to screen
*/
fitToScreen() {
const container = this._cropperContainer.nativeElement;
const min = {
width: container.offsetWidth,
height: container.offsetHeight
};
const { width, height } = this._img;
const minScale = {
width: min.width / width,
height: min.height / height
};
const result = Math.max(minScale.width, minScale.height);
this.setScale(result);
}
fit() {
this.setScale(this.minScale);
}
/**
* Simulate pointerMove with clientX = 0 and clientY = 0,
* this is used by `setScale` and `rotate`
*/
_simulatePointerMove() {
this._isSliding = true;
this._startPointerEvent = {
x: 0,
y: 0
};
this._pointerMove({
clientX: 0,
clientY: 0,
type: 'n',
preventDefault: () => { }
});
this._isSliding = false;
this._startPointerEvent = null;
}
_markForCheck() {
this.cd.markForCheck();
}
updatePosition(xOrigin, yOrigin) {
const hostRect = this._cropperContainerRect();
const areaRect = this._areaCropperRect();
const areaWidth = Math.min(areaRect.width, hostRect.width);
const areaHeight = Math.min(areaRect.height, hostRect.height);
let x, y;
if (xOrigin == null && yOrigin == null) {
xOrigin = this._imgRect.xc;
yOrigin = this._imgRect.yc;
}
x = Math.max((areaRect.left - hostRect.left), 0);
y = Math.max(0, (areaRect.top - hostRect.top));
x -= (xOrigin - (areaWidth / 2));
y -= (yOrigin - (areaHeight / 2));
this._setStylesForContImg({
x, y
});
}
_slideEnd() {
this._cropIfAutoCrop();
}
_cropIfAutoCrop() {
if (this.config.autoCrop) {
this.crop();
}
}
/** + */
zoomIn() {
const scalePercent = this._calcScalePercent(this._scal3Fix, this._minScale, this._maxScale);
const newScale = scalePercent < 20 ? ((this._scal3Fix * this._imgCanvas.nativeElement.width + 50)
/ (this._imgCanvas.nativeElement.width)) : (this._scal3Fix + .05);
if (newScale > this.minScale && newScale <= this._maxScale) {
this.setScale(newScale);
}
else {
this.setScale(this._maxScale);
}
}
_calcScalePercent(num, min, max) {
return ((num - min) / (max - min)) * 100;
}
/** Clean the img cropper */
clean() {
// fix choosing the same image does not load
if (this._currentInputElement) {
this._currentInputElement.value = '';
this._currentInputElement = null;
}
if (this.isLoaded) {
this._imgRect = {};
this.startTransform = undefined;
this.scale = undefined;
this._scal3Fix = undefined;
this._rotation = 0;
this._minScale = undefined;
this._isLoadedImg = false;
this.isLoaded = false;
this.isCropped = false;
this._currentLoadConfig = undefined;
this.config = this._initialConfig;
const canvas = this._imgCanvas.nativeElement;
canvas.width = 0;
canvas.height = 0;
this.cleaned.emit(null);
this.cd.markForCheck();
}
}
/** - */
zoomOut() {
const scalePercent = this._calcScalePercent(this._scal3Fix, this._minScale, this._maxScale);
const newScale = scalePercent < 20 ? ((this._scal3Fix * this._imgCanvas.nativeElement.width - 50)
/ (this._imgCanvas.nativeElement.width)) : (this._scal3Fix - .05);
if (newScale > this.minScale && newScale <= this._maxScale) {
this.setScale(newScale);
}
else {
this.fit();
}
}
center() {
const newStyles = {
...this._getCenterPoints()
};
this._setStylesForContImg(newStyles);
this._cropIfAutoCrop();
}
/**
* load an image from a given configuration,
* or from the result of a cropped image
*/
loadImage(config, fn) {
this.clean();
const _config = this._currentLoadConfig = typeof config === 'string'
? { originalDataURL: config }
: { ...config };
let src = _config.originalDataURL;
if (!src) {
const err = {
name: _config.name,
error: ImgCropperError.Other,
type: _config.type,
size: _config.size
};
this.error.emit(err);
return;
}
this._initialAreaWidth = this._initialConfig.width;
this._initialAreaHeight = this._initialConfig.height;
if (_config.areaWidth && _config.areaHeight) {
this.config.width = _config.areaWidth;
this.config.height = _config.areaHeight;
}
if (_config.originalDataURL && isSvgImage(_config.originalDataURL)) {
src = normalizeSVG(_config.originalDataURL);
}
const cropEvent = { ..._config };
new Promise((resolve, reject) => {
const img = createHtmlImg(src);
this._ngZone.runOutsideAngular(() => {
img.onerror = err => reject(err);
img.onabort = err => reject(err);
img.onload = () => resolve(img);
});
})
.then((img) => {
this._loadImageToCanvas(img);
this._updateMinScale(img);
this._updateMaxScale();
this._isLoadedImg = true;
this.imageLoaded.emit(cropEvent);
this.cd.markForCheck();
Promise.resolve(null).then(() => {
setTimeout(() => {
this._ngZone.run(() => { this._positionImg(cropEvent, fn); });
});
});
}, () => {
const err = {
name: _config.name,
error: ImgCropperError.Type,
type: _config.type,
size: _config.size
};
this.error.emit(err);
});
}
_updateAreaIfNeeded() {
if (!this._config.responsiveArea)
return;
const rootRect = this._cropperContainerRect();
const areaRect = this._areaCropperRect();
const { minWidth = 1, minHeight = 1, round } = this.config;
// Calculate maximum dimensions allowed for the area
const maxWidth = Math.max(rootRect.width, minWidth);
const maxHeight = Math.max(rootRect.height, minHeight);
let newWidth = areaRect.width;
let newHeight = areaRect.height;
if (round) {
newWidth = newHeight = Math.min(maxWidth, maxHeight); // Área cuadrada si round es true
}
else {
const shouldResizeWidth = (areaRect.width > maxWidth
|| areaRect.width < this._initialAreaWidth
|| this._areaWidthResized);
const shouldResizeHeight = (areaRect.height > maxHeight
|| areaRect.height < this._initialAreaHeight
|| this._areaHeightResized);
let posibleWidth_w = 0;
let posibleWidth_h = 0;
let posibleHeight_h = 0;
let posibleHeight_w = 0;
if (shouldResizeWidth) {
newWidth = posibleWidth_w = Math.min(maxWidth, this._areaWidthResized || this._initialAreaWidth);
newHeight = posibleWidth_h = (newWidth * areaRect.height) / areaRect.width;
}
if (shouldResizeHeight) {
newHeight = posibleHeight_h = Math.min(maxHeight, this._areaHeightResized || this._initialAreaHeight);
newWidth = posibleHeight_w = (newHeight * areaRect.width) / areaRect.height;
}
if (shouldResizeHeight && shouldResizeWidth) {
if (Math.min(posibleWidth_w, posibleWidth_h) < Math.min(posibleHeight_h, posibleHeight_w)) {
newWidth = posibleWidth_w;
newHeight = posibleWidth_h;
}
else {
newHeight = posibleHeight_h;
newWidth = posibleHeight_w;
}
}
}
// Apply changes if dimensions have changed
if (newWidth !== this.config.width || newHeight !== this.config.height) {
const newScale = (this._scal3Fix * newWidth) / areaRect.width;
this.config.width = newWidth;
this.config.height = newHeight;
this._updateMinScale();
this._updateMaxScale();
this.setScale(newScale, true);
this._markForCheck();
}
}
/**
* @private
*/
_updateAbsoluteScale() {
const scale = this._scal3Fix / (this.config.width / this._initialAreaWidth);
this._absoluteScale = scale;
}
/**
* Load Image from URL
* @deprecated Use `loadImage` instead of `setImageUrl`
* @param src URL
* @param fn function that will be called before emit the event loaded
*/
setImageUrl(src, fn) {
this.loadImage(src, fn);
}
_positionImg(cropEvent, fn) {
const loadConfig = this._currentLoadConfig;
this._updateMinScale(this._imgCanvas.nativeElement);
this._updateMaxScale();
this.isLoaded = false;
if (fn) {
fn();
}
else {
if (loadConfig.scale) {
this.setScale(loadConfig.scale, true);
}
else {
this.setScale(this.minScale, true);
}
// this.rotate(loadConfig.rotation || 0);
this._updateAreaIfNeeded();
this._markForCheck();
this._ngZone.runOutsideAngular(() => {
Promise.resolve(null).then(() => {
if (loadConfig.xOrigin != null && loadConfig.yOrigin != null) {
this.updatePosition(loadConfig.xOrigin, loadConfig.yOrigin);
}
this._updateAreaIfNeeded();
this.isLoaded = true;
this._cropIfAutoCrop();
this._ngZone.run(() => {
this._markForCheck();
this.ready.emit(cropEvent);
if (this.config.hasTouchEvents) {
this._addEventsToScaleWithScroll();
}
else {
this._removeEventsToScaleWithScroll();
}
// tslint:disable-next-line: deprecation
this.loaded.emit(cropEvent);
});
});
});
}
}
rotate2(degrees) {
this._rotation -= degrees;
const newStyles = {};
newStyles.transform = `translate(${(this._imgRect.x)}px,${(this._imgRect.y)}px)`;
newStyles.transform += ` scale(${this._scal3Fix})`;
newStyles.transform += ` rotate(${this._rotation}deg)`;
newStyles.transformOrigin = `${this._imgRect.xc}px ${this._imgRect.yc}px 0`;
for (const key in newStyles) {
if (newStyles.hasOwnProperty(key)) {
this._renderer.setStyle(this._imgContainer.nativeElement, key, newStyles[key]);
}
}
}
rotate(degrees) {
_normalizeDegrees(degrees);
const newRotation = this._rotation + degrees;
clearTimeout(this._rotateTimeOut);
this._pendingRotation += degrees;
this._rotation = newRotation;
this._rotateWithAnimation();
this._rotateTimeOut = setTimeout(() => {
this._setStylesForContImg({});
this._rotateCanvas();
}, 140);
}
_rotateWithAnimation() {
const newStyles = {};
// easeOutQuad
newStyles.transition = `cubic-bezier(0.250, 0.460, 0.450, 0.940) 140ms`;
newStyles.transform = `translate(${(this._imgRect.x)}px,${(this._imgRect.y)}px)`;
newStyles.transform += ` scale(${this._scal3Fix})`;
newStyles.transform += ` rotate(${this._pendingRotation}deg)`;
newStyles.transformOrigin = `${this._imgRect.xc}px ${this._imgRect.yc}px 0`;
for (const key in newStyles) {
if (newStyles.hasOwnProperty(key)) {
this._renderer.setStyle(this._imgContainer.nativeElement, key, newStyles[key]);
}
}
}
_rotateCanvas() {
let validDegrees = this._pendingRotation;
const degreesRad = validDegrees * Math.PI / 180;
const canvas = this._imgCanvas.nativeElement;
canvas.removeAttribute('style');
this._pendingRotation = 0;
this._renderer.removeStyle(this._imgContainer.nativeElement, 'transition');
const canvasClon = createCanvasImg(canvas);
const ctx = canvas.getContext('2d');
// clear
ctx.clearRect(0, 0, canvasClon.width, canvasClon.height);
// rotate canvas image
const transform = `rotate(${validDegrees}deg) scale(${1 / this._scal3Fix})`;
const transformOrigin = `${this._imgRect.xc}px ${this._imgRect.yc}px 0`;
canvas.style.transform = transform;
canvas.style.opacity = '0';
// tslint:disable-next-line: deprecation
canvas.style.webkitTransform = transform;
canvas.style.transformOrigin = transformOrigin;
// tslint:disable-next-line: deprecation
canvas.style.webkitTransformOrigin = transformOrigin;
const { left, top } = canvas.getBoundingClientRect();
// save rect
const canvasRect = canvas.getBoundingClientRect();
// remove rotate styles
canvas.removeAttribute('style');
// set w & h
const w = canvasRect.width;
const h = canvasRect.height;
ctx.canvas.width = w;
ctx.canvas.height = h;
// clear
ctx.clearRect(0, 0, w, h);
// translate and rotate
ctx.translate(w / 2, h / 2);
ctx.rotate(degreesRad);
ctx.drawImage(canvasClon, -canvasClon.width / 2, -canvasClon.height / 2);
// Update min scale
this._updateMinScale(canvas);
this._updateMaxScale();
// set the minimum scale, only if necessary
if (this.scale < this.minScale) {
this.setScale(0, true);
} // ↑ no AutoCrop
const rootRect = this._cropperContainerRect();
this._setStylesForContImg({
x: (left - rootRect.left),
y: (top - rootRect.top)
});
// keep image inside the frame
const originPosition = { ...this._imgRect };
this.startTransform = {
x: originPosition.x,
y: originPosition.y,
xOrigin: originPosition.xc,
yOrigin: originPosition.yc
};
this._setStylesForContImg({});
this._simulatePointerMove();
this._cropIfAutoCrop();
}
_updateMinScale(canvas) {
if (!canvas) {
canvas = this._imgCanvas.nativeElement;
}
const config = this.config;
const minScale = (config.extraZoomOut ? Math.min : Math.max)(config.width / canvas.width, config.height / canvas.height);
this._minScale = minScale;
this.minScaleChange.emit(minScale);
}
/**
* @private
*/
_updateMaxScale() {
const maxScale = (this.config.width / this._initialAreaWidth) * 3;
this._maxScale = maxScale;
this.maxScaleChange.emit(maxScale);
}
/**
* Resize & crop image
*/
crop(config) {
const newConfig = config
? { ...{}, ...(this.config || new ImgCropperConfig()), ...config } : this.config;
// this._loadImageToCanvas(this._mainImage.nativeElement);
const cropEvent = this._imgCrop(newConfig);
this.cd.markForCheck();
return cropEvent;
}
/**
* @docs-private
*/
_imgCrop(myConfig) {
const canvasElement = document.createElement('canvas');
const areaRect = this._areaCropperRect();
const canvasRect = this._canvasRect();
const scaleFix = this._scal3Fix;
const left = (areaRect.left - canvasRect.left) / scaleFix;
const top = (areaRect.top - canvasRect.top) / scaleFix;
const { output } = myConfig;
const currentImageLoadConfig = this._currentLoadConfig;
const area = {
width: myConfig.width,
height: myConfig.height
};
canvasElement.width = area.width / scaleFix;
canvasElement.height = area.height / scaleFix;
const ctx = canvasElement.getContext('2d');
if (myConfig.fill) {
ctx.fillStyle = myConfig.fill;
ctx.fillRect(0, 0, canvasElement.width, canvasElement.height);
}
// crop
ctx.drawImage(this._imgCanvas.nativeElement, -(left), -(top));
const result = canvasElement;
// TODO: check if the image to be sized is smaller than the crop area
if (myConfig.output === ImgResolution.Default) {
const areaWidth = this._areaWidthResized ?? this._initialConfig.width;
const areaHeight = this._areaHeightResized ?? this._initialConfig.height;
resizeCanvas(result, areaWidth, areaHeight);
}
else if (typeof output === 'object') {
if (output.width && output.height) {
resizeCanvas(result, output.width, output.height);
}
else if (output.width) {
const newHeight = area.height * output.width / area.width;
resizeCanvas(result, output.width, newHeight);
}
else if (output.height) {
const newWidth = area.width * output.height / area.height;
resizeCanvas(result, newWidth, output.height);
}
}
const type = currentImageLoadConfig.originalDataURL?.startsWith('http')
? currentImageLoadConfig.type || myConfig.type
: myConfig.type || currentImageLoadConfig.type;
const dataURL = result.toDataURL(type);
const cropEvent = {
dataURL,
type,
name: currentImageLoadConfig.name,
areaWidth: this._initialAreaWidth,
areaHeight: this._initialAreaHeight,
width: result.width,
height: result.height,
originalDataURL: currentImageLoadConfig.originalDataURL,
scale: this._absoluteScale,
rotation: this._rotation,
left: (areaRect.left - canvasRect.left) / this._scal3Fix,
top: (areaRect.top - canvasRect.top) / this._scal3Fix,
size: currentImageLoadConfig.size,
xOrigin: this._imgRect.xc,
yOrigin: this._imgRect.yc,
position: {
x: this._imgRect.xc,
y: this._imgRect.yc
}
};
this.isCropped = true;
this.cropped.emit(cropEvent);
return cropEvent;
}
// _rootRect(): DOMRect {
// return this._elementRef.nativeElement.getBoundingClientRect() as DOMRect;
// }
_cropperContainerRect() {
return this._cropperContainer.nativeElement.getBoundingClientRect();
}
_areaCropperRect() {
return this._areaRef.nativeElement.getBoundingClientRect();
}
_canvasRect() {
return this._imgCanvas.nativeElement.getBoundingClientRect();
}
_bindGlobalEvents(triggerEvent) {
const element = this._document;
const isTouch = isTouchEvent(triggerEvent);
const moveEventName = isTouch ? 'touchmove' : 'mousemove';
const endEventName = isTouch ? 'touchend' : 'mouseup';
element.addEventListener(moveEventName, this._pointerMove, activeEventOptions);
element.addEventListener(endEventName, this._pointerUp, activeEventOptions);
if (isTouch) {
element.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.addEventListener('blur', this._windowBlur);
}
}
/** Removes any global event listeners that we may have added. */
_removeGlobalEvents() {
const element = this._document;
element.removeEventListener('mousemove', this._pointerMove, activeEventOptions);
element.removeEventListener('mouseup', this._pointerUp, activeEventOptions);
element.removeEventListener('touchmove', this._pointerMove, activeEventOptions);
element.removeEventListener('touchend', this._pointerUp, activeEventOptions);
element.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.removeEventListener('blur', this._windowBlur);
}
}
_addEventsToScaleWithScroll() {
if (!this._events.has('wheel')) {
this._events.set('wheel', this._onWheel);
this._ngZone.runOutsideAngular(() => {
const element = this._elementRef.nativeElement;
element.addEventListener('wheel', this._onWheel, activeEventOptions);
});
}
}
_removeEventsToScaleWithScroll() {
if (this._events.has('wheel')) {
this._events.delete('wheel');
const host = this._elementRef.nativeElement;
host.removeEventListener('wheel', this._onWheel, activeEventOptions);
}
}
/** Use defaultView of injected document if available or fallback to global window reference */
_getWindow() {
return this._document.defaultView || window;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: _LyImageCropperBase, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: DOCUMENT }, { token: i1.ViewportRuler }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.10", type: _LyImageCropperBase, isStandalone: true, inputs: { config: "config", scale: "scale", maxFileSize: "maxFileSize" }, outputs: { scaleChange: "scaleChange", minScaleChange: "minScale", maxScaleChange: "maxScale", loaded: "loaded", imageLoaded: "imageLoaded", ready: "ready", cropped: "cropped", cleaned: "cleaned", error: "error" }, viewQueries: [{ propertyName: "_cropperContainer", first: true, predicate: ["_cropperContainer"], descendants: true, static: true }, { propertyName: "_imgContainer", first: true, predicate: ["_imgContainer"], descendants: true, static: true }, { propertyName: "_areaRef", first: true, predicate: ["_area"], descendants: true, read: ElementRef }, { propertyName: "_imgCanvas", first: true, predicate: ["_imgCanvas"], descendants: true, static: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: _LyImageCropperBase, decorators: [{
type: Directive
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i1.ViewportRuler }], propDecorators: { _cropperContainer: [{
type: ViewChild,
args: ['_cropperContainer', { static: true }]
}], _imgContainer: [{
type: ViewChild,
args: ['_imgContainer', { static: true }]
}], _areaRef: [{
type: ViewChild,
args: ['_area', {
read: ElementRef
}]
}], _imgCanvas: [{
type: ViewChild,
args: ['_imgCanvas', { static: true }]
}], config: [{
type: Input
}], scale: [{
type: Input
}], maxFileSize: [{
type: Input
}], scaleChange: [{
type: Output
}], minScaleChange: [{
type: Output,
args: ['minScale']
}], maxScaleChange: [{
type: Output,
args: ['maxScale']
}], loaded: [{
type: Output
}], imageLoaded: [{
type: Output
}], ready: [{
type: Output
}], cropped: [{
type: Output
}], cleaned: [{
type: Output
}], error: [{
type: Output
}] } });
/**
* @dynamic
*/
class _LyCropperAreaBase {
set resizableArea(val) {
if (val !== this._resizableArea) {
this._resizableArea = val;
Promise.resolve(null).then(() => {
if (val) {
this._removeResizableArea();
this._addResizableArea();
}
else {
this._removeResizableArea();
}
});
}
}
get resizableArea() {
return this._resizableArea;
}
// @Style<boolean, LyCropperArea>(
// (_value, _media) => ({ after }, selectors: SelectorsFn) => {
// const $$ = selectors(STYLES);
// return (_className: string) => `${_className}{ ;`;
// },
// coerceBooleanProperty
// ) round: BooleanInput;
constructor(_elementRef, _ngZone, _cropper, _document) {
this._elementRef = _elementRef;
this._ngZone = _ngZone;
this._cropper = _cropper;
this._pointerDown = (event) => {
this._cropper._isPointerUp.next(false);
// Don't do anything if the
// user is using anything other than the main mouse button.
if (this._isSliding || (!isTouchEvent(event) && event.button !== 0)) {
return;
}
event.preventDefault();
this._ngZone.run(() => {
this._isSliding = true;
this._lastPointerEvent = event;
this._startPointerEvent = getGesturePointFromEvent(event);
this._startAreaRect = this._cropper._areaCropperRect();
this._startImgRect = this._cropper._canvasRect();
this._bindGlobalEvents(event);
});
};
this._pointerMove = (event) => {
if (this._isSliding) {
event.preventDefault();
this._lastPointerEvent = event;
const element = this._elementRef.nativeElement;
const { width, height, minWidth, minHeight } = this._cropper.config;
const point = getGesturePointFromEvent(event);
const deltaX = point.x - this._startPointerEvent.x;
const deltaY = point.y - this._startPointerEvent.y;
const startAreaRect = this._startAreaRect;
const startImgRect = this._startImgRect;
const round = this.round;
const keepAspectRatio = this._cropper.config.keepAspectRatio || event.shiftKey;
let newWidth = 0;
let newHeight = 0;
const rootRect = this._cropper._cropperContainerRect();
if (round) {
// The distance from the center of the cropper area to the pointer
const originX = ((width / 2 / Math.sqrt(2)) + deltaX);
const originY = ((height / 2 / Math.sqrt(2)) + deltaY);
// Leg
const side = Math.sqrt(originX ** 2 + originY ** 2);
newWidth = newHeight = side * 2;
}
else if (keepAspectRatio) {
newWidth = width + deltaX * 2;
newHeight = height + deltaY * 2;
if (width !== height) {
if (width > height) {
newHeight = height / (width / newWidth);
}
else if (height > width) {
newWidth = width / (height / newHeight);
}
}
else {
newWidth = newHeight = Math.max(newWidth, newHeight);
}
}
else {
newWidth = width + deltaX * 2;
newHeight = height + deltaY * 2;
}
// To min width
if (newWidth < minWidth) {
newWidth = minWidth;
}
// To min height
if (newHeight < minHeight) {
newHeight = minHeight;
}
// Do not overflow the cropper area
const centerX = startAreaRect.x + startAreaRect.width / 2;
const centerY = startAreaRect.y + startAreaRect.height / 2;
const topOverflow = startImgRect.y > centerY - (newHeight / 2);
const bottomOverflow = centerY + (newHeight / 2) > startImgRect.bottom;
const minHeightOnOverflow = Math.min((centerY - startImgRect.y) * 2, (startImgRect.bottom - centerY) * 2);
const leftOverflow = startImgRect.x > centerX - (newWidth / 2);
const rightOverflow = centerX + (newWidth / 2) > startImgRect.right;
const minWidthOnOverflow = Math.min((centerX - startImgRect.x) * 2, (startImgRect.right - centerX) * 2);
const minOnOverflow = Math.min(minWidthOnOverflow, minHeightOnOverflow);
if (round) {
if (topOverflow || bottomOverflow || leftOverflow || rightOverflow) {
newHeight = newWidth = minOnOverflow;
}
}
else if (keepAspectRatio) {
const newNewWidth = [];
const newNewHeight = [];
if ((topOverflow || bottomOverflow)) {
newHeight = minHeightOnOverflow;
newNewHeight.push(newHeight);
newWidth = width / (height / minHeightOnOverflow);
newNewWidth.push(newWidth);
}
if ((leftOverflow || rightOverflow)) {
newWidth = minWidthOnOverflow;
newNewWidth.push(newWidth);
newHeight = height / (width / minWidthOnOverflow);
newNewHeight.push(newHeight);
}
if (newNewWidth.length === 2) {
newWidth = Math.min(...newNewWidth);
}
if (newNewHeight.length === 2) {
newHeight = Math.min(...newNewHeight);
}
}
else {
if (topOverflow || bottomOverflow) {
newHeight = minHeightOnOverflow;
}
if (leftOverflow || rightOverflow) {
newWidth = minWidthOnOverflow;
}
}
// Do not overflow the container
if (round) {
const min = Math.min(rootRect.width, rootRect.height);
if (newWidth > min) {
newWidth = newHeight = min;
}
else if (newHeight > min) {
newWidth = newHeight = min;
}
}
else if (keepAspectRatio) {
if (newWidth > rootRect.width) {
newWidth = rootRect.width;
newHeight = height / (width / rootRect.width);
}
if (newHeight > rootRect.height) {
newWidth = width / (height / rootRect.height);
newHeight = rootRect.height;
}
}
else {
if (newWidth > rootRect.width) {
newWidth = rootRect.width;
}
if (newHeight > rootRect.height) {
newHeight = rootRect.height;
}
}
// round values
const newWidthRounded = Math.round(newWidth);
const newHeightRounded = Math.round(newHeight);
element.style.width = `${newWidthRounded}px`;
element.style.height = `${newHeightRounded}px`;
this._currentWidth = newWidthRounded;
this._currentHeight = newHeightRounded;
}
};
/** Called when the user has lifted their pointer. */
this._pointerUp = (event) => {
this._cropper._isPointerUp.next(true);
const hasChange = this._currentWidth !== this._cropper._initialAreaWidth
|| this._currentHeight !== this._cropper._initialAreaHeight;
if (this._isSliding && (this._currentWidth != null)) {
event.preventDefault();
this._removeGlobalEvents();
this._cropper._areaWidthResized = hasChange ? this._currentWidth : null;
this._cropper._areaHeightResized = hasChange ? this._currentHeight : null;
// this._cropper._initialAreaWidth =
this._cropper.config.width = this._currentWidth;
// this._cropper._initialAreaHeight =
this._cropper.config.height = this._currentHeight;
this._cropper._updateMinScale();
this._cropper._updateMaxScale();
this._cropper._updateAbsoluteScale();
this._isSliding = false;
this._startPointerEvent = null;
this._currentWidth = null;
this._currentHeight = null;
this._cropper._markForCheck();
}
};
/** Called when the window has lost focus. */
this._windowBlur = () => {
// If the window is blurred while dragging we need to stop dragging because the
// browser won't dispatch the `mouseup` and `touchend` events anymore.
if (this._lastPointerEvent) {
this._pointerUp(this._lastPointerEvent);
}
};
this._document = _document;
}
ngOnDestroy() {
this._removeResizableArea();
}
_addResizableArea() {
this._ngZone.runOutsideAngular(() => {
const element = this._resizer.nativeElement;
element.addEventListener('mousedown', this._pointerDown, activeEventOptions);
element.addEventListener('touchstart', this._pointerDown, activeEventOptions);
});
}
_removeResizableArea() {
const element = this._resizer?.nativeElement;
if (element) {
this._lastPointerEvent = null;
this._removeGlobalEvents();
element.removeEventListener('mousedown', this._pointerDown, activeEventOptions);
element.removeEventListener('touchstart', this._pointerDown, activeEventOptions);
}
}
_bindGlobalEvents(triggerEvent) {
const element = this._document;
const isTouch = isTouchEvent(triggerEvent);
const moveEventName = isTouch ? 'touchmove' : 'mousemove';
const endEventName = isTouch ? 'touchend' : 'mouseup';
element.addEventListener(moveEventName, this._pointerMove, activeEventOptions);
element.addEventListener(endEventName, this._pointerUp, activeEventOptions);
if (isTouch) {
element.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.addEventListener('blur', this._windowBlur);
}
}
/** Removes any global event listeners that we may have added. */
_removeGlobalEvents() {
const element = this._document;
element.removeEventListener('mousemove', this._pointerMove, activeEventOptions);
element.removeEventListener('mouseup', this._pointerUp, activeEventOptions);
element.removeEventListener('touchmove', this._pointerMove, activeEventOptions);
element.removeEventListener('touchend', this._pointerUp, activeEventOptions);
element.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
const window = this._getWindow();
if (typeof window !== 'undefined' && window) {
window.removeEventListener('blur', this._windowBlur);
}
}
/** Use defaultView of injected document if available or fallback to global window reference */
_getWindow() {
return this._document.defaultView || window;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: _LyCropperAreaBase, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: _LyImageCropperBase }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.10", type: _LyCropperAreaBase, isStandalone: true, inputs: { resizableArea: "resizableArea", keepAspectRatio: "keepAspectRatio", round: "round" }, viewQueries: [{ propertyName: "_resizer", first: true, predicate: ["resizer"], descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: _LyCropperAreaBase, decorators: [{
type: Directive
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: _LyImageCropperBase }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }], propDecorators: { _resizer: [{
type: ViewChild,
args: ['resizer']
}], resizableArea: [{
type: Input
}], keepAspectRatio: [{
type: Input
}], round: [{
type: Input
}, {
type: Input,
args: [{ transform: booleanAttribute }]
}] } });
/**
* Normalize degrees for cropper rotation
* @docs-private
*/
function _normalizeDegrees(n) {
const de = n % 360;
if (de % 90) {
throw new Error(`LyCropper: Invalid \`${n}\` degree, only accepted values: 0, 90, 180, 270 & 360.`);
}
return de;
}
/**
* @docs-private
*/
function createCanvasImg(img) {
// create a new canvas
const newCanvas = document.createElement('canvas');
const context = newCanvas.getContext('2d');
// set dimensions
newCanvas.width = img.width;
newCanvas.height = img.height;
// apply the old canvas to the new one
context.drawImage(img, 0, 0);
// return the new canvas
return newCanvas;
}
function normalizeSVG(dataURL) {
if (window.atob && isSvgImage(dataURL)) {
const len = dataURL.length / 5;
const text = window.atob(dataURL.replace(DATA_IMAGE_SVG_PREFIX, ''));
const span = document.createElement('span');
span.innerHTML = text;
const svg = span.querySelector('svg');
span.setAttribute('style', 'display:none');
document.body.appendChild(span);
const width = parseFloat(getComputedStyle(svg).width) || 1;
const height = parseFloat(getComputedStyle(svg).height) || 1;
const max = Math.max(width, height);
svg.setAttribute('width', `${len / (width / max)}px`);
svg.setAttribute('height', `${len / (height / max)}px`);
// const result = DATA_IMAGE_SVG_PREFIX + window.btoa(span.innerHTML);
document.body.removeChild(span);
const blob = new Blob([span.innerHTML], { type: 'image/svg+xml' });
return URL.createObjectURL(blob);
}
return dataURL;
}
function isSvgImage(dataUrl) {
return dataUrl.startsWith(DATA_IMAGE_SVG_PREFIX);
}
function createHtmlImg(src) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = src;
return img;
}
function getGesturePointFromEvent(event) {
// `touches` will be empty for start/end events so we have to fall back to `changedTouches`.
const point = isTouchEvent(event)
? (event.touches[0] || event.changedTouches[0])
: event;
return {
x: point.clientX,
y: point.clientY
};
}
/** Returns whether an event is a touch event. */
function isTouchEvent(event) {
return event.type[0] === 't';
}
// Calculate distance between two fingers
function calDistanceFromTouchEvent(event) {
return Math.hypot(event.touches[0].clientX - event.touches[1].clientX, event.touches[0].clientY - event.touches[1].clientY);
}
;
function getCenterFromTouchEvent(event) {
const x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
const y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
return { x, y };
}
const STYLE_PRIORITY = -2;
const pos = (100 * Math.sqrt(2) - 100) / 2 / Math.sqrt(2);
const STYLES = (theme, selectors) => {
const $$ = selectors(STYLES);
const { after } = theme;
const transition = `${theme.animations.curves.acceleration} 100ms`;
return {
$name: LyImageCropper.и,
$priority: STYLE_PRIORITY,
root: () => (_className) => `${_className}{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;overflow:hidden;position:relative;justify-content:center;align-items:center;}${st2c(((theme.cropper
&& theme.cropper.root
&& (theme.cropper.root instanceof StyleCollection
? theme.cropper.root.setTransformer(fn => fn($$))
: theme.cropper.root($$)))), `${_className}`)}`,
cropperContainer: (_className) => `${_className}{position:relative;margin:auto;width:80%;height:80%;}`,
imgContainer: (_className) => `${_className}{cursor:move;position:absolute;top:0;left:0;display:flex;touch-action:none;}${_className} > canvas{display:block;}`,
overlay: (_className) => `${st2c((LY_COMMON_STYLES.fill), `${_className}`)}`,
area: (_className) => `${_className}{pointer-events:none;box-shadow:0 0 0 20000px rgba(0,0,0,0.4);}${st2c((LY_COMMON_STYLES.fill), `${_className}`)}${_className}{margin:auto;}${st2c((LY_COMMON_STYLES.fill), `${_className}:before,${_className}:after`)}${_className}:before,${_className}:after{content:'';}${_className}:before{width:0;height:0;margin:auto;border-radius:50%;background:#fff;border:solid 2px rgb(255,255,255);}${_className}:after{border:solid 2px rgb(255,255,255);border-radius:inherit;}`,
grid: (_className) => `${_className}{position:absolute;width:100%;height:100%;opacity:0;transition:${transition};}${_className}::before,${_className}::after{content:' ';box-sizing:border-box;position:absolute;border:1px solid ${color(0xffffff, 0.4)};}${_className}::before{top:0;bottom:0;left:33.33%;right:33.33%;border-top:0;border-bottom:0;}${_className}::after{top:33.33%;bottom:33.33%;left:0;right:0;border-left:0;border-right:0;}`,
showGrid: () => (_className) => `${_className} ${$$.grid}{opacity:1;}`,
isPointerUp: null,
resizer: (_className) => `${_className}{width:10px;height:10px;background:#fff;border-radius:3px;position:absolute;touch-action:none;bottom:0;${after}:0;pointer-events:all;cursor:${after === 'right'
? 'nwse-resize'
: 'nesw-resize'};}${st2c((LY_COMMON_STYLES.fill), `${_className}:before`)}${_className}:before{content:'';width:30px;height:30px;transform:translate(-35%,-35%);}`,
defaultContent: (_className) => `${_className}{display:flex;align-items:center;justify-content:center;}${st2c((LY_COMMON_STYLES.fill), `${_className},${_className} > input`)}${_className} *:not(input){pointer-events:none;}${_className} > input{background:transparent;opacity:0;width:100%;height:100%;}`
};
};
/**
* @dynamic
*/
class LyCropperArea extends _LyCropperAreaBase {
constructor() {
super(...arguments);
this.sRenderer = inject(StyleRenderer);
this.classes = this.sRenderer.renderSheet(STYLES, 'area');
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyCropperArea, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "19.2.10", type: LyCropperArea, isStandalone: true, selector: "ly-cropper-area", inputs: { round: ["round", "round", booleanAttribute] }, providers: [
StyleRenderer
], exportAs: ["lyCropperArea"], usesInheritance: true, ngImport: i0, template: "<div #resizer\n *ngIf=\"resizableArea\"\n [class]=\"classes.resizer\"\n>\n</div>\n<div [class]=\"classes.grid\"></div>\n", dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
__decorate([
Style((_value, _media) => ({ after }, selectors) => {
const $$ = selectors(STYLES);
return (_className) => `${_className}{border-radius:50%;}${_className} ${$$.resizer}{${after}:${pos}%;bottom:${pos}%;transform:translate(4px,4px);}${_className} ${$$.grid}{border-radius:50%;overflow:hidden;}`;
}, coerceBooleanProperty)
], LyCropperArea.prototype, "round", void 0);
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyCropperArea, decorators: [{
type: Component,
args: [{ selector: 'ly-cropper-area', providers: [
StyleRenderer
], changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'lyCropperArea', standalone: true, imports: [NgIf], template: "<div #resizer\n *ngIf=\"resizableArea\"\n [class]=\"classes.resizer\"\n>\n</div>\n<div [class]=\"classes.grid\"></div>\n" }]
}], propDecorators: { round: [{
type: Input,
args: [{
transform: booleanAttribute
}]
}] } });
class LyImageCropper extends _LyImageCropperBase {
constructor() {
super(...arguments);
this.sRenderer = inject(StyleRenderer);
this.classes = this.sRenderer.renderSheet(STYLES, 'root');
this.areaGridActiveCssClass = this.classes.showGrid;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropper, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: LyImageCropper, isStandalone: true, selector: "ly-img-cropper, ly-image-cropper", providers: [
StyleRenderer,
{ provide: _LyImageCropperBase, useExisting: LyImageCropper },
], usesInheritance: true, ngImport: i0, template: "<!-- (selectstart): On Safari starting to slide temporarily triggers text selection mode which\nshow the wrong cursor. We prevent it by stopping the `selectstart` event. -->\n<div #_cropperContainer class=\"{{classes.cropperContainer}}\">\n <div #_imgContainer\n [className]=\"classes.imgContainer\"\n (selectstart)=\"$event.preventDefault()\"\n >\n <canvas #_imgCanvas></canvas>\n </div>\n <ly-cropper-area #_area\n [round]=\"!!config.round\"\n [resizableArea]=\"!!config.resizableArea\"\n [keepAspectRatio]=\"!!config.keepAspectRatio\"\n *ngIf=\"_isLoadedImg; else content\"\n [ngStyle]=\"{\n width: config.width + 'px',\n height: config.height + 'px'\n }\"></ly-cropper-area>\n</div>\n<ng-template #content>\n <div [className]=\"classes.defaultContent\">\n <input #_fileInput type=\"file\" (change)=\"selectInputEvent($event)\" accept=\"image/*\">\n <ng-content></ng-content>\n </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: LyCropperArea, selector: "ly-cropper-area", inputs: ["round"], exportAs: ["lyCropperArea"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropper, decorators: [{
type: Component,
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, selector: 'ly-img-cropper, ly-image-cropper', providers: [
StyleRenderer,
{ provide: _LyImageCropperBase, useExisting: LyImageCropper },
], standalone: true, imports: [LyCropperArea, NgStyle, NgIf], template: "<!-- (selectstart): On Safari starting to slide temporarily triggers text selection mode which\nshow the wrong cursor. We prevent it by stopping the `selectstart` event. -->\n<div #_cropperContainer class=\"{{classes.cropperContainer}}\">\n <div #_imgContainer\n [className]=\"classes.imgContainer\"\n (selectstart)=\"$event.preventDefault()\"\n >\n <canvas #_imgCanvas></canvas>\n </div>\n <ly-cropper-area #_area\n [round]=\"!!config.round\"\n [resizableArea]=\"!!config.resizableArea\"\n [keepAspectRatio]=\"!!config.keepAspectRatio\"\n *ngIf=\"_isLoadedImg; else content\"\n [ngStyle]=\"{\n width: config.width + 'px',\n height: config.height + 'px'\n }\"></ly-cropper-area>\n</div>\n<ng-template #content>\n <div [className]=\"classes.defaultContent\">\n <input #_fileInput type=\"file\" (change)=\"selectInputEvent($event)\" accept=\"image/*\">\n <ng-content></ng-content>\n </div>\n</ng-template>\n" }]
}] });
/**
* @dynamic
*/
class LyCropperAreaBase extends _LyCropperAreaBase {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyCropperAreaBase, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: LyCropperAreaBase, isStandalone: true, selector: "ly-cropper-area-base", host: { classAttribute: "ly-cropper-area" }, exportAs: ["lyCropperAreaBase"], usesInheritance: true, ngImport: i0, template: "@if (resizableArea) {\n <div #resizer\n class=\"ly-cropper-area-resizer\"\n >\n </div>\n}\n<div class=\"ly-cropper-area-grid\"></div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyCropperAreaBase, decorators: [{
type: Component,
args: [{ selector: 'ly-cropper-area-base', changeDetection: ChangeDetectionStrategy.OnPush, exportAs: 'lyCropperAreaBase', standalone: true, host: {
'class': 'ly-cropper-area'
}, template: "@if (resizableArea) {\n <div #resizer\n class=\"ly-cropper-area-resizer\"\n >\n </div>\n}\n<div class=\"ly-cropper-area-grid\"></div>\n" }]
}] });
class LyImageCropperBase extends _LyImageCropperBase {
constructor() {
super(...arguments);
this.areaGridActiveCssClass = 'ly-cropper-area-grid-active';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBase, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: LyImageCropperBase, isStandalone: true, selector: "ly-img-cropper-base, ly-image-cropper-base", host: { classAttribute: "ly-cropper-root" }, providers: [
{ provide: _LyImageCropperBase, useExisting: LyImageCropperBase },
], exportAs: ["lyImageCropperBase"], usesInheritance: true, ngImport: i0, template: "<!-- (selectstart): On Safari starting to slide temporarily triggers text selection mode which\nshow the wrong cursor. We prevent it by stopping the `selectstart` event. -->\n<div #_cropperContainer class=\"ly-cropper-container\">\n <div #_imgContainer\n class=\"ly-cropper-img-container\"\n (selectstart)=\"$event.preventDefault()\"\n >\n <canvas #_imgCanvas></canvas>\n </div>\n @if (_isLoadedImg) {\n <ly-cropper-area-base #_area\n [round]=\"!!config.round\"\n [resizableArea]=\"!!config.resizableArea\"\n [keepAspectRatio]=\"!!config.keepAspectRatio\"\n [ngStyle]=\"{\n width: config.width + 'px',\n height: config.height + 'px'\n }\"></ly-cropper-area-base>\n } @else {\n <div class=\"ly-cropper-default-content\">\n <input #_fileInput type=\"file\" (change)=\"selectInputEvent($event)\" accept=\"image/*\">\n <ng-content></ng-content>\n </div>\n }\n</div>\n", styles: [".ly-cropper-root{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;overflow:hidden;position:relative;justify-content:center;align-items:center}.ly-cropper-container{position:relative;margin:auto;width:80%;height:80%}.ly-cropper-img-container{cursor:move;position:absolute;top:0;left:0;display:flex;touch-action:none}.ly-cropper-img-container>canvas{display:block}.ly-cropper-area{pointer-events:none;box-shadow:0 0 0 20000px #0006;position:absolute;inset:0;margin:auto}.ly-cropper-area-round{border-radius:50%}.ly-cropper-area-round.ly-cropper-area-resizer{right:14.64%;bottom:14.64%;transform:translate(4px,4px)}.ly-cropper-area-round.ly-cropper-area-grid{border-radius:50%;overflow:hidden}.ly-cropper-area:before,.ly-cropper-area:after{position:absolute;inset:0;content:\"\"}.ly-cropper-area:before{width:0;height:0;margin:auto;border-radius:50%;background:#fff;border:solid 2px rgb(255,255,255)}.ly-cropper-area:after{border:solid 2px rgb(255,255,255);border-radius:inherit}.ly-cropper-area-grid{position:absolute;width:100%;height:100%;opacity:0;transition:cubic-bezier(.4,0,1,1)}.ly-cropper-area-grid:before,.ly-cropper-area-grid:after{content:\" \";box-sizing:border-box;position:absolute;border:1px solid rgba(255,255,255,.4)}.ly-cropper-area-grid:before{inset:0 33.33%;border-top:0;border-bottom:0}.ly-cropper-area-grid:after{inset:33.33% 0;border-left:0;border-right:0}.ly-cropper-area-grid-active .ly-cropper-area-grid{opacity:1}.ly-cropper-area-resizer{width:10px;height:10px;background:#fff;border-radius:3px;position:absolute;touch-action:none;bottom:0;right:0;pointer-events:all;cursor:nwse-resize}.ly-cropper-area-resizer:before{position:absolute;inset:0;content:\"\";width:30px;height:30px;transform:translate(-35%,-35%)}.ly-cropper-default-content{display:flex;align-items:center;justify-content:center}.ly-cropper-default-content,.ly-cropper-default-content>input{position:absolute;inset:0}.ly-cropper-default-content *:not(input){pointer-events:none}.ly-cropper-default-content>input{background:transparent;opacity:0;width:100%;height:100%}\n"], dependencies: [{ kind: "component", type: LyCropperAreaBase, selector: "ly-cropper-area-base", exportAs: ["lyCropperAreaBase"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBase, decorators: [{
type: Component,
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ly-img-cropper-base, ly-image-cropper-base', providers: [
{ provide: _LyImageCropperBase, useExisting: LyImageCropperBase },
], standalone: true, exportAs: 'lyImageCropperBase', imports: [LyCropperAreaBase, NgStyle], host: {
'class': 'ly-cropper-root'
}, encapsulation: ViewEncapsulation.None, template: "<!-- (selectstart): On Safari starting to slide temporarily triggers text selection mode which\nshow the wrong cursor. We prevent it by stopping the `selectstart` event. -->\n<div #_cropperContainer class=\"ly-cropper-container\">\n <div #_imgContainer\n class=\"ly-cropper-img-container\"\n (selectstart)=\"$event.preventDefault()\"\n >\n <canvas #_imgCanvas></canvas>\n </div>\n @if (_isLoadedImg) {\n <ly-cropper-area-base #_area\n [round]=\"!!config.round\"\n [resizableArea]=\"!!config.resizableArea\"\n [keepAspectRatio]=\"!!config.keepAspectRatio\"\n [ngStyle]=\"{\n width: config.width + 'px',\n height: config.height + 'px'\n }\"></ly-cropper-area-base>\n } @else {\n <div class=\"ly-cropper-default-content\">\n <input #_fileInput type=\"file\" (change)=\"selectInputEvent($event)\" accept=\"image/*\">\n <ng-content></ng-content>\n </div>\n }\n</div>\n", styles: [".ly-cropper-root{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;overflow:hidden;position:relative;justify-content:center;align-items:center}.ly-cropper-container{position:relative;margin:auto;width:80%;height:80%}.ly-cropper-img-container{cursor:move;position:absolute;top:0;left:0;display:flex;touch-action:none}.ly-cropper-img-container>canvas{display:block}.ly-cropper-area{pointer-events:none;box-shadow:0 0 0 20000px #0006;position:absolute;inset:0;margin:auto}.ly-cropper-area-round{border-radius:50%}.ly-cropper-area-round.ly-cropper-area-resizer{right:14.64%;bottom:14.64%;transform:translate(4px,4px)}.ly-cropper-area-round.ly-cropper-area-grid{border-radius:50%;overflow:hidden}.ly-cropper-area:before,.ly-cropper-area:after{position:absolute;inset:0;content:\"\"}.ly-cropper-area:before{width:0;height:0;margin:auto;border-radius:50%;background:#fff;border:solid 2px rgb(255,255,255)}.ly-cropper-area:after{border:solid 2px rgb(255,255,255);border-radius:inherit}.ly-cropper-area-grid{position:absolute;width:100%;height:100%;opacity:0;transition:cubic-bezier(.4,0,1,1)}.ly-cropper-area-grid:before,.ly-cropper-area-grid:after{content:\" \";box-sizing:border-box;position:absolute;border:1px solid rgba(255,255,255,.4)}.ly-cropper-area-grid:before{inset:0 33.33%;border-top:0;border-bottom:0}.ly-cropper-area-grid:after{inset:33.33% 0;border-left:0;border-right:0}.ly-cropper-area-grid-active .ly-cropper-area-grid{opacity:1}.ly-cropper-area-resizer{width:10px;height:10px;background:#fff;border-radius:3px;position:absolute;touch-action:none;bottom:0;right:0;pointer-events:all;cursor:nwse-resize}.ly-cropper-area-resizer:before{position:absolute;inset:0;content:\"\";width:30px;height:30px;transform:translate(-35%,-35%)}.ly-cropper-default-content{display:flex;align-items:center;justify-content:center}.ly-cropper-default-content,.ly-cropper-default-content>input{position:absolute;inset:0}.ly-cropper-default-content *:not(input){pointer-events:none}.ly-cropper-default-content>input{background:transparent;opacity:0;width:100%;height:100%}\n"] }]
}] });
class LyImageCropperModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperModule, imports: [LyImageCropper, LyCropperArea], exports: [LyImageCropper, LyCropperArea] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperModule, decorators: [{
type: NgModule,
args: [{
imports: [LyImageCropper, LyCropperArea],
exports: [LyImageCropper, LyCropperArea]
}]
}] });
class LyImageCropperBaseModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBaseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBaseModule, imports: [LyImageCropperBase], exports: [LyImageCropperBase] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBaseModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: LyImageCropperBaseModule, decorators: [{
type: NgModule,
args: [{
imports: [LyImageCropperBase],
exports: [LyImageCropperBase]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { ImgCropperConfig, ImgCropperError, ImgResolution, LyCropperArea, LyCropperAreaBase, LyImageCropper, LyImageCropperBase, LyImageCropperBaseModule, LyImageCropperModule, PointerChange, STYLES, _LyCropperAreaBase, _LyImageCropperBase, _normalizeDegrees };
//# sourceMappingURL=alyle-ui-image-cropper.mjs.map