@libs-ui/components-image-editor 0.2.161

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.
@@ -0,0 +1,994 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { EventEmitter, Output, Input, Component, signal, input, model, viewChild, output, inject, Optional, Inject, ChangeDetectionStrategy } from '@angular/core';
4
+ import { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';
5
+ import { LibsUiComponentsInputsValidComponent } from '@libs-ui/components-inputs-valid';
6
+ import { LibsUiComponentsModalComponent } from '@libs-ui/components-modal';
7
+ import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';
8
+ import { LibsUiComponentsSpinnerComponent } from '@libs-ui/components-spinner';
9
+ import { LibsUiDynamicComponentService } from '@libs-ui/services-dynamic-component';
10
+ import { get, convertBase64ToBlob, getLabelBySizeFile, convertBlobToFile, isNil, LINK_IMAGE_ERROR_TOKEN_INJECT } from '@libs-ui/utils';
11
+ import * as i1 from '@ngx-translate/core';
12
+ import { TranslateModule } from '@ngx-translate/core';
13
+ import { Subject, fromEvent } from 'rxjs';
14
+ import { takeUntil, tap, mergeMap } from 'rxjs/operators';
15
+
16
+ const cropRationItems = () => {
17
+ return [
18
+ {
19
+ key: 'free',
20
+ icon: 'libs-ui-icon-customize-image-outline'
21
+ },
22
+ {
23
+ key: '1:1',
24
+ value: 1,
25
+ icon: 'libs-ui-icon-ratio-1-1'
26
+ },
27
+ {
28
+ key: '2:3',
29
+ value: 2 / 3,
30
+ icon: 'libs-ui-icon-ratio-2-3'
31
+ },
32
+ {
33
+ key: '3:2',
34
+ value: 3 / 2,
35
+ icon: 'libs-ui-icon-ratio-3-2'
36
+ },
37
+ {
38
+ key: '3:4',
39
+ value: 3 / 4,
40
+ icon: 'libs-ui-icon-ratio-3-4'
41
+ },
42
+ {
43
+ key: '4:3',
44
+ value: 4 / 3,
45
+ icon: 'libs-ui-icon-ratio-4-3'
46
+ },
47
+ {
48
+ key: '9:16',
49
+ value: 9 / 16,
50
+ icon: 'libs-ui-icon-ratio-9-16'
51
+ },
52
+ {
53
+ key: '16:9',
54
+ value: 16 / 9,
55
+ icon: 'libs-ui-icon-ratio-16-9'
56
+ }
57
+ ];
58
+ };
59
+ const getMimeTypeFromSrc = (src) => {
60
+ if (!src) {
61
+ return undefined;
62
+ }
63
+ const match = /^data:(.*?);/.exec(src);
64
+ if (match) {
65
+ return match[1];
66
+ }
67
+ const srcSplit = src.split('.');
68
+ const mineType = srcSplit[srcSplit.length - 1];
69
+ return `image/${(mineType.toLowerCase()) === 'jpg' ? 'jpeg' : mineType}`;
70
+ };
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ const getDataUrl = (canvas, mimetype, src) => {
73
+ const mimeTypeBySrc = getMimeTypeFromSrc(src);
74
+ if (mimetype || mimeTypeBySrc) {
75
+ return canvas.toDataURL(mimetype || mimeTypeBySrc);
76
+ }
77
+ return canvas.toDataURL();
78
+ };
79
+ const getWidthHeightResizeCropFollow = (ratioValue, width, height, maxWidth, maxHeight, minWidth, minHeight) => {
80
+ height = Math.min(height, maxHeight);
81
+ height = Math.max(minHeight, height);
82
+ if (ratioValue) {
83
+ width = height * ratioValue;
84
+ width = Math.min(maxWidth, width);
85
+ width = Math.max(minWidth, width);
86
+ height = width / ratioValue;
87
+ }
88
+ width = Math.min(maxWidth, width);
89
+ width = Math.max(minWidth, width);
90
+ return [width, height];
91
+ };
92
+ const getCropRectImage = (rectClip, imgWidth, imgHeight, scale) => {
93
+ return {
94
+ left: Math.max(rectClip.left * scale, 0),
95
+ top: Math.max(rectClip.top * scale, 0),
96
+ width: rectClip.width * scale,
97
+ height: rectClip.height * scale
98
+ };
99
+ };
100
+ const getStylesOfElement = (element, fields) => {
101
+ return fields?.map(field => parseFloat(get(element, field) || '0'));
102
+ };
103
+
104
+ class LibsUiComponentsImageEditorResizeComponent {
105
+ buttonsFooter;
106
+ image = new Image();
107
+ originFileSize;
108
+ currentFileSize;
109
+ inputValidFunctionControl;
110
+ resizeData;
111
+ originWidth;
112
+ originHeight;
113
+ src;
114
+ zIndex;
115
+ mimetype;
116
+ outClose = new EventEmitter();
117
+ outSave = new EventEmitter();
118
+ constructor() {
119
+ this.buttonsFooter = [{
120
+ type: 'button-third',
121
+ label: 'i18n_cancel',
122
+ action: async () => this.outClose.emit()
123
+ }, {
124
+ label: 'i18n_save',
125
+ action: async () => {
126
+ this.outSave.emit(this.resizeData);
127
+ this.outClose.emit();
128
+ }
129
+ }];
130
+ this.inputValidFunctionControl = [];
131
+ }
132
+ async ngOnInit() {
133
+ const fileSize = await this.getFileSize();
134
+ this.currentFileSize = fileSize;
135
+ this.originFileSize = fileSize;
136
+ }
137
+ async handlerResizeRatio() {
138
+ let ratio = parseInt(`${this.resizeData.ratio}`);
139
+ if (!ratio || ratio < 0) {
140
+ return;
141
+ }
142
+ if (ratio > 100) {
143
+ ratio = 100;
144
+ this.resizeData.ratio = ratio;
145
+ }
146
+ this.resizeData.width = Math.max(parseInt(`${this.originWidth * ratio / 100}`), 1);
147
+ this.resizeData.height = Math.max(parseInt(`${this.originHeight * ratio / 100}`), 1);
148
+ // this.inputValidFunctionControl.forEach(item => item.detectChanges());
149
+ this.currentFileSize = await this.getFileSize();
150
+ }
151
+ getFileSize() {
152
+ return new Promise((resolve) => {
153
+ this.image.onload = () => {
154
+ const canvas = document.createElement('canvas');
155
+ canvas.width = this.resizeData.width;
156
+ canvas.height = this.resizeData.height;
157
+ const ctx = canvas.getContext('2d');
158
+ if (ctx) {
159
+ ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
160
+ }
161
+ const src = getDataUrl(canvas, this.mimetype, this.src);
162
+ const file = convertBase64ToBlob(src);
163
+ resolve(getLabelBySizeFile(convertBlobToFile(file, Date.now().toLocaleString()).size));
164
+ };
165
+ this.image.src = this.src;
166
+ });
167
+ }
168
+ handlerResizeWidth() {
169
+ this.processResizeWidthOrHeight('width', 'height', this.originWidth, this.originHeight);
170
+ }
171
+ handlerResizeHeight() {
172
+ this.processResizeWidthOrHeight('height', 'width', this.originHeight, this.originWidth);
173
+ }
174
+ async processResizeWidthOrHeight(resizeBy, fieldResize, originByValue, originOtherValue) {
175
+ const value = parseInt(`${this.resizeData[resizeBy]}`);
176
+ if (!value || value > originByValue) {
177
+ return;
178
+ }
179
+ this.resizeData[fieldResize] = Math.max(parseInt(`${originOtherValue * value / originByValue}`), 1);
180
+ this.resizeData.ratio = Math.max(parseInt(`${100 * value / originByValue}`), 1);
181
+ this.currentFileSize = await this.getFileSize();
182
+ }
183
+ handlerInputValidFunctionControl(event) {
184
+ this.inputValidFunctionControl.push(event);
185
+ }
186
+ handlerEvent(event) {
187
+ if (event === 'close') {
188
+ this.outClose.emit();
189
+ }
190
+ }
191
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorResizeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
192
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: LibsUiComponentsImageEditorResizeComponent, isStandalone: true, selector: "libs-ui-components-image_editor-resize", inputs: { resizeData: "resizeData", originWidth: "originWidth", originHeight: "originHeight", src: "src", zIndex: "zIndex", mimetype: "mimetype" }, outputs: { outClose: "outClose", outSave: "outSave" }, ngImport: i0, template: "<libs_ui-components-modal [headerConfig]=\"{ignoreHeaderTheme: true}\"\n [mode]=\"'center'\"\n [width]=\"'500px'\"\n [height]=\"'auto'\"\n [title]=\"'i18n_resize' | translate\"\n [zIndex]=\"zIndex\"\n [buttonsFooter]=\"buttonsFooter\"\n (outEvent)=\"handlerEvent($event)\">\n <div class=\"libs-ui-modal-body-custom w-full h-full\">\n <div class=\"flex items-center\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_ratio_size' | translate }} (%)\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[132px]'\"\n [dataType]=\"'int'\"\n [fieldNameBind]=\"'ratio'\"\n [(item)]=\"resizeData\"\n [minValueNumber]=\"1\"\n [maxValueNumber]=\"100\"\n [valueUpDownNumber]=\"1\"\n [validRequired]=\"{isRequired: true}\"\n [ignoreShowError]=\"true\"\n (outValueChange)=\"handlerResizeRatio()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"mt-[16px] relative\">\n <div class=\"flex items-center\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_width' | translate }}\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[102px]'\"\n [dataType]=\"'int'\"\n [maxValueNumber]=\"originWidth\"\n [minValueNumber]=\"1\"\n [fieldNameBind]=\"'width'\"\n [(item)]=\"resizeData\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n (outValueChange)=\"handlerResizeWidth()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"flex items-center mt-[8px]\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_length' | translate }}\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[102px]'\"\n [dataType]=\"'int'\"\n [minValueNumber]=\"1\"\n [maxValueNumber]=\"originHeight\"\n [fieldNameBind]=\"'height'\"\n [(item)]=\"resizeData\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n (outValueChange)=\"handlerResizeHeight()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"absolute flex items-center left-[236px] top-[14px]\">\n <div\n class=\"w-[20px] h-[44px] libs-ui-border-top-general libs-ui-border-right-general libs-ui-border-bottom-general\">\n </div>\n <div class=\"ml-[6px] text-[#9ca2ad] libs-ui-icon-lock\"></div>\n </div>\n </div>\n <div class=\"mt-[16px]\">\n <div class=\"libs-ui-font-h6m text-[#6a7383] mb-[8px]\">{{ 'i18n_size_photo' | translate }}</div>\n <div class=\"libs-ui-font-h5r rounded-[4px] py-[8px] px-[16px] bg-[#f8f9fa] text-[#9ca2ad]\">\n {{ 'i18n_file_size_compare_when_resize' | translate:{current_size: currentFileSize, original_size: originFileSize} }}\n </div>\n </div>\n </div>\n</libs_ui-components-modal>\n", dependencies: [{ kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "component", type: LibsUiComponentsModalComponent, selector: "libs_ui-components-modal", inputs: ["show", "mode", "isBackdropTransparent", "isBackgroundTransparentModal", "isSizeBackdropByWidthHeightInput", "hasShadowBoxWhenHiddenBackDropTransparent", "classIncludeModalWrapper", "zIndex", "width", "height", "maxWidth", "maxHeight", "minWidth", "isFullScreen", "disable", "ignoreCommunicateMicroEvent", "headerConfig", "bodyConfig", "footerConfig", "buttonsFooter", "title", "titleUseXssFilter", "titleUseTooltip", "titleUseInnerText"], outputs: ["showChange", "widthChange", "heightChange", "maxWidthChange", "maxHeightChange", "minWidthChange", "disableChange", "buttonsFooterChange", "outScrollContent", "outEvent", "outFunctionControl"] }, { kind: "component", type: LibsUiComponentsInputsValidComponent, selector: "libs_ui-components-inputs-valid", inputs: ["item", "labelConfig", "emitEmptyInDataTypeNumber", "ignoreBlockInputMaxValue", "fieldNameBind", "showCount", "typeComponentSelectItem", "valueComponentSelectItem", "disableComponentSelectItem", "tagInput", "dataType", "typeInput", "modeInput", "resetAutoCompletePassword", "textAreaEnterNotNewLine", "fixedFloat", "acceptNegativeValue", "valueUpDownNumber", "ignoreWidthInput100", "classIncludeInput", "classContainerInput", "readonly", "disable", "noBorder", "backgroundNone", "useColorModeExist", "placeholder", "keepPlaceholderOnly", "classContainerBottomInput", "autoRemoveEmoji", "defaultHeight", "maxHeightTextArea", "minHeightTextArea", "ignoreShowError", "borderError", "iconLeftClass", "popoverContentIconLeft", "iconRightClass", "popoverContentIconRight", "zIndexPopoverContent", "unitsLeft", "configUnitLeft", "keySelectedUnitLeft", "unitsRight", "configUnitRight", "keySelectedUnitRight", "maxValueNumber", "minValueNumber", "ignoreContentLeft", "ignoreContentRight", "isBaselineStyle", "valuePatternShowError", "validPattern", "validRequired", "validMinLength", "validMinValue", "validMaxValue", "validMaxLength", "functionValid", "maxLength", "positionMessageErrorStartInput", "classInclude", "resize", "templateLeftBottomInput", "templateRightBottomInput", "onlyAcceptNegativeValue", "autoAddZeroLessThan10InTypeInt", "maxLengthNumberCount", "classMessageErrorInclude", "ignoreStopPropagationEvent", "ignoreUnitRightClassReadOnly", "paddingRightCustomSpecific", "focusTimeOut"], outputs: ["itemChange", "outValueChange", "outSelect", "outIconLeft", "outIconRight", "outClickButtonLabel", "outSwitchEventLabel", "outLabelRightClick", "outEnterInputEvent", "outHeightAreaChange", "outFunctionsControl", "outFocusAndBlur", "outChangeValueByButtonUpDown"] }] });
193
+ }
194
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorResizeComponent, decorators: [{
195
+ type: Component,
196
+ args: [{ selector: 'libs-ui-components-image_editor-resize', standalone: true, imports: [
197
+ TranslateModule, LibsUiComponentsModalComponent, LibsUiComponentsInputsValidComponent
198
+ ], template: "<libs_ui-components-modal [headerConfig]=\"{ignoreHeaderTheme: true}\"\n [mode]=\"'center'\"\n [width]=\"'500px'\"\n [height]=\"'auto'\"\n [title]=\"'i18n_resize' | translate\"\n [zIndex]=\"zIndex\"\n [buttonsFooter]=\"buttonsFooter\"\n (outEvent)=\"handlerEvent($event)\">\n <div class=\"libs-ui-modal-body-custom w-full h-full\">\n <div class=\"flex items-center\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_ratio_size' | translate }} (%)\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[132px]'\"\n [dataType]=\"'int'\"\n [fieldNameBind]=\"'ratio'\"\n [(item)]=\"resizeData\"\n [minValueNumber]=\"1\"\n [maxValueNumber]=\"100\"\n [valueUpDownNumber]=\"1\"\n [validRequired]=\"{isRequired: true}\"\n [ignoreShowError]=\"true\"\n (outValueChange)=\"handlerResizeRatio()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"mt-[16px] relative\">\n <div class=\"flex items-center\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_width' | translate }}\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[102px]'\"\n [dataType]=\"'int'\"\n [maxValueNumber]=\"originWidth\"\n [minValueNumber]=\"1\"\n [fieldNameBind]=\"'width'\"\n [(item)]=\"resizeData\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n (outValueChange)=\"handlerResizeWidth()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"flex items-center mt-[8px]\">\n <div class=\"libs-ui-font-h7r mr-[4px] text-[#6a7383] w-[100px] shrink-0\">\n {{ 'i18n_length' | translate }}\n </div>\n <libs_ui-components-inputs-valid [ignoreWidthInput100]=\"true\"\n [classIncludeInput]=\"'w-[102px]'\"\n [dataType]=\"'int'\"\n [minValueNumber]=\"1\"\n [maxValueNumber]=\"originHeight\"\n [fieldNameBind]=\"'height'\"\n [(item)]=\"resizeData\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n (outValueChange)=\"handlerResizeHeight()\"\n (outFunctionsControl)=\"handlerInputValidFunctionControl($event)\" />\n </div>\n <div class=\"absolute flex items-center left-[236px] top-[14px]\">\n <div\n class=\"w-[20px] h-[44px] libs-ui-border-top-general libs-ui-border-right-general libs-ui-border-bottom-general\">\n </div>\n <div class=\"ml-[6px] text-[#9ca2ad] libs-ui-icon-lock\"></div>\n </div>\n </div>\n <div class=\"mt-[16px]\">\n <div class=\"libs-ui-font-h6m text-[#6a7383] mb-[8px]\">{{ 'i18n_size_photo' | translate }}</div>\n <div class=\"libs-ui-font-h5r rounded-[4px] py-[8px] px-[16px] bg-[#f8f9fa] text-[#9ca2ad]\">\n {{ 'i18n_file_size_compare_when_resize' | translate:{current_size: currentFileSize, original_size: originFileSize} }}\n </div>\n </div>\n </div>\n</libs_ui-components-modal>\n" }]
199
+ }], ctorParameters: () => [], propDecorators: { resizeData: [{
200
+ type: Input,
201
+ args: [{ required: true }]
202
+ }], originWidth: [{
203
+ type: Input,
204
+ args: [{ required: true }]
205
+ }], originHeight: [{
206
+ type: Input,
207
+ args: [{ required: true }]
208
+ }], src: [{
209
+ type: Input,
210
+ args: [{ required: true }]
211
+ }], zIndex: [{
212
+ type: Input
213
+ }], mimetype: [{
214
+ type: Input
215
+ }], outClose: [{
216
+ type: Output
217
+ }], outSave: [{
218
+ type: Output
219
+ }] } });
220
+
221
+ /* eslint-disable @typescript-eslint/no-explicit-any */
222
+ class LibsUiComponentsImageEditorComponent {
223
+ linkImageError;
224
+ loading = signal(false);
225
+ loadingImage = signal(true);
226
+ changed = signal(false);
227
+ originWidth = signal(0);
228
+ originHeight = signal(0);
229
+ cropRatioItems = signal(cropRationItems());
230
+ cropRatioItemSelected = signal('');
231
+ cropSize = signal({ width: 20, height: 20 });
232
+ resizeData = signal({ ratio: 100, width: 600, height: 800 });
233
+ dragGridCrop = signal(false);
234
+ image = new Image();
235
+ canvas = document.createElement('canvas');
236
+ containerHeight = signal(800);
237
+ containerWidth = signal(1200);
238
+ imgContainerRect = signal({ top: 0, left: 0, width: 0, height: 0 });
239
+ rectClip = signal({ top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 });
240
+ resizeState = signal('none');
241
+ moveState = signal('none');
242
+ startMouseDim = signal({ clientX: 0, clientY: 0, width: 0, height: 0, top: 0, left: 0, imageTop: 0, imageLeft: 0, imageWidth: 0, imageHeight: 0 });
243
+ dataUrlOrigin = signal('');
244
+ ratioValue = signal(0);
245
+ minHeight = 20;
246
+ minWidth = 20;
247
+ onDestroy = new Subject();
248
+ resizeComponentRef;
249
+ modeShowButton = input('save-file');
250
+ mimetype = input();
251
+ zIndex = input(1200);
252
+ imgSrc = model.required();
253
+ originUrl = input();
254
+ nameFile = input();
255
+ hasZoom = input(false);
256
+ aspectRatio = model();
257
+ requiredCropFollowRatio = input(false);
258
+ imageOrigin = viewChild.required('imageOrigin');
259
+ imageClip = viewChild.required('imageClip');
260
+ imageEditorContainer = viewChild.required('imageEditorContainer');
261
+ imageContainer = viewChild.required('imageContainer');
262
+ cropArea = viewChild.required('cropArea');
263
+ cropTL = viewChild.required('cropTL');
264
+ cropBL = viewChild.required('cropBL');
265
+ cropBR = viewChild.required('cropBR');
266
+ cropTR = viewChild.required('cropTR');
267
+ cropLineVL = viewChild.required('cropLineVL');
268
+ cropLineVR = viewChild.required('cropLineVR');
269
+ cropLineHT = viewChild.required('cropLineHT');
270
+ cropLineHB = viewChild.required('cropLineHB');
271
+ circleTL = viewChild.required('circleTL');
272
+ circleTR = viewChild.required('circleTR');
273
+ circleBL = viewChild.required('circleBL');
274
+ circleBR = viewChild.required('circleBR');
275
+ outClose = output();
276
+ outSaveFile = output();
277
+ outFunctionsControl = output();
278
+ dynamicComponentService = inject(LibsUiDynamicComponentService);
279
+ constructor(linkImageError) {
280
+ this.linkImageError = linkImageError;
281
+ }
282
+ ngAfterViewInit() {
283
+ if (this.aspectRatio()) {
284
+ this.dragGridCrop.set(true);
285
+ const aspectRatioFound = this.cropRatioItems().find(item => item.key === this.aspectRatio()?.key);
286
+ this.cropRatioItemSelected.set(aspectRatioFound?.key || this.cropRatioItems()[0].key);
287
+ this.updateOriginImageSize();
288
+ this.changedImage(true);
289
+ }
290
+ this.updateModalSize();
291
+ this.initData();
292
+ this.initMouseEvent();
293
+ this.outFunctionsControl.emit({
294
+ cropImage: this.cropImage.bind(this),
295
+ setLoadingState: (loading) => this.loading.set(loading)
296
+ });
297
+ }
298
+ handlerImageLoaded() {
299
+ setTimeout(() => {
300
+ this.updateModalSize();
301
+ this.loadingImage.set(false);
302
+ }, 500);
303
+ }
304
+ handlerImageError(e) {
305
+ console.log('e', e);
306
+ e.target.src = this.linkImageError;
307
+ }
308
+ updateModalSize() {
309
+ const maxHeight = window.innerHeight - 100;
310
+ const maxWidth = window.innerWidth - 400;
311
+ this.containerHeight.set(maxHeight - 124);
312
+ this.containerWidth.set(maxWidth - 384);
313
+ this.updateOriginImageSize();
314
+ }
315
+ initData() {
316
+ this.imgContainerRect.set(this.imageContainer().nativeElement.getBoundingClientRect());
317
+ this.image.setAttribute('crossorigin', 'anonymous');
318
+ this.image.onload = () => {
319
+ this.imgContainerRect.set(this.imageContainer().nativeElement.getBoundingClientRect());
320
+ this.canvas.height = this.image.height;
321
+ this.canvas.width = this.image.width;
322
+ this.originHeight.set(this.image.height);
323
+ this.originWidth.set(this.image.width);
324
+ this.updateOriginImageSize();
325
+ this.canvas.getContext('2d')?.drawImage(this.image, 0, 0, this.canvas.width, this.canvas.height);
326
+ this.dataUrlOrigin.set(getDataUrl(this.canvas, this.mimetype(), this.image.src));
327
+ this.imgSrc.set(this.dataUrlOrigin());
328
+ };
329
+ this.image.src = this.imgSrc();
330
+ }
331
+ initMouseEvent() {
332
+ const mouseUp = this.initEvent({ nativeElement: document }, 'mouseup', { callStopPropagation: true, callPreventDefault: true }, undefined, () => this.handlerMouseup());
333
+ const mouseMove = this.initEvent({ nativeElement: document }, 'mousemove', { callStopPropagation: true, callPreventDefault: true }).pipe(takeUntil(mouseUp));
334
+ this.initEvent({ nativeElement: document }, 'wheel', { callStopPropagation: true }, undefined, (e) => this.handlerMousewheel(e)).subscribe();
335
+ this.initEvent({ nativeElement: window }, 'resize', {}, undefined, () => this.updateModalSize()).subscribe();
336
+ this.initEvent(this.imageContainer(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => this.handlerImageContainerMousedown(e)).subscribe((e) => this.handlerMousemove(e));
337
+ this.initEvent(this.cropTL(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'TL'); }).subscribe((e) => this.handlerMousemove(e));
338
+ this.initEvent(this.cropBL(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'BL'); }).subscribe((e) => this.handlerMousemove(e));
339
+ this.initEvent(this.cropBR(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'BR'); }).subscribe((e) => this.handlerMousemove(e));
340
+ this.initEvent(this.cropTR(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'TR'); }).subscribe((e) => this.handlerMousemove(e));
341
+ this.initEvent(this.cropLineVL(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'VL'); }).subscribe((e) => this.handlerMousemove(e));
342
+ this.initEvent(this.cropLineVR(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'VR'); }).subscribe((e) => this.handlerMousemove(e));
343
+ this.initEvent(this.cropLineHT(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'HT'); }).subscribe((e) => this.handlerMousemove(e));
344
+ this.initEvent(this.cropLineHB(), 'mousedown', { callStopPropagation: true, callPreventDefault: true }, mouseMove, (e) => { this.handlerCropResize(e, 'HB'); }).subscribe((e) => this.handlerMousemove(e));
345
+ this.initEvent(this.cropArea(), 'mousedown', {}, undefined, (e) => this.handlerMousedownCropArea(e)).subscribe((e) => this.handlerMousemove(e));
346
+ }
347
+ initEvent(element, eventName, flag, obsMerge, callback) {
348
+ const obs = fromEvent(element.nativeElement, eventName).pipe(tap((e) => {
349
+ if (flag.callStopPropagation && e?.stopPropagation) {
350
+ e.stopPropagation();
351
+ }
352
+ if (flag.callPreventDefault && e?.preventDefault) {
353
+ e.preventDefault();
354
+ }
355
+ if (callback) {
356
+ callback(e);
357
+ }
358
+ }), takeUntil(this.onDestroy));
359
+ if (obsMerge) {
360
+ return obs.pipe(mergeMap(() => obsMerge), (takeUntil(this.onDestroy)));
361
+ }
362
+ return obs;
363
+ }
364
+ ;
365
+ updateOriginImageSize() {
366
+ let width = this.originWidth();
367
+ let height = this.originHeight();
368
+ const ratio = width / height;
369
+ if (width >= height) {
370
+ width = Math.min(this.containerWidth(), width);
371
+ height = Math.min(width / ratio, this.containerHeight());
372
+ width = height * ratio;
373
+ }
374
+ else {
375
+ height = Math.min(this.containerHeight(), height);
376
+ width = height * ratio;
377
+ }
378
+ this.setImagePosition(width, height);
379
+ this.updateCropAreaPos();
380
+ this.updateRectClipPos();
381
+ }
382
+ setImagePosition(width, height) {
383
+ const left = this.containerWidth() / 2 - width / 2;
384
+ const top = this.containerHeight() / 2 - height / 2;
385
+ const styles = { width, height, top, left, display: 'block' };
386
+ this.setStylesElements(this.imageOrigin(), styles);
387
+ this.setStylesElements(this.imageClip(), styles);
388
+ this.setCirclePosition(styles);
389
+ }
390
+ setCirclePosition(styles) {
391
+ this.setStylesElements(this.circleTL(), { top: (styles.top || 0) - 5, left: (styles.left || 0) - 5 });
392
+ this.setStylesElements(this.circleTR(), { top: (styles.top || 0) - 5, left: (styles.left || 0) + (styles.width || 0) - 10 });
393
+ this.setStylesElements(this.circleBL(), { top: (styles.top || 0) + (styles.height || 0) - 10, left: (styles.left || 0) - 5 });
394
+ this.setStylesElements(this.circleBR(), { top: (styles.top || 0) + (styles.height || 0) - 10, left: (styles.left || 0) + (styles.width || 0) - 10 });
395
+ }
396
+ updateCropAreaPos() {
397
+ const [imgTop, imgLeft, imgWidth, imgHeight] = getStylesOfElement(this.imageClip().nativeElement, ['style.top', 'style.left', 'offsetWidth', 'offsetHeight']);
398
+ const rectImg = {
399
+ top: Math.max(0, imgTop),
400
+ left: Math.max(0, imgLeft),
401
+ width: imgWidth,
402
+ height: imgHeight
403
+ };
404
+ rectImg.width = Math.min(imgWidth, this.containerWidth() - imgLeft);
405
+ if (imgLeft < 0) {
406
+ rectImg.width = Math.min(imgWidth + imgLeft, this.containerWidth());
407
+ }
408
+ rectImg.height = Math.min(imgHeight, this.containerHeight() - imgTop);
409
+ if (imgTop < 0) {
410
+ rectImg.height = Math.min(imgTop + imgHeight, this.containerHeight());
411
+ }
412
+ this.ratioValue.set(this.getCropRatioValue());
413
+ if (!this.ratioValue()) {
414
+ this.setStylesElements(this.cropArea(), rectImg);
415
+ return;
416
+ }
417
+ let width = rectImg.width;
418
+ let height = width / this.ratioValue();
419
+ width = Math.min(rectImg.width, width);
420
+ height = width / this.ratioValue();
421
+ height = Math.min(rectImg.height, height);
422
+ width = height * this.ratioValue();
423
+ const top = rectImg.top + (rectImg.height - height) / 2;
424
+ const left = rectImg.left + (rectImg.width - width) / 2;
425
+ this.setStylesElements(this.cropArea(), { width, height, top, left });
426
+ }
427
+ getCropRatioValue() {
428
+ const cropRatio = this.aspectRatio() || this.cropRatioItems().find(item => item.key === this.cropRatioItemSelected());
429
+ if (!cropRatio) {
430
+ return this.originWidth() / this.originHeight();
431
+ }
432
+ return cropRatio.value ?? 0;
433
+ }
434
+ updateRectClipPos() {
435
+ const reactClip = this.getRectClipImage();
436
+ this.imageClip().nativeElement.style.clip = `rect(${reactClip.top}px, ${reactClip.right}px, ${reactClip.bottom}px, ${reactClip.left}px)`;
437
+ }
438
+ getRectClipImage() {
439
+ const imageRect = this.imageClip().nativeElement.getBoundingClientRect();
440
+ if (!imageRect.width) {
441
+ return this.rectClip();
442
+ }
443
+ const cropRect = this.cropArea().nativeElement.getBoundingClientRect();
444
+ const width = Math.round(cropRect.width);
445
+ const height = Math.round(cropRect.height);
446
+ const left = cropRect.left - imageRect.left;
447
+ const top = cropRect.top - imageRect.top;
448
+ const right = Math.round(left + width);
449
+ const bottom = Math.round(top + height);
450
+ const ratio = this.originWidth() / imageRect.width;
451
+ this.rectClip.set({ left, top, right, bottom, width, height });
452
+ this.cropSize.set({
453
+ height: top >= 0 ? (Math.round((bottom - top) * ratio)) : (Math.floor((bottom - top) * ratio)),
454
+ width: top >= 0 ? (Math.round(((right - left) * (this.ratioValue() ?? 1)) * ratio)) : (Math.floor(((right - left) * (this.ratioValue() ?? 1)) * ratio))
455
+ });
456
+ return this.rectClip();
457
+ }
458
+ handlerMousemove(e) {
459
+ const typeState = ['BL', 'TL', 'TR', 'BR', 'VL', 'VR', 'HT', 'HB'];
460
+ const methodName = `resizeCropFollow${this.resizeState()}Dir`; // resizeCropFollowBLDir, resizeCropFollowTLDir, resizeCropFollowTRDir, resizeCropFollowBRDir, resizeCropFollowVLDir, resizeCropFollowVRDir, resizeCropFollowHTDir, resizeCropFollowHBDir
461
+ if (typeState.includes(this.resizeState())) {
462
+ this[methodName](e);
463
+ }
464
+ switch (this.moveState()) {
465
+ case 'clip':
466
+ this.moveClipArea(e);
467
+ break;
468
+ case 'image':
469
+ this.moveImage(e);
470
+ break;
471
+ }
472
+ this.changedImage(true);
473
+ }
474
+ resizeCropFollowBLDir = (e) => {
475
+ const momentY = e.clientY - this.startMouseDim().clientY;
476
+ const momentX = this.ratioValue() ? -momentY * this.ratioValue() : e.clientX - this.startMouseDim().clientX;
477
+ const maxHeight = this.startMouseDim().imageTop + this.startMouseDim().imageHeight - this.startMouseDim().top;
478
+ const maxWidth = this.startMouseDim().left + this.startMouseDim().width - this.startMouseDim().imageLeft;
479
+ let { width, height, left } = this.getRectResizeCropFollow({ width: -1, height: 1, left: 1, top: 0 }, momentX, momentY);
480
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
481
+ left = this.startMouseDim().left + this.startMouseDim().width - width;
482
+ this.setStylesElements(this.cropArea(), { left, height, width }, true);
483
+ };
484
+ resizeCropFollowTLDir = (e) => {
485
+ const momentY = e.clientY - this.startMouseDim().clientY;
486
+ const momentX = this.ratioValue() ? this.ratioValue() * momentY : e.clientX - this.startMouseDim().clientX;
487
+ const maxHeight = this.startMouseDim().top + this.startMouseDim().height - this.startMouseDim().imageTop;
488
+ const maxWidth = this.startMouseDim().left + this.startMouseDim().width - this.startMouseDim().imageLeft;
489
+ let { width, height, top, left } = this.getRectResizeCropFollow({ width: -1, height: -1, left: 1, top: 1 }, momentX, momentY);
490
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
491
+ left = this.startMouseDim().left + this.startMouseDim().width - width;
492
+ top = this.startMouseDim().top + this.startMouseDim().height - height;
493
+ this.setStylesElements(this.cropArea(), { top, left, height, width }, true);
494
+ };
495
+ resizeCropFollowTRDir = (e) => {
496
+ const momentY = e.clientY - this.startMouseDim().clientY;
497
+ const momentX = this.ratioValue() ? -momentY * this.ratioValue() : e.clientX - this.startMouseDim().clientX;
498
+ const maxHeight = this.startMouseDim().top + this.startMouseDim().height - this.startMouseDim().imageTop;
499
+ const maxWidth = this.startMouseDim().imageLeft + this.startMouseDim().imageWidth - this.startMouseDim().left;
500
+ let { width, height, top } = this.getRectResizeCropFollow({ width: 1, height: -1, left: 0, top: 1 }, momentX, momentY);
501
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
502
+ top = this.startMouseDim().top + this.startMouseDim().height - height;
503
+ this.setStylesElements(this.cropArea(), { top, height, width }, true);
504
+ };
505
+ resizeCropFollowBRDir(e) {
506
+ const momentY = e.clientY - this.startMouseDim().clientY;
507
+ const momentX = this.ratioValue() ? momentY * this.ratioValue() : e.clientX - this.startMouseDim().clientX;
508
+ const maxHeight = this.startMouseDim().imageTop + this.startMouseDim().imageHeight - this.startMouseDim().top;
509
+ const maxWidth = this.startMouseDim().imageLeft + this.startMouseDim().imageWidth - this.startMouseDim().left;
510
+ let { width, height } = this.getRectResizeCropFollow({ width: 1, height: 1, left: 0, top: 0 }, momentX, momentY);
511
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
512
+ this.setStylesElements(this.cropArea(), { height, width }, true);
513
+ }
514
+ resizeCropFollowVLDir = (e) => {
515
+ const momentX = e.clientX - this.startMouseDim().clientX;
516
+ const momentY = this.ratioValue() ? momentX / this.ratioValue() : 0;
517
+ const maxHeight = this.startMouseDim().top + this.startMouseDim().height - this.startMouseDim().imageTop;
518
+ const maxWidth = this.startMouseDim().left + this.startMouseDim().width - this.startMouseDim().imageLeft;
519
+ let { width, height, top, left } = this.getRectResizeCropFollow({ width: -1, height: -1, left: 1, top: 1 }, momentX, momentY);
520
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
521
+ left = this.startMouseDim().left + this.startMouseDim().width - width;
522
+ top = this.startMouseDim().top + this.startMouseDim().height - height;
523
+ this.setStylesElements(this.cropArea(), { top, left, height, width }, true);
524
+ };
525
+ resizeCropFollowVRDir = (e) => {
526
+ const momentX = e.clientX - this.startMouseDim().clientX;
527
+ const momentY = this.ratioValue() ? -momentX / this.ratioValue() : 0;
528
+ const maxHeight = this.startMouseDim().top + this.startMouseDim().height - this.startMouseDim().imageTop;
529
+ const maxWidth = this.startMouseDim().imageLeft + this.startMouseDim().imageWidth - this.startMouseDim().left;
530
+ let { width, height, top } = this.getRectResizeCropFollow({ width: 1, height: -1, left: 0, top: 1 }, momentX, momentY);
531
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
532
+ top = this.startMouseDim().top + this.startMouseDim().height - height;
533
+ this.setStylesElements(this.cropArea(), { top, height, width }, true);
534
+ };
535
+ resizeCropFollowHTDir = (e) => {
536
+ const momentY = e.clientY - this.startMouseDim().clientY;
537
+ const momentX = this.ratioValue() ? momentY * this.ratioValue() : 0;
538
+ const maxWidth = this.startMouseDim().width + this.startMouseDim().left - this.startMouseDim().imageLeft;
539
+ const maxHeight = this.startMouseDim().top + this.startMouseDim().height - this.startMouseDim().imageTop;
540
+ let { width, height, top, left } = this.getRectResizeCropFollow({ width: -1, height: -1, left: 1, top: 1 }, momentX, momentY);
541
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
542
+ top = this.startMouseDim().top + this.startMouseDim().height - height;
543
+ left = this.startMouseDim().left + this.startMouseDim().width - width;
544
+ this.setStylesElements(this.cropArea(), { top, left, height, width }, true);
545
+ };
546
+ resizeCropFollowHBDir = (e) => {
547
+ const momentY = e.clientY - this.startMouseDim().clientY;
548
+ const momentX = this.ratioValue() ? momentY * this.ratioValue() : 0;
549
+ const maxHeight = this.startMouseDim().imageTop + this.startMouseDim().imageHeight - this.startMouseDim().top;
550
+ const maxWidth = this.startMouseDim().width + this.startMouseDim().left - this.startMouseDim().imageLeft;
551
+ let { width, height, left } = this.getRectResizeCropFollow({ width: 1, height: 1, left: -1, top: 0 }, momentX, momentY);
552
+ [width, height] = getWidthHeightResizeCropFollow(this.ratioValue(), width, height, maxWidth, maxHeight, this.minWidth, this.minHeight);
553
+ left = this.startMouseDim().left + this.startMouseDim().width - width;
554
+ this.setStylesElements(this.cropArea(), { height, left, width }, true);
555
+ };
556
+ getRectResizeCropFollow(ratio, momentX, momentY) {
557
+ const width = this.startMouseDim().width + momentX * ratio.width;
558
+ const height = this.startMouseDim().height + momentY * ratio.height;
559
+ const top = this.startMouseDim().top + momentY * ratio.top;
560
+ const left = this.startMouseDim().left + momentX * ratio.left;
561
+ return { width, height, left, top };
562
+ }
563
+ handlerMouseup() {
564
+ this.imageContainer().nativeElement.classList.remove('cursor-move');
565
+ this.cropArea().nativeElement.classList.remove('cursor-move');
566
+ this.resizeState.set('none');
567
+ this.moveState.set('none');
568
+ }
569
+ handlerMousewheel(event) {
570
+ if (!this.hasZoom()) {
571
+ return;
572
+ }
573
+ if (event.deltaY < 0) {
574
+ this.zoomImage(1 / 1.1);
575
+ return;
576
+ }
577
+ this.zoomImage(1.1);
578
+ }
579
+ handlerImageContainerMousedown(e) {
580
+ if (this.resizeState() === 'none' && this.moveState() === 'none') {
581
+ this.moveState.set('image');
582
+ this.handlerMousedown(e);
583
+ }
584
+ return false;
585
+ }
586
+ handlerCropResize(e, resizeType) {
587
+ this.resizeState.set(resizeType);
588
+ this.dragGridCrop.set(true);
589
+ this.handlerMousedown(e);
590
+ }
591
+ handlerMousedownCropArea(e) {
592
+ this.moveState.set('clip');
593
+ this.handlerMousedown(e);
594
+ }
595
+ handlerMousedown(e) {
596
+ const [width, height, top, left] = getStylesOfElement(this.cropArea().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
597
+ const [imageWidth, imageHeight, imageTop, imageLeft] = getStylesOfElement(this.imageOrigin().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
598
+ switch (this.moveState()) {
599
+ case 'image':
600
+ this.imageContainer().nativeElement.classList.add('cursor-move');
601
+ break;
602
+ case 'clip':
603
+ if (width === imageWidth && height === imageHeight) {
604
+ return;
605
+ }
606
+ this.cropArea().nativeElement.classList.add('cursor-move');
607
+ break;
608
+ default:
609
+ break;
610
+ }
611
+ this.startMouseDim.set({
612
+ clientX: e.clientX,
613
+ clientY: e.clientY,
614
+ width: width,
615
+ height: height,
616
+ top: top,
617
+ left: left,
618
+ imageTop: imageTop,
619
+ imageLeft: imageLeft,
620
+ imageWidth: imageWidth,
621
+ imageHeight: imageHeight
622
+ });
623
+ this.ratioValue.set(this.getCropRatioValue());
624
+ }
625
+ moveImage(e) {
626
+ const momentY = e.clientY - this.startMouseDim().clientY;
627
+ const momentX = e.clientX - this.startMouseDim().clientX;
628
+ const imgTop = this.startMouseDim().imageTop + momentY;
629
+ const imgLeft = this.startMouseDim().imageLeft + momentX;
630
+ const top = this.startMouseDim().top + momentY;
631
+ const left = this.startMouseDim().left + momentX;
632
+ this.setStylesElements(this.imageOrigin(), { top: imgTop, left: imgLeft });
633
+ this.setStylesElements(this.imageClip(), { top: imgTop, left: imgLeft });
634
+ this.setStylesElements(this.cropArea(), { top, left });
635
+ this.setCirclePosition({ top: imgTop, left: imgLeft, width: this.startMouseDim().imageWidth, height: this.startMouseDim().imageHeight });
636
+ }
637
+ moveClipArea(e) {
638
+ const [imgWidth, imgHeight, imgTop, imgLeft] = getStylesOfElement(this.imageClip().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
639
+ const [cropWidth, cropHeight] = getStylesOfElement(this.cropArea().nativeElement, ['offsetWidth', 'offsetHeight']);
640
+ const momentY = e.clientY - this.startMouseDim().clientY;
641
+ const momentX = e.clientX - this.startMouseDim().clientX;
642
+ let top = Math.max(imgTop, this.startMouseDim().top + momentY);
643
+ let left = Math.max(imgLeft, this.startMouseDim().left + momentX);
644
+ top = Math.min(top, imgTop + imgHeight - cropHeight);
645
+ left = Math.min(left, imgLeft + imgWidth - cropWidth);
646
+ this.setStylesElements(this.cropArea(), { top, left }, true);
647
+ }
648
+ zoomImage(scale) {
649
+ const [currWidth, currHeight, currTop, currLeft] = getStylesOfElement(this.imageOrigin().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
650
+ const width = Math.max(currWidth * scale, 50);
651
+ const height = this.imageOrigin().nativeElement.offsetHeight * width / this.imageOrigin().nativeElement.offsetWidth;
652
+ const top = currTop + (currHeight - height) / 2;
653
+ const left = currLeft + (currWidth - width) / 2;
654
+ let [cropWidth, cropHeight] = getStylesOfElement(this.cropArea().nativeElement, ['offsetWidth', 'offsetHeight']);
655
+ const [cropTop, cropLeft] = getStylesOfElement(this.cropArea().nativeElement, ['style.top', 'style.left']);
656
+ const maxCropWidth = width + left - cropLeft;
657
+ const maxCropHeight = height + top - cropTop;
658
+ const minWidth = 20;
659
+ const minHeight = 20;
660
+ cropWidth = Math.min(cropWidth, maxCropWidth);
661
+ cropHeight = Math.min(cropHeight, maxCropHeight);
662
+ cropWidth = Math.max(cropWidth, minWidth);
663
+ cropHeight = Math.max(cropHeight, minHeight);
664
+ this.ratioValue.set(this.getCropRatioValue());
665
+ if (this.ratioValue()) {
666
+ cropHeight = cropWidth / this.ratioValue();
667
+ cropHeight = Math.min(cropHeight, maxCropHeight);
668
+ cropHeight = Math.max(cropHeight, minHeight);
669
+ cropWidth = cropHeight * this.ratioValue();
670
+ }
671
+ this.rectClip.update(rect => {
672
+ const rectTop = cropTop - top;
673
+ const rectLeft = cropLeft - left;
674
+ return {
675
+ ...rect,
676
+ top: rectTop,
677
+ left: rectLeft,
678
+ right: rectLeft + cropWidth,
679
+ bottom: rectTop + cropHeight
680
+ };
681
+ });
682
+ const clip = `rect(${this.rectClip().top}px, ${this.rectClip().right}px, ${this.rectClip().bottom}px, ${this.rectClip().left}px)`;
683
+ this.setStylesElements(this.cropArea(), { top: cropTop, left: cropLeft, width: cropWidth, height: cropHeight });
684
+ this.setStylesElements(this.imageOrigin(), { top, left, width, height });
685
+ this.setCirclePosition({ top, left, width, height });
686
+ this.setStylesElements(this.imageClip(), { clip, top, left, width, height });
687
+ this.getRectClipImage();
688
+ }
689
+ handlerRotateImage(event) {
690
+ event.stopPropagation();
691
+ this.image.src = this.imgSrc();
692
+ this.image.onload = () => {
693
+ const canvas = document.createElement('canvas');
694
+ const ctx = canvas.getContext("2d");
695
+ const [width, height] = getStylesOfElement(this.imageOrigin().nativeElement, ['offsetWidth', 'offsetHeight']);
696
+ const originWidth = this.image.width;
697
+ const originHeight = this.image.height;
698
+ let top = parseInt(this.imageOrigin().nativeElement.style.top);
699
+ let left = parseInt(this.imageOrigin().nativeElement.style.left);
700
+ let newHeight = width;
701
+ let newWidth = height;
702
+ const ratio = newWidth / newHeight;
703
+ canvas.height = originWidth;
704
+ canvas.width = originHeight;
705
+ ctx?.translate(originHeight / 2, originWidth / 2);
706
+ ctx?.rotate(Math.PI / 2);
707
+ ctx?.drawImage(this.image, -originWidth / 2, -originHeight / 2);
708
+ const src = getDataUrl(canvas, this.mimetype(), this.image.src);
709
+ this.imgSrc.set(src);
710
+ newWidth = Math.max(newWidth, originHeight);
711
+ newWidth = Math.min(newWidth, this.containerWidth());
712
+ newHeight = newWidth / ratio;
713
+ newHeight = Math.max(newHeight, originWidth);
714
+ newHeight = Math.min(newHeight, this.containerHeight());
715
+ newWidth = newHeight * ratio;
716
+ top = top - (newHeight / 2 - height / 2);
717
+ left = left - (newWidth / 2 - width / 2);
718
+ this.setStylesElements(this.imageOrigin(), { src, top, left, width: newWidth, height: newHeight });
719
+ this.setCirclePosition({ top, left, width: newWidth, height: newHeight });
720
+ this.setStylesElements(this.imageClip(), { src, top, left, width: newWidth, height: newHeight });
721
+ this.getOriginalImageSize(() => {
722
+ this.cropSize.set({ width: this.originWidth(), height: this.originHeight() });
723
+ this.updateOriginImageSize();
724
+ });
725
+ this.changedImage(true);
726
+ };
727
+ }
728
+ getOriginalImageSize(callback) {
729
+ this.image.src = this.imgSrc();
730
+ this.image.onload = () => {
731
+ this.originWidth.set(this.image.width);
732
+ this.originHeight.set(this.image.height);
733
+ if (callback) {
734
+ return callback();
735
+ }
736
+ };
737
+ }
738
+ changedImage(status) {
739
+ this.changed.set(status);
740
+ }
741
+ handlerFlipImage(event, mode) {
742
+ event.stopPropagation();
743
+ this.image.src = this.imgSrc();
744
+ this.image.onload = () => {
745
+ const canvas = document.createElement('canvas');
746
+ const ctx = canvas.getContext("2d");
747
+ const width = this.image.width;
748
+ const height = this.image.height;
749
+ canvas.height = height;
750
+ canvas.width = width;
751
+ if (ctx) {
752
+ ctx.drawImage(this.image, 0, 0, width, height);
753
+ if (mode === 'horizontal') {
754
+ ctx.translate(width, 0);
755
+ ctx.scale(-1, 1);
756
+ }
757
+ if (mode === 'vertical') {
758
+ ctx.translate(0, height);
759
+ ctx.scale(1, -1);
760
+ }
761
+ ctx.clearRect(0, 0, width, height);
762
+ ctx.drawImage(this.image, 0, 0);
763
+ }
764
+ const dataUrl = getDataUrl(canvas, this.mimetype(), this.image.src);
765
+ this.imgSrc.set(dataUrl);
766
+ this.imageOrigin().nativeElement.src = dataUrl;
767
+ this.imageClip().nativeElement.src = dataUrl;
768
+ this.updateCropAreaPos();
769
+ this.updateRectClipPos();
770
+ this.changedImage(true);
771
+ };
772
+ }
773
+ handlerRestoreImage(event) {
774
+ event.stopPropagation();
775
+ this.imgSrc.set(this.dataUrlOrigin());
776
+ this.dragGridCrop.set(false);
777
+ this.getOriginalImageSize(() => {
778
+ this.updateOriginImageSize();
779
+ this.changedImage(false);
780
+ this.cropRatioItemSelected.set('');
781
+ this.updateCropAreaPos();
782
+ this.updateRectClipPos();
783
+ });
784
+ }
785
+ cropImage() {
786
+ return new Promise((resolve) => {
787
+ this.image.src = this.imgSrc();
788
+ this.image.onload = () => {
789
+ const canvas = document.createElement('canvas');
790
+ const originWidth = this.image.width;
791
+ const width = parseInt(this.imageClip().nativeElement.offsetWidth);
792
+ const height = parseInt(this.imageClip().nativeElement.offsetHeight);
793
+ const scale = originWidth / width;
794
+ const rectClip = this.getRectClipImage();
795
+ const cropRect = getCropRectImage(rectClip, width, height, scale);
796
+ canvas.height = cropRect.height;
797
+ canvas.width = cropRect.width;
798
+ this.originHeight.set(canvas.height);
799
+ this.originWidth.set(canvas.width);
800
+ const ctx = canvas.getContext("2d");
801
+ if (ctx) {
802
+ ctx.drawImage(this.image, cropRect.left, cropRect.top, cropRect.width, cropRect.height, 0, 0, cropRect.width, cropRect.height);
803
+ }
804
+ const dataUrl = getDataUrl(canvas, this.mimetype(), this.image.src);
805
+ this.changedImage(true);
806
+ resolve(dataUrl);
807
+ };
808
+ });
809
+ }
810
+ async handlerSaveFile(event, modeSave) {
811
+ event.stopPropagation();
812
+ const dataUrl = await this.cropImage();
813
+ const file = convertBase64ToBlob(dataUrl);
814
+ this.outSaveFile.emit({ file: file, url: dataUrl, mode: modeSave });
815
+ }
816
+ async handlerClose(event) {
817
+ event.stopPropagation();
818
+ this.outClose.emit({ isClickButtonClose: true });
819
+ }
820
+ handlerSelectCropRatioItem(event, key) {
821
+ event.stopPropagation();
822
+ this.cropRatioItemSelected.set(key);
823
+ this.aspectRatio.set(undefined);
824
+ this.changed.set(true);
825
+ this.dragGridCrop.set(true);
826
+ this.updateOriginImageSize();
827
+ }
828
+ handlerResize(event) {
829
+ event.stopPropagation();
830
+ if (this.resizeComponentRef) {
831
+ return;
832
+ }
833
+ this.resizeComponentRef = this.dynamicComponentService.resolveComponentFactory(LibsUiComponentsImageEditorResizeComponent);
834
+ const instance = this.resizeComponentRef.instance;
835
+ const subs = instance.outClose.pipe(takeUntil(this.onDestroy)).subscribe(() => {
836
+ this.dynamicComponentService.remove(this.resizeComponentRef);
837
+ this.resizeComponentRef = undefined;
838
+ subs.unsubscribe();
839
+ });
840
+ instance.src = this.imgSrc();
841
+ instance.mimetype = this.mimetype();
842
+ instance.zIndex = this.zIndex();
843
+ subs.add(instance.outSave.pipe(takeUntil(this.onDestroy)).subscribe(event => {
844
+ this.resizeData.set(event);
845
+ this.saveResize();
846
+ }));
847
+ instance.resizeData = { ratio: 100, width: this.originWidth(), height: this.originHeight() };
848
+ instance.originWidth = this.originWidth();
849
+ instance.originHeight = this.originHeight();
850
+ this.dynamicComponentService.addToBody(this.resizeComponentRef);
851
+ }
852
+ saveResize() {
853
+ this.image.src = this.imgSrc();
854
+ this.image.onload = () => {
855
+ const canvas = document.createElement('canvas');
856
+ canvas.width = this.resizeData().width;
857
+ canvas.height = this.resizeData().height;
858
+ const ctx = canvas.getContext('2d');
859
+ if (ctx) {
860
+ ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height);
861
+ }
862
+ const src = getDataUrl(canvas, this.mimetype(), this.image.src);
863
+ const [currWidth, currHeight, currTop, currLeft] = getStylesOfElement(this.imageOrigin().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
864
+ const ratio = currWidth / currHeight;
865
+ let newWidth = Math.min(this.resizeData().width, this.containerWidth());
866
+ let newHeight = newWidth / ratio;
867
+ newHeight = Math.min(this.resizeData().height, this.containerHeight());
868
+ newWidth = newHeight * ratio;
869
+ Math.min(this.resizeData().height, this.containerHeight());
870
+ const top = currTop + (currHeight - newHeight) / 2;
871
+ const left = currLeft + (currWidth - newWidth) / 2;
872
+ const movementY = currTop - top;
873
+ const movementX = currLeft - left;
874
+ this.rectClip.update(rect => {
875
+ const rectTop = rect.top + movementY;
876
+ const rectLeft = rect.left + movementX;
877
+ return {
878
+ ...rect,
879
+ top: rectTop,
880
+ left: rectLeft,
881
+ right: rectLeft + newWidth,
882
+ bottom: rectTop + newHeight
883
+ };
884
+ });
885
+ const clip = `rect(${this.rectClip().top}px, ${this.rectClip().right}px, ${this.rectClip().bottom}px, ${this.rectClip().left}px)`;
886
+ this.setStylesElements(this.imageOrigin(), { src, top, left, width: newWidth, height: newHeight });
887
+ this.setCirclePosition({ top, left, width: newWidth, height: newHeight });
888
+ this.setStylesElements(this.imageClip(), { src, clip, top, left, width: newWidth, height: newHeight });
889
+ this.imgSrc.set(src);
890
+ this.originWidth.set(this.resizeData().width);
891
+ this.originHeight.set(this.resizeData().height);
892
+ this.updateCropAreaPos();
893
+ this.updateRectClipPos();
894
+ this.changedImage(true);
895
+ };
896
+ }
897
+ handlerCropWidth() {
898
+ this.processCropWidthOrHeight('width');
899
+ }
900
+ handlerCropHeight() {
901
+ this.processCropWidthOrHeight('height');
902
+ }
903
+ processCropWidthOrHeight(cropBy) {
904
+ this.ratioValue.set(this.getCropRatioValue());
905
+ const [currImgWidth, currImgHeight, currImgTop, currImgLeft] = getStylesOfElement(this.imageOrigin().nativeElement, ['offsetWidth', 'offsetHeight', 'style.top', 'style.left']);
906
+ let [cropWidth, cropHeight] = getStylesOfElement(this.cropArea().nativeElement, ['offsetWidth', 'offsetHeight']);
907
+ const [cropTop, cropLeft] = getStylesOfElement(this.cropArea().nativeElement, ['style.top', 'style.left']);
908
+ const maxCropWidth = currImgLeft + currImgWidth - cropLeft;
909
+ const maxCropHeight = currImgHeight + currImgTop - cropTop;
910
+ const ratio = this.originWidth() / currImgWidth;
911
+ let cropValue = get(this.cropSize, cropBy) / ratio;
912
+ cropValue = Math.min(cropValue, cropBy === 'width' ? maxCropWidth : maxCropHeight);
913
+ cropValue = Math.max(1, cropValue);
914
+ if (this.ratioValue()) {
915
+ if (cropBy === 'width') {
916
+ cropHeight = cropValue / this.ratioValue();
917
+ cropHeight = Math.min(cropHeight, maxCropHeight);
918
+ cropValue = cropHeight * this.ratioValue();
919
+ this.cropSize.update(cropSize => ({ ...cropSize, height: Math.round(cropHeight * ratio) }));
920
+ this.cropArea().nativeElement.style.height = `${cropHeight}px`;
921
+ }
922
+ if (cropBy === 'height') {
923
+ cropWidth = cropValue * this.ratioValue();
924
+ cropWidth = Math.min(cropWidth, maxCropWidth);
925
+ cropValue = cropWidth / this.ratioValue();
926
+ this.cropSize.update(cropSize => ({ ...cropSize, width: Math.round(cropWidth * ratio) }));
927
+ this.cropArea().nativeElement.style.width = `${cropWidth}px`;
928
+ }
929
+ }
930
+ this.cropSize.update(cropSize => ({ ...cropSize, [cropBy]: Math.round(cropValue * ratio) }));
931
+ this.cropArea().nativeElement.style[cropBy] = `${cropValue}px`;
932
+ const imageRect = this.imageClip().nativeElement.getBoundingClientRect();
933
+ const cropRect = this.cropArea().nativeElement.getBoundingClientRect();
934
+ const { width, height } = cropRect;
935
+ const left = cropRect.left - imageRect.left;
936
+ const top = cropRect.top - imageRect.top;
937
+ const right = left + width;
938
+ const bottom = top + height;
939
+ this.imageClip().nativeElement.style.clip = `rect(${top}px, ${right}px, ${bottom}px, ${left}px)`;
940
+ }
941
+ setStylesElements(element, styles, isUpdateRectClipPos) {
942
+ if (!isNil(styles.top)) {
943
+ element.nativeElement.style.top = `${styles.top}px`;
944
+ }
945
+ if (!isNil(styles.left)) {
946
+ element.nativeElement.style.left = `${styles.left}px`;
947
+ }
948
+ if (!isNil(styles.width)) {
949
+ element.nativeElement.style.width = `${styles.width}px`;
950
+ }
951
+ if (!isNil(styles.height)) {
952
+ element.nativeElement.style.height = `${styles.height}px`;
953
+ }
954
+ if (!isNil(styles.display)) {
955
+ element.nativeElement.style.display = styles.display;
956
+ }
957
+ if (!isNil(styles.src)) {
958
+ element.nativeElement.src = styles.src;
959
+ }
960
+ if (!isNil(styles.clip)) {
961
+ element.nativeElement.style.clip = styles.clip;
962
+ }
963
+ if (isUpdateRectClipPos) {
964
+ this.updateRectClipPos();
965
+ }
966
+ }
967
+ ngOnDestroy() {
968
+ this.onDestroy.next();
969
+ this.onDestroy.complete();
970
+ this.dynamicComponentService.remove(this.resizeComponentRef);
971
+ // this.dynamicComponentService.remove(this.forwardComponentRef);
972
+ }
973
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorComponent, deps: [{ token: LINK_IMAGE_ERROR_TOKEN_INJECT, optional: true }], target: i0.ɵɵFactoryTarget.Component });
974
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: LibsUiComponentsImageEditorComponent, isStandalone: true, selector: "libs_ui-components-image_editor", inputs: { modeShowButton: { classPropertyName: "modeShowButton", publicName: "modeShowButton", isSignal: true, isRequired: false, transformFunction: null }, mimetype: { classPropertyName: "mimetype", publicName: "mimetype", isSignal: true, isRequired: false, transformFunction: null }, zIndex: { classPropertyName: "zIndex", publicName: "zIndex", isSignal: true, isRequired: false, transformFunction: null }, imgSrc: { classPropertyName: "imgSrc", publicName: "imgSrc", isSignal: true, isRequired: true, transformFunction: null }, originUrl: { classPropertyName: "originUrl", publicName: "originUrl", isSignal: true, isRequired: false, transformFunction: null }, nameFile: { classPropertyName: "nameFile", publicName: "nameFile", isSignal: true, isRequired: false, transformFunction: null }, hasZoom: { classPropertyName: "hasZoom", publicName: "hasZoom", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, requiredCropFollowRatio: { classPropertyName: "requiredCropFollowRatio", publicName: "requiredCropFollowRatio", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imgSrc: "imgSrcChange", aspectRatio: "aspectRatioChange", outClose: "outClose", outSaveFile: "outSaveFile", outFunctionsControl: "outFunctionsControl" }, viewQueries: [{ propertyName: "imageOrigin", first: true, predicate: ["imageOrigin"], descendants: true, isSignal: true }, { propertyName: "imageClip", first: true, predicate: ["imageClip"], descendants: true, isSignal: true }, { propertyName: "imageEditorContainer", first: true, predicate: ["imageEditorContainer"], descendants: true, isSignal: true }, { propertyName: "imageContainer", first: true, predicate: ["imageContainer"], descendants: true, isSignal: true }, { propertyName: "cropArea", first: true, predicate: ["cropArea"], descendants: true, isSignal: true }, { propertyName: "cropTL", first: true, predicate: ["cropTL"], descendants: true, isSignal: true }, { propertyName: "cropBL", first: true, predicate: ["cropBL"], descendants: true, isSignal: true }, { propertyName: "cropBR", first: true, predicate: ["cropBR"], descendants: true, isSignal: true }, { propertyName: "cropTR", first: true, predicate: ["cropTR"], descendants: true, isSignal: true }, { propertyName: "cropLineVL", first: true, predicate: ["cropLineVL"], descendants: true, isSignal: true }, { propertyName: "cropLineVR", first: true, predicate: ["cropLineVR"], descendants: true, isSignal: true }, { propertyName: "cropLineHT", first: true, predicate: ["cropLineHT"], descendants: true, isSignal: true }, { propertyName: "cropLineHB", first: true, predicate: ["cropLineHB"], descendants: true, isSignal: true }, { propertyName: "circleTL", first: true, predicate: ["circleTL"], descendants: true, isSignal: true }, { propertyName: "circleTR", first: true, predicate: ["circleTR"], descendants: true, isSignal: true }, { propertyName: "circleBL", first: true, predicate: ["circleBL"], descendants: true, isSignal: true }, { propertyName: "circleBR", first: true, predicate: ["circleBR"], descendants: true, isSignal: true }], ngImport: i0, template: "<libs_ui-components-modal [width]=\"'calc(100vw - 400px)'\"\n [height]=\"'calc(100vh - 100px)'\"\n [minWidth]=\"'1200px'\"\n [headerConfig]=\"{ignoreHeaderTheme: true, removeButtonClose: true}\"\n [bodyConfig]=\"{classInclude: '!p-0', scrollOverlayOptions: {scrollX: 'hidden', scrollY: 'hidden'}}\"\n [footerConfig]=\"{hidden: true}\"\n [zIndex]=\"zIndex()\"\n [buttonsFooter]=\"[]\"\n [mode]=\"'center'\">\n <div class=\"libs-ui-modal-header-custom w-full flex items-center justify-between\">\n <div class=\"w-full relative mx-[24px] h-[16px]\">\n <div class=\"flex w-full libs-ui-font-h4s absolute\">\n <libs_ui-components-popover [type]=\"'text'\"\n [config]=\"{zIndex: zIndex()}\">\n {{ 'i18n_edit_file_name' | translate:{file_name: nameFile()} }}\n </libs_ui-components-popover>\n </div>\n </div>\n <div class=\"flex items-center\">\n @if (modeShowButton() === 'save-file') {\n <libs_ui-components-buttons-button [type]=\"'button-third'\"\n [label]=\"'i18n_cancel'\"\n [disable]=\"loading() || loadingImage()\"\n [classInclude]=\"'py-[6px] px-[12px] mr-[16px]'\"\n (outClick)=\"handlerClose($event)\" />\n\n <libs_ui-components-buttons-button class=\"mr-[24px]\"\n [label]=\"'i18n_save'\"\n [disable]=\"loading() || loadingImage()\"\n [classInclude]=\"'py-[6px] px-[12px]'\"\n (outClick)=\"handlerSaveFile($event,'save-file')\" />\n }\n @else {\n <libs_ui-components-buttons-button [label]=\"'i18n_save_to_new_file'\"\n [type]=\"'button-outline'\"\n [classInclude]=\"'mx-[16px] py-[6px] px-[12px]'\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerSaveFile($event,'save-api-as-new-file')\" />\n <libs_ui-components-buttons-button [type]=\"'button-secondary'\"\n [label]=\"'i18n_save'\"\n [classInclude]=\"'py-[6px] px-[12px]'\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerSaveFile($event,'save-api')\" />\n <div class=\"h-[12px] libs-ui-border-left-general mx-[16px]\"></div>\n <libs_ui-components-buttons-button class=\"mr-[24px]\"\n [classInclude]=\"'p-[6px]'\"\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [disable]=\"loading()\"\n [popover]=\"{config: {content: 'i18n_exit_edit_mode', zIndex: zIndex()}}\"\n [classIconLeft]=\"'libs-ui-icon-close'\"\n (outClick)=\"handlerClose($event)\" />\n }\n </div>\n </div>\n <div class=\"libs-ui-modal-body-custom h-full w-full flex\">\n <div #imageEditorContainer\n class=\"relative w-full h-full bg-[#e6e7ea] border-radius-bottom-left-8px p-[32px]\">\n @if (loadingImage()) {\n <div class=\"absolute h-full w-full bg-[#e6e7ea] top-0 left-0 z-[5]\">\n <libs_ui-components-spinner [size]=\"'medium'\" />\n </div>\n }\n <div class=\"absolute h-[32px] w-[calc(100%-64px)] bg-[#e6e7eab8] top-0 left-[32px] z-[1]\">\n </div>\n <div class=\"absolute w-[32px] h-full bg-[#e6e7eab8] left-0 top-0 z-[1] rounded-bl-[8px]\">\n </div>\n <div class=\"absolute w-[32px] h-full bg-[#e6e7eab8] right-0 top-0 z-[1]\">\n </div>\n <div class=\"absolute h-[32px] w-[calc(100%-64px)] bg-[#e6e7eab8] bottom-0 left-[32px] z-[1]\">\n </div>\n <div #imageContainer\n class=\"relative w-full h-full rounded-bl-[8px] min-h-0\">\n <div #circleTL\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleTR\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleBL\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleBR\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <img #imageOrigin\n class=\"absolute border-[4px] border-[#8e61ee] rounded-[4px] w-0 h-0\"\n loading=\"lazy\"\n [src]=\"imgSrc()\"\n (load)=\"handlerImageLoaded()\"\n (error)=\"handlerImageError($event)\" />\n <img #imageClip\n class=\"absolute z-[1] w-0 h-0\"\n loading=\"lazy\"\n [src]=\"imgSrc()\"\n (error)=\"handlerImageError($event)\" />\n <div class=\"absolute top-0 left-0 right-0 bottom-0 bg-[#e6e7eab8]\">\n </div>\n <div #cropArea\n class=\"absolute z-[2] border-[4px] border-[#8e61ee] rounded-[4px]\">\n <div class=\"relative w-full h-full\">\n <div #cropTL\n class=\"absolute z-[2] left-[-18px] top-[-17px] libs-ui-image-editor-edit-cursor-nwse-resize\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropBL\n class=\"absolute z-[2] left-[-17px] bottom-[-18px] libs-ui-image-editor-edit-cursor-nesw-resize rotate-[270deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropTR\n class=\"absolute z-[2] right-[-17px] top-[-17px] libs-ui-image-editor-edit-cursor-nesw-resize rotate-[90deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropBR\n class=\"absolute z-[2] right-[-18px] bottom-[-17px] libs-ui-image-editor-edit-cursor-nwse-resize rotate-[180deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropLineVL\n class=\"absolute z-[1] w-[12px] left-[-6px] top-0 bottom-0 libs-ui-image-editor-edit-cursor-ew-resize\">\n </div>\n <div #cropLineVR\n class=\"absolute z-[1] w-[12px] right-[-6px] top-0 bottom-0 libs-ui-image-editor-edit-cursor-ew-resize\">\n </div>\n <div #cropLineHT\n class=\"absolute z-[1] h-[12px] left-0 top-[-6px] right-0 libs-ui-image-editor-edit-cursor-ns-resize\">\n </div>\n <div #cropLineHB\n class=\"absolute z-[1] h-[12px] left-0 right-0 bottom-[-6px] libs-ui-image-editor-edit-cursor-ns-resize\">\n </div>\n <div class=\"absolute z-[1] h-[1px] top-0 left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] h-[1px] bottom-0 left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] w-[1px] top-0 left-0 bottom-0\">\n </div>\n <div class=\"absolute z-[1] w-[1px] top-0 right-0 bottom-0\">\n </div>\n <div class=\"absolute z-[1] h-[2px] top-[33px] left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] h-[2px] top-[66px] left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] w-[2px] top-0 bottom-0 left-[33px]\">\n </div>\n <div class=\"absolute z-[1] w-[2px] top-0 bottom-0 left-[66px]\">\n </div>\n </div>\n </div>\n </div>\n @if (loading()) {\n <libs_ui-components-spinner [size]=\"'medium'\" />\n }\n </div>\n <div class=\"w-[320px] shrink-0 p-[16px] bg-[#ffffff] rounded-br-[8px] z-[2]\">\n <div class=\"flex items-center justify-between mb-[16px]\">\n <div class=\"libs-ui-font-h4m\">{{ 'i18n_cutting_ratio' | translate }}</div>\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [type]=\"'button-third'\"\n [classIconLeft]=\"'libs-ui-icon-refresh'\"\n [classInclude]=\"'p-[6px] mr-[8px]'\"\n [popover]=\"{config: {content: 'i18n_restore_the_original_state', zIndex: zIndex()}}\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerRestoreImage($event)\" />\n </div>\n <div class=\"mb-[16px] libs-ui-image-editor-edit-display-grid-gap-16\">\n @for (item of cropRatioItems(); track item.key) {\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage() || requiredCropFollowRatio()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"item.icon+' !text-[20px]'\"\n (outClick)=\"handlerSelectCropRatioItem($event, item.key)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ (item.key === 'free' ? 'i18n_custom' : item.key) | translate }}\n </div>\n </div>\n }\n </div>\n <div class=\"mb-[16px] flex\">\n <libs_ui-components-inputs-valid class=\"mr-[16px]\"\n [dataType]=\"'int'\"\n [labelConfig]=\"{labelLeft: 'i18n_width', classInclude: 'mb-[4px]', labelLeftClass: 'libs-ui-font-h7r'}\"\n [fieldNameBind]=\"'width'\"\n [(item)]=\"cropSize\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n [maxValueNumber]=\"originWidth()\"\n [disable]=\"loading() || loadingImage()\"\n (outValueChange)=\"handlerCropWidth()\" />\n <libs_ui-components-inputs-valid [dataType]=\"'int'\"\n [labelConfig]=\"{labelLeft: 'i18n_length', classInclude: 'mb-[4px]', labelLeftClass: 'libs-ui-font-h7r'}\"\n [fieldNameBind]=\"'height'\"\n [(item)]=\"cropSize\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n [maxValueNumber]=\"originHeight()\"\n [disable]=\"loading() || loadingImage()\"\n (outValueChange)=\"handlerCropHeight()\" />\n </div>\n <div class=\"libs-ui-font-h4m mb-[16px]\">{{ 'i18n_rotate_photos' | translate }}</div>\n <div class=\"flex libs-ui-image-editor-edit-display-grid-gap-16\">\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-rotate-image-outline !text-[20px]'\"\n (outClick)=\"handlerRotateImage($event)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_rotate_ninety_degrees' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-flip-vertical !text-[20px]'\"\n (outClick)=\"handlerFlipImage($event, 'vertical')\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_vertical_flip' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-flip-horizontal !text-[20px]'\"\n (outClick)=\"handlerFlipImage($event, 'horizontal')\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_horizontal_flip' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-scale !text-[20px]'\"\n (outClick)=\"handlerResize($event)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_resize' | translate }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</libs_ui-components-modal>\n\n<ng-template #svgArrow>\n <svg xmlns=\"http://www.w3.org/2000/svg\"\n width=\"40\"\n height=\"40\"\n viewBox=\"0 0 40 40\"\n fill=\"none\">\n <path d=\"M33.6987 14.6969L18.3309 14.6969C16.516 14.6969 15.0311 16.1818 15.0311 17.9967L15.0311 33.3645\"\n stroke=\"white\"\n stroke-width=\"5.83333\"\n stroke-miterlimit=\"10\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n</ng-template>\n\n<ng-template #svgCircle>\n <svg xmlns=\"http://www.w3.org/2000/svg\"\n width=\"15\"\n height=\"15\"\n viewBox=\"0 0 15 15\"\n fill=\"none\">\n <circle cx=\"7.5\"\n cy=\"7.5\"\n r=\"7.5\"\n fill=\"white\" />\n </svg>\n</ng-template>\n", styles: [".libs-ui-image-editor-edit-display-grid-gap-16{display:grid;gap:16px;grid-template-columns:1fr 1fr 1fr 1fr}.libs-ui-image-editor-edit-top-33{top:33.33%}.libs-ui-image-editor-edit-top-66{top:66.66%}.libs-ui-image-editor-edit-left-33{left:33.33%}.libs-ui-image-editor-edit-left-66{left:66.66%}.libs-ui-image-editor-edit-cursor-ew-resize{cursor:ew-resize!important}.libs-ui-image-editor-edit-cursor-ns-resize{cursor:ns-resize!important}.libs-ui-image-editor-edit-cursor-nwse-resize{cursor:nwse-resize!important}.libs-ui-image-editor-edit-cursor-nesw-resize{cursor:nesw-resize!important}.libs-ui-image-editor-edit-action-item{display:flex;flex-direction:column;align-items:center;width:60px;cursor:pointer}.libs-ui-image-editor-edit-action-item-icon{padding:12px;border-radius:8px;width:max-content;border:1px solid #e6e7ea}.libs-ui-image-editor-edit-action-item-icon:before{font-size:20px}.libs-ui-image-editor-edit-action-item:hover .libs-ui-image-editor-edit-action-item-icon{border:1px solid var(--mo-global-color-primary-border-hover)}.libs-ui-image-editor-edit-action-item:hover .libs-ui-image-editor-edit-action-item-icon:before{color:var(--mo-global-color-primary-text-bright-20)!important}.libs-ui-image-editor-edit-action-item[active=true] .libs-ui-image-editor-edit-action-item-icon{border:1px solid var(--mo-global-color-primary-border-active)!important;background-color:var(--mo-global-color-primary-background-95)!important}.libs-ui-image-editor-edit-action-item[active=true] .libs-ui-image-editor-edit-action-item-icon:before{color:var(--mo-global-color-primary-text-active)!important}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "component", type: LibsUiComponentsPopoverComponent, selector: "libs_ui-components-popover,[LibsUiComponentsPopoverDirective]", inputs: ["debugId", "flagMouse", "type", "mode", "config", "ignoreShowPopover", "elementRefCustom", "classInclude", "ignoreHiddenPopoverContentWhenMouseLeave", "ignoreStopPropagationEvent", "ignoreCursorPointerModeLikeClick", "isAddContentToParentDocument", "ignoreClickOutside"], outputs: ["outEvent", "outChangStageFlagMouse", "outEventPopoverContent", "outFunctionsControl"] }, { kind: "component", type: LibsUiComponentsModalComponent, selector: "libs_ui-components-modal", inputs: ["show", "mode", "isBackdropTransparent", "isBackgroundTransparentModal", "isSizeBackdropByWidthHeightInput", "hasShadowBoxWhenHiddenBackDropTransparent", "classIncludeModalWrapper", "zIndex", "width", "height", "maxWidth", "maxHeight", "minWidth", "isFullScreen", "disable", "ignoreCommunicateMicroEvent", "headerConfig", "bodyConfig", "footerConfig", "buttonsFooter", "title", "titleUseXssFilter", "titleUseTooltip", "titleUseInnerText"], outputs: ["showChange", "widthChange", "heightChange", "maxWidthChange", "maxHeightChange", "minWidthChange", "disableChange", "buttonsFooterChange", "outScrollContent", "outEvent", "outFunctionControl"] }, { kind: "component", type: LibsUiComponentsInputsValidComponent, selector: "libs_ui-components-inputs-valid", inputs: ["item", "labelConfig", "emitEmptyInDataTypeNumber", "ignoreBlockInputMaxValue", "fieldNameBind", "showCount", "typeComponentSelectItem", "valueComponentSelectItem", "disableComponentSelectItem", "tagInput", "dataType", "typeInput", "modeInput", "resetAutoCompletePassword", "textAreaEnterNotNewLine", "fixedFloat", "acceptNegativeValue", "valueUpDownNumber", "ignoreWidthInput100", "classIncludeInput", "classContainerInput", "readonly", "disable", "noBorder", "backgroundNone", "useColorModeExist", "placeholder", "keepPlaceholderOnly", "classContainerBottomInput", "autoRemoveEmoji", "defaultHeight", "maxHeightTextArea", "minHeightTextArea", "ignoreShowError", "borderError", "iconLeftClass", "popoverContentIconLeft", "iconRightClass", "popoverContentIconRight", "zIndexPopoverContent", "unitsLeft", "configUnitLeft", "keySelectedUnitLeft", "unitsRight", "configUnitRight", "keySelectedUnitRight", "maxValueNumber", "minValueNumber", "ignoreContentLeft", "ignoreContentRight", "isBaselineStyle", "valuePatternShowError", "validPattern", "validRequired", "validMinLength", "validMinValue", "validMaxValue", "validMaxLength", "functionValid", "maxLength", "positionMessageErrorStartInput", "classInclude", "resize", "templateLeftBottomInput", "templateRightBottomInput", "onlyAcceptNegativeValue", "autoAddZeroLessThan10InTypeInt", "maxLengthNumberCount", "classMessageErrorInclude", "ignoreStopPropagationEvent", "ignoreUnitRightClassReadOnly", "paddingRightCustomSpecific", "focusTimeOut"], outputs: ["itemChange", "outValueChange", "outSelect", "outIconLeft", "outIconRight", "outClickButtonLabel", "outSwitchEventLabel", "outLabelRightClick", "outEnterInputEvent", "outHeightAreaChange", "outFunctionsControl", "outFocusAndBlur", "outChangeValueByButtonUpDown"] }, { kind: "component", type: LibsUiComponentsButtonsButtonComponent, selector: "libs_ui-components-buttons-button", inputs: ["flagMouse", "type", "buttonCustom", "sizeButton", "label", "disable", "isPending", "imageLeft", "classInclude", "classIconLeft", "classIconRight", "classLabel", "iconOnlyType", "popover", "ignoreStopPropagationEvent", "zIndex", "widthLabelPopover", "styleIconLeft", "styleButton", "ignoreFocusWhenInputTab", "ignoreSetClickWhenShowPopover", "ignorePointerEvent", "isActive"], outputs: ["outClick", "outPopoverEvent", "outFunctionsControl"] }, { kind: "component", type: LibsUiComponentsSpinnerComponent, selector: "libs_ui-components-spinner", inputs: ["type", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
975
+ }
976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsImageEditorComponent, decorators: [{
977
+ type: Component,
978
+ args: [{ selector: 'libs_ui-components-image_editor', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
979
+ NgTemplateOutlet, TranslateModule, LibsUiComponentsPopoverComponent, LibsUiComponentsModalComponent,
980
+ LibsUiComponentsInputsValidComponent, LibsUiComponentsButtonsButtonComponent, LibsUiComponentsSpinnerComponent,
981
+ ], template: "<libs_ui-components-modal [width]=\"'calc(100vw - 400px)'\"\n [height]=\"'calc(100vh - 100px)'\"\n [minWidth]=\"'1200px'\"\n [headerConfig]=\"{ignoreHeaderTheme: true, removeButtonClose: true}\"\n [bodyConfig]=\"{classInclude: '!p-0', scrollOverlayOptions: {scrollX: 'hidden', scrollY: 'hidden'}}\"\n [footerConfig]=\"{hidden: true}\"\n [zIndex]=\"zIndex()\"\n [buttonsFooter]=\"[]\"\n [mode]=\"'center'\">\n <div class=\"libs-ui-modal-header-custom w-full flex items-center justify-between\">\n <div class=\"w-full relative mx-[24px] h-[16px]\">\n <div class=\"flex w-full libs-ui-font-h4s absolute\">\n <libs_ui-components-popover [type]=\"'text'\"\n [config]=\"{zIndex: zIndex()}\">\n {{ 'i18n_edit_file_name' | translate:{file_name: nameFile()} }}\n </libs_ui-components-popover>\n </div>\n </div>\n <div class=\"flex items-center\">\n @if (modeShowButton() === 'save-file') {\n <libs_ui-components-buttons-button [type]=\"'button-third'\"\n [label]=\"'i18n_cancel'\"\n [disable]=\"loading() || loadingImage()\"\n [classInclude]=\"'py-[6px] px-[12px] mr-[16px]'\"\n (outClick)=\"handlerClose($event)\" />\n\n <libs_ui-components-buttons-button class=\"mr-[24px]\"\n [label]=\"'i18n_save'\"\n [disable]=\"loading() || loadingImage()\"\n [classInclude]=\"'py-[6px] px-[12px]'\"\n (outClick)=\"handlerSaveFile($event,'save-file')\" />\n }\n @else {\n <libs_ui-components-buttons-button [label]=\"'i18n_save_to_new_file'\"\n [type]=\"'button-outline'\"\n [classInclude]=\"'mx-[16px] py-[6px] px-[12px]'\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerSaveFile($event,'save-api-as-new-file')\" />\n <libs_ui-components-buttons-button [type]=\"'button-secondary'\"\n [label]=\"'i18n_save'\"\n [classInclude]=\"'py-[6px] px-[12px]'\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerSaveFile($event,'save-api')\" />\n <div class=\"h-[12px] libs-ui-border-left-general mx-[16px]\"></div>\n <libs_ui-components-buttons-button class=\"mr-[24px]\"\n [classInclude]=\"'p-[6px]'\"\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [disable]=\"loading()\"\n [popover]=\"{config: {content: 'i18n_exit_edit_mode', zIndex: zIndex()}}\"\n [classIconLeft]=\"'libs-ui-icon-close'\"\n (outClick)=\"handlerClose($event)\" />\n }\n </div>\n </div>\n <div class=\"libs-ui-modal-body-custom h-full w-full flex\">\n <div #imageEditorContainer\n class=\"relative w-full h-full bg-[#e6e7ea] border-radius-bottom-left-8px p-[32px]\">\n @if (loadingImage()) {\n <div class=\"absolute h-full w-full bg-[#e6e7ea] top-0 left-0 z-[5]\">\n <libs_ui-components-spinner [size]=\"'medium'\" />\n </div>\n }\n <div class=\"absolute h-[32px] w-[calc(100%-64px)] bg-[#e6e7eab8] top-0 left-[32px] z-[1]\">\n </div>\n <div class=\"absolute w-[32px] h-full bg-[#e6e7eab8] left-0 top-0 z-[1] rounded-bl-[8px]\">\n </div>\n <div class=\"absolute w-[32px] h-full bg-[#e6e7eab8] right-0 top-0 z-[1]\">\n </div>\n <div class=\"absolute h-[32px] w-[calc(100%-64px)] bg-[#e6e7eab8] bottom-0 left-[32px] z-[1]\">\n </div>\n <div #imageContainer\n class=\"relative w-full h-full rounded-bl-[8px] min-h-0\">\n <div #circleTL\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleTR\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleBL\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <div #circleBR\n class=\"absolute z-[5]\"\n [class.hidden]=\"!dragGridCrop()\"\n [class.flex]=\"dragGridCrop()\">\n <ng-template *ngTemplateOutlet=\"svgCircle\" />\n </div>\n <img #imageOrigin\n class=\"absolute border-[4px] border-[#8e61ee] rounded-[4px] w-0 h-0\"\n loading=\"lazy\"\n [src]=\"imgSrc()\"\n (load)=\"handlerImageLoaded()\"\n (error)=\"handlerImageError($event)\" />\n <img #imageClip\n class=\"absolute z-[1] w-0 h-0\"\n loading=\"lazy\"\n [src]=\"imgSrc()\"\n (error)=\"handlerImageError($event)\" />\n <div class=\"absolute top-0 left-0 right-0 bottom-0 bg-[#e6e7eab8]\">\n </div>\n <div #cropArea\n class=\"absolute z-[2] border-[4px] border-[#8e61ee] rounded-[4px]\">\n <div class=\"relative w-full h-full\">\n <div #cropTL\n class=\"absolute z-[2] left-[-18px] top-[-17px] libs-ui-image-editor-edit-cursor-nwse-resize\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropBL\n class=\"absolute z-[2] left-[-17px] bottom-[-18px] libs-ui-image-editor-edit-cursor-nesw-resize rotate-[270deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropTR\n class=\"absolute z-[2] right-[-17px] top-[-17px] libs-ui-image-editor-edit-cursor-nesw-resize rotate-[90deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropBR\n class=\"absolute z-[2] right-[-18px] bottom-[-17px] libs-ui-image-editor-edit-cursor-nwse-resize rotate-[180deg]\">\n <ng-template *ngTemplateOutlet=\"svgArrow\" />\n </div>\n <div #cropLineVL\n class=\"absolute z-[1] w-[12px] left-[-6px] top-0 bottom-0 libs-ui-image-editor-edit-cursor-ew-resize\">\n </div>\n <div #cropLineVR\n class=\"absolute z-[1] w-[12px] right-[-6px] top-0 bottom-0 libs-ui-image-editor-edit-cursor-ew-resize\">\n </div>\n <div #cropLineHT\n class=\"absolute z-[1] h-[12px] left-0 top-[-6px] right-0 libs-ui-image-editor-edit-cursor-ns-resize\">\n </div>\n <div #cropLineHB\n class=\"absolute z-[1] h-[12px] left-0 right-0 bottom-[-6px] libs-ui-image-editor-edit-cursor-ns-resize\">\n </div>\n <div class=\"absolute z-[1] h-[1px] top-0 left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] h-[1px] bottom-0 left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] w-[1px] top-0 left-0 bottom-0\">\n </div>\n <div class=\"absolute z-[1] w-[1px] top-0 right-0 bottom-0\">\n </div>\n <div class=\"absolute z-[1] h-[2px] top-[33px] left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] h-[2px] top-[66px] left-0 right-0\">\n </div>\n <div class=\"absolute z-[1] w-[2px] top-0 bottom-0 left-[33px]\">\n </div>\n <div class=\"absolute z-[1] w-[2px] top-0 bottom-0 left-[66px]\">\n </div>\n </div>\n </div>\n </div>\n @if (loading()) {\n <libs_ui-components-spinner [size]=\"'medium'\" />\n }\n </div>\n <div class=\"w-[320px] shrink-0 p-[16px] bg-[#ffffff] rounded-br-[8px] z-[2]\">\n <div class=\"flex items-center justify-between mb-[16px]\">\n <div class=\"libs-ui-font-h4m\">{{ 'i18n_cutting_ratio' | translate }}</div>\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [type]=\"'button-third'\"\n [classIconLeft]=\"'libs-ui-icon-refresh'\"\n [classInclude]=\"'p-[6px] mr-[8px]'\"\n [popover]=\"{config: {content: 'i18n_restore_the_original_state', zIndex: zIndex()}}\"\n [disable]=\"loading() || loadingImage() || !changed()\"\n (outClick)=\"handlerRestoreImage($event)\" />\n </div>\n <div class=\"mb-[16px] libs-ui-image-editor-edit-display-grid-gap-16\">\n @for (item of cropRatioItems(); track item.key) {\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage() || requiredCropFollowRatio()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"item.icon+' !text-[20px]'\"\n (outClick)=\"handlerSelectCropRatioItem($event, item.key)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ (item.key === 'free' ? 'i18n_custom' : item.key) | translate }}\n </div>\n </div>\n }\n </div>\n <div class=\"mb-[16px] flex\">\n <libs_ui-components-inputs-valid class=\"mr-[16px]\"\n [dataType]=\"'int'\"\n [labelConfig]=\"{labelLeft: 'i18n_width', classInclude: 'mb-[4px]', labelLeftClass: 'libs-ui-font-h7r'}\"\n [fieldNameBind]=\"'width'\"\n [(item)]=\"cropSize\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n [maxValueNumber]=\"originWidth()\"\n [disable]=\"loading() || loadingImage()\"\n (outValueChange)=\"handlerCropWidth()\" />\n <libs_ui-components-inputs-valid [dataType]=\"'int'\"\n [labelConfig]=\"{labelLeft: 'i18n_length', classInclude: 'mb-[4px]', labelLeftClass: 'libs-ui-font-h7r'}\"\n [fieldNameBind]=\"'height'\"\n [(item)]=\"cropSize\"\n [unitsRight]=\"[{id: 'px', label: 'px'}]\"\n [keySelectedUnitRight]=\"'px'\"\n [valueUpDownNumber]=\"1\"\n [maxValueNumber]=\"originHeight()\"\n [disable]=\"loading() || loadingImage()\"\n (outValueChange)=\"handlerCropHeight()\" />\n </div>\n <div class=\"libs-ui-font-h4m mb-[16px]\">{{ 'i18n_rotate_photos' | translate }}</div>\n <div class=\"flex libs-ui-image-editor-edit-display-grid-gap-16\">\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-rotate-image-outline !text-[20px]'\"\n (outClick)=\"handlerRotateImage($event)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_rotate_ninety_degrees' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-flip-vertical !text-[20px]'\"\n (outClick)=\"handlerFlipImage($event, 'vertical')\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_vertical_flip' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-flip-horizontal !text-[20px]'\"\n (outClick)=\"handlerFlipImage($event, 'horizontal')\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_horizontal_flip' | translate }}\n </div>\n </div>\n\n <div class=\"flex flex-col items-center\">\n <libs_ui-components-buttons-button [iconOnlyType]=\"true\"\n [sizeButton]=\"'large'\"\n [disable]=\"loading() || loadingImage()\"\n [type]=\"'button-outline'\"\n [classIconLeft]=\"'libs-ui-icon-scale !text-[20px]'\"\n (outClick)=\"handlerResize($event)\" />\n <div class=\"libs-ui-font-h6r text-[#6a7383] mt-[4px]\">\n {{ 'i18n_resize' | translate }}\n </div>\n </div>\n </div>\n </div>\n </div>\n</libs_ui-components-modal>\n\n<ng-template #svgArrow>\n <svg xmlns=\"http://www.w3.org/2000/svg\"\n width=\"40\"\n height=\"40\"\n viewBox=\"0 0 40 40\"\n fill=\"none\">\n <path d=\"M33.6987 14.6969L18.3309 14.6969C16.516 14.6969 15.0311 16.1818 15.0311 17.9967L15.0311 33.3645\"\n stroke=\"white\"\n stroke-width=\"5.83333\"\n stroke-miterlimit=\"10\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\" />\n </svg>\n</ng-template>\n\n<ng-template #svgCircle>\n <svg xmlns=\"http://www.w3.org/2000/svg\"\n width=\"15\"\n height=\"15\"\n viewBox=\"0 0 15 15\"\n fill=\"none\">\n <circle cx=\"7.5\"\n cy=\"7.5\"\n r=\"7.5\"\n fill=\"white\" />\n </svg>\n</ng-template>\n", styles: [".libs-ui-image-editor-edit-display-grid-gap-16{display:grid;gap:16px;grid-template-columns:1fr 1fr 1fr 1fr}.libs-ui-image-editor-edit-top-33{top:33.33%}.libs-ui-image-editor-edit-top-66{top:66.66%}.libs-ui-image-editor-edit-left-33{left:33.33%}.libs-ui-image-editor-edit-left-66{left:66.66%}.libs-ui-image-editor-edit-cursor-ew-resize{cursor:ew-resize!important}.libs-ui-image-editor-edit-cursor-ns-resize{cursor:ns-resize!important}.libs-ui-image-editor-edit-cursor-nwse-resize{cursor:nwse-resize!important}.libs-ui-image-editor-edit-cursor-nesw-resize{cursor:nesw-resize!important}.libs-ui-image-editor-edit-action-item{display:flex;flex-direction:column;align-items:center;width:60px;cursor:pointer}.libs-ui-image-editor-edit-action-item-icon{padding:12px;border-radius:8px;width:max-content;border:1px solid #e6e7ea}.libs-ui-image-editor-edit-action-item-icon:before{font-size:20px}.libs-ui-image-editor-edit-action-item:hover .libs-ui-image-editor-edit-action-item-icon{border:1px solid var(--mo-global-color-primary-border-hover)}.libs-ui-image-editor-edit-action-item:hover .libs-ui-image-editor-edit-action-item-icon:before{color:var(--mo-global-color-primary-text-bright-20)!important}.libs-ui-image-editor-edit-action-item[active=true] .libs-ui-image-editor-edit-action-item-icon{border:1px solid var(--mo-global-color-primary-border-active)!important;background-color:var(--mo-global-color-primary-background-95)!important}.libs-ui-image-editor-edit-action-item[active=true] .libs-ui-image-editor-edit-action-item-icon:before{color:var(--mo-global-color-primary-text-active)!important}\n"] }]
982
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
983
+ type: Optional
984
+ }, {
985
+ type: Inject,
986
+ args: [LINK_IMAGE_ERROR_TOKEN_INJECT]
987
+ }] }] });
988
+
989
+ /**
990
+ * Generated bundle index. Do not edit.
991
+ */
992
+
993
+ export { LibsUiComponentsImageEditorComponent, LibsUiComponentsImageEditorResizeComponent };
994
+ //# sourceMappingURL=libs-ui-components-image-editor.mjs.map