@acorex/components 22.0.0 → 22.0.1-next.0

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.
Files changed (149) hide show
  1. package/emoji/README.md +3 -0
  2. package/emojipicker/README.md +3 -0
  3. package/fesm2022/acorex-components-accordion.mjs +4 -4
  4. package/fesm2022/acorex-components-accordion.mjs.map +1 -1
  5. package/fesm2022/acorex-components-audio-wave.mjs +291 -267
  6. package/fesm2022/acorex-components-audio-wave.mjs.map +1 -1
  7. package/fesm2022/acorex-components-bubble.mjs +71 -0
  8. package/fesm2022/acorex-components-bubble.mjs.map +1 -0
  9. package/fesm2022/acorex-components-button-group.mjs +12 -5
  10. package/fesm2022/acorex-components-button-group.mjs.map +1 -1
  11. package/fesm2022/acorex-components-button.mjs +2 -2
  12. package/fesm2022/acorex-components-button.mjs.map +1 -1
  13. package/fesm2022/acorex-components-calendar.mjs +321 -118
  14. package/fesm2022/acorex-components-calendar.mjs.map +1 -1
  15. package/fesm2022/acorex-components-combo-box.mjs +238 -48
  16. package/fesm2022/acorex-components-combo-box.mjs.map +1 -1
  17. package/fesm2022/acorex-components-conversation-acorex-components-conversation-DDkiZfPb.mjs +19058 -0
  18. package/fesm2022/acorex-components-conversation-acorex-components-conversation-DDkiZfPb.mjs.map +1 -0
  19. package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-COUB9zrM.mjs +50 -0
  20. package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-COUB9zrM.mjs.map +1 -0
  21. package/fesm2022/acorex-components-conversation-image-picker-editor-dialog.component-COT96F_v.mjs +87 -0
  22. package/fesm2022/acorex-components-conversation-image-picker-editor-dialog.component-COT96F_v.mjs.map +1 -0
  23. package/fesm2022/acorex-components-conversation-picker-rename-file-dialog.component-CH3Q6Hry.mjs +65 -0
  24. package/fesm2022/acorex-components-conversation-picker-rename-file-dialog.component-CH3Q6Hry.mjs.map +1 -0
  25. package/fesm2022/acorex-components-conversation.mjs +1 -15470
  26. package/fesm2022/acorex-components-conversation.mjs.map +1 -1
  27. package/fesm2022/acorex-components-cron-job.mjs +1 -1
  28. package/fesm2022/acorex-components-cron-job.mjs.map +1 -1
  29. package/fesm2022/acorex-components-data-pager.mjs +4 -4
  30. package/fesm2022/acorex-components-data-pager.mjs.map +1 -1
  31. package/fesm2022/acorex-components-data-table.mjs +126 -30
  32. package/fesm2022/acorex-components-data-table.mjs.map +1 -1
  33. package/fesm2022/acorex-components-datetime-picker.mjs +1 -1
  34. package/fesm2022/acorex-components-datetime-picker.mjs.map +1 -1
  35. package/fesm2022/acorex-components-decorators.mjs +36 -6
  36. package/fesm2022/acorex-components-decorators.mjs.map +1 -1
  37. package/fesm2022/acorex-components-dialog.mjs +1 -1
  38. package/fesm2022/acorex-components-dialog.mjs.map +1 -1
  39. package/fesm2022/acorex-components-dropdown-button.mjs +1 -1
  40. package/fesm2022/acorex-components-dropdown-button.mjs.map +1 -1
  41. package/fesm2022/acorex-components-emoji.mjs +74 -0
  42. package/fesm2022/acorex-components-emoji.mjs.map +1 -0
  43. package/fesm2022/acorex-components-emojipicker.mjs +425 -0
  44. package/fesm2022/acorex-components-emojipicker.mjs.map +1 -0
  45. package/fesm2022/acorex-components-fab.mjs +2 -2
  46. package/fesm2022/acorex-components-fab.mjs.map +1 -1
  47. package/fesm2022/acorex-components-file-viewer.mjs +339 -89
  48. package/fesm2022/acorex-components-file-viewer.mjs.map +1 -1
  49. package/fesm2022/acorex-components-form.mjs +1 -1
  50. package/fesm2022/acorex-components-form.mjs.map +1 -1
  51. package/fesm2022/acorex-components-highlight.mjs +80 -0
  52. package/fesm2022/acorex-components-highlight.mjs.map +1 -0
  53. package/fesm2022/acorex-components-image-editor.mjs +686 -416
  54. package/fesm2022/acorex-components-image-editor.mjs.map +1 -1
  55. package/fesm2022/acorex-components-inline-reaction.mjs +111 -0
  56. package/fesm2022/acorex-components-inline-reaction.mjs.map +1 -0
  57. package/fesm2022/acorex-components-kanban.mjs +1 -1
  58. package/fesm2022/acorex-components-kanban.mjs.map +1 -1
  59. package/fesm2022/acorex-components-loading-dialog.mjs +1 -1
  60. package/fesm2022/acorex-components-loading-dialog.mjs.map +1 -1
  61. package/fesm2022/acorex-components-lookup.mjs +742 -134
  62. package/fesm2022/acorex-components-lookup.mjs.map +1 -1
  63. package/fesm2022/acorex-components-map.mjs +7 -0
  64. package/fesm2022/acorex-components-map.mjs.map +1 -1
  65. package/fesm2022/acorex-components-menu.mjs +96 -16
  66. package/fesm2022/acorex-components-menu.mjs.map +1 -1
  67. package/fesm2022/acorex-components-message-status.mjs +71 -0
  68. package/fesm2022/acorex-components-message-status.mjs.map +1 -0
  69. package/fesm2022/acorex-components-message-time.mjs +36 -0
  70. package/fesm2022/acorex-components-message-time.mjs.map +1 -0
  71. package/fesm2022/acorex-components-message.mjs +120 -0
  72. package/fesm2022/acorex-components-message.mjs.map +1 -0
  73. package/fesm2022/acorex-components-notification.mjs +1 -1
  74. package/fesm2022/acorex-components-notification.mjs.map +1 -1
  75. package/fesm2022/acorex-components-paint.mjs +1 -1
  76. package/fesm2022/acorex-components-paint.mjs.map +1 -1
  77. package/fesm2022/acorex-components-popover.mjs +19 -8
  78. package/fesm2022/acorex-components-popover.mjs.map +1 -1
  79. package/fesm2022/acorex-components-popup.mjs +1 -1
  80. package/fesm2022/acorex-components-popup.mjs.map +1 -1
  81. package/fesm2022/acorex-components-rail-navigation.mjs +2 -2
  82. package/fesm2022/acorex-components-rail-navigation.mjs.map +1 -1
  83. package/fesm2022/acorex-components-range-slider.mjs +2 -2
  84. package/fesm2022/acorex-components-range-slider.mjs.map +1 -1
  85. package/fesm2022/acorex-components-reaction.mjs +144 -0
  86. package/fesm2022/acorex-components-reaction.mjs.map +1 -0
  87. package/fesm2022/acorex-components-rest-api-generator.mjs +5 -5
  88. package/fesm2022/acorex-components-rest-api-generator.mjs.map +1 -1
  89. package/fesm2022/acorex-components-rrule.mjs +1 -1
  90. package/fesm2022/acorex-components-rrule.mjs.map +1 -1
  91. package/fesm2022/acorex-components-scheduler-picker.mjs +5 -5
  92. package/fesm2022/acorex-components-scheduler-picker.mjs.map +1 -1
  93. package/fesm2022/acorex-components-scheduler.mjs +1 -1
  94. package/fesm2022/acorex-components-scheduler.mjs.map +1 -1
  95. package/fesm2022/acorex-components-selection-list.mjs +1 -2
  96. package/fesm2022/acorex-components-selection-list.mjs.map +1 -1
  97. package/fesm2022/acorex-components-tabs.mjs +510 -74
  98. package/fesm2022/acorex-components-tabs.mjs.map +1 -1
  99. package/fesm2022/acorex-components-tree-view.mjs +49 -6
  100. package/fesm2022/acorex-components-tree-view.mjs.map +1 -1
  101. package/fesm2022/acorex-components-wysiwyg.mjs +76 -6
  102. package/fesm2022/acorex-components-wysiwyg.mjs.map +1 -1
  103. package/highlight/README.md +13 -0
  104. package/inline-reaction/README.md +3 -0
  105. package/lookup/README.md +1 -1
  106. package/message/README.md +3 -0
  107. package/message-status/README.md +3 -0
  108. package/message-time/README.md +3 -0
  109. package/package.json +42 -12
  110. package/reaction/README.md +3 -0
  111. package/types/acorex-components-audio-wave.d.ts +132 -73
  112. package/types/acorex-components-bubble.d.ts +31 -0
  113. package/types/acorex-components-button-group.d.ts +4 -0
  114. package/types/acorex-components-button.d.ts +1 -1
  115. package/types/acorex-components-calendar.d.ts +41 -9
  116. package/types/acorex-components-combo-box.d.ts +34 -4
  117. package/types/acorex-components-conversation.d.ts +1745 -1322
  118. package/types/acorex-components-data-table.d.ts +27 -7
  119. package/types/acorex-components-decorators.d.ts +13 -1
  120. package/types/acorex-components-emoji.d.ts +42 -0
  121. package/types/acorex-components-emojipicker.d.ts +146 -0
  122. package/types/acorex-components-file-viewer.d.ts +48 -4
  123. package/types/acorex-components-highlight.d.ts +31 -0
  124. package/types/acorex-components-image-editor.d.ts +68 -48
  125. package/types/acorex-components-inline-reaction.d.ts +52 -0
  126. package/types/acorex-components-lookup.d.ts +213 -82
  127. package/types/acorex-components-map.d.ts +5 -0
  128. package/types/acorex-components-menu.d.ts +15 -3
  129. package/types/acorex-components-message-status.d.ts +23 -0
  130. package/types/acorex-components-message-time.d.ts +19 -0
  131. package/types/acorex-components-message.d.ts +59 -0
  132. package/types/acorex-components-popover.d.ts +5 -3
  133. package/types/acorex-components-reaction.d.ts +85 -0
  134. package/types/acorex-components-selection-list.d.ts +1 -1
  135. package/types/acorex-components-tabs.d.ts +148 -19
  136. package/types/acorex-components-tree-view.d.ts +31 -2
  137. package/types/acorex-components-wysiwyg.d.ts +13 -0
  138. package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-DCcRJYfF.mjs +0 -50
  139. package/fesm2022/acorex-components-conversation-composer-action-choice-dialog.component-DCcRJYfF.mjs.map +0 -1
  140. package/fesm2022/acorex-components-conversation-open-composer-picker-for-files-DnSW908R.mjs +0 -210
  141. package/fesm2022/acorex-components-conversation-open-composer-picker-for-files-DnSW908R.mjs.map +0 -1
  142. package/fesm2022/acorex-components-flow-chart.mjs +0 -735
  143. package/fesm2022/acorex-components-flow-chart.mjs.map +0 -1
  144. package/fesm2022/acorex-components-query-builder.mjs +0 -267
  145. package/fesm2022/acorex-components-query-builder.mjs.map +0 -1
  146. package/flow-chart/README.md +0 -3
  147. package/query-builder/README.md +0 -3
  148. package/types/acorex-components-flow-chart.d.ts +0 -187
  149. package/types/acorex-components-query-builder.d.ts +0 -52
@@ -1,24 +1,21 @@
1
1
  import { AXComponent, MXBaseComponent, MXInputBaseValueComponent, MXLookComponent, AXFocusableComponent, AXValuableComponent } from '@acorex/cdk/common';
2
2
  import * as i1 from '@acorex/components/decorators';
3
3
  import { AXDecoratorModule, AXDecoratorIconComponent } from '@acorex/components/decorators';
4
- import { AXLoadingSpinnerComponent, AXLoadingModule } from '@acorex/components/loading';
4
+ import { AXLoadingSpinnerComponent } from '@acorex/components/loading';
5
+ import { AXTranslatorPipe } from '@acorex/core/translation';
6
+ import { isPlatformBrowser, AsyncPipe } from '@angular/common';
5
7
  import * as i0 from '@angular/core';
6
- import { signal, Injectable, input, inject, NgZone, ElementRef, viewChild, Renderer2, PLATFORM_ID, effect, afterNextRender, ViewEncapsulation, Component, linkedSignal, contentChild, forwardRef, HostBinding, output, computed, NgModule } from '@angular/core';
8
+ import { signal, Injectable, input, inject, NgZone, ElementRef, viewChild, Renderer2, PLATFORM_ID, effect, untracked, afterNextRender, ChangeDetectionStrategy, ViewEncapsulation, Component, DestroyRef, contentChild, forwardRef, output, computed, NgModule } from '@angular/core';
7
9
  import * as i1$1 from '@angular/forms';
8
10
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
9
11
  import { classes } from 'polytype';
10
- import { isPlatformBrowser, AsyncPipe } from '@angular/common';
11
- import { AXButtonComponent, AXButtonModule } from '@acorex/components/button';
12
+ import { AXButtonComponent } from '@acorex/components/button';
12
13
  import * as i2 from '@acorex/components/tooltip';
13
14
  import { AXTooltipModule, AXTooltipDirective } from '@acorex/components/tooltip';
14
- import { AXTranslatorPipe } from '@acorex/core/translation';
15
15
  import * as i2$1 from '@acorex/components/toolbar';
16
16
  import { AXToolBarModule } from '@acorex/components/toolbar';
17
- import { AXColorBoxComponent, AXColorBoxModule } from '@acorex/components/color-box';
18
- import { AXRangeSliderComponent, AXRangeSliderModule } from '@acorex/components/range-slider';
19
- import { AXDialogModule } from '@acorex/components/dialog';
20
- import { AXPopoverModule } from '@acorex/components/popover';
21
- import { AXSelectBoxModule } from '@acorex/components/select-box';
17
+ import { AXColorBoxComponent } from '@acorex/components/color-box';
18
+ import { AXRangeSliderComponent } from '@acorex/components/range-slider';
22
19
 
23
20
  class AXImageEditorService {
24
21
  constructor() {
@@ -38,6 +35,8 @@ class AXImageEditorService {
38
35
  ...(ngDevMode ? [{ debugName: "rotate" }] : /* istanbul ignore next */ []));
39
36
  this.isImageLoad = signal(false, /* @ts-ignore */
40
37
  ...(ngDevMode ? [{ debugName: "isImageLoad" }] : /* istanbul ignore next */ []));
38
+ this.loadError = signal(false, /* @ts-ignore */
39
+ ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
41
40
  this.newImage = null;
42
41
  this.initialImage = null;
43
42
  this.imageBlob = signal([], /* @ts-ignore */
@@ -46,6 +45,10 @@ class AXImageEditorService {
46
45
  ...(ngDevMode ? [{ debugName: "undoPressed" }] : /* istanbul ignore next */ []));
47
46
  this.redoPressed = signal(false, /* @ts-ignore */
48
47
  ...(ngDevMode ? [{ debugName: "redoPressed" }] : /* istanbul ignore next */ []));
48
+ this.canUndo = signal(false, /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
50
+ this.canRedo = signal(false, /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
49
52
  this.cropperRatio = signal(null, /* @ts-ignore */
50
53
  ...(ngDevMode ? [{ debugName: "cropperRatio" }] : /* istanbul ignore next */ []));
51
54
  this.cropperSize = signal(150, /* @ts-ignore */
@@ -54,6 +57,7 @@ class AXImageEditorService {
54
57
  toggleTool(tool) {
55
58
  if (this.activeToolState() === tool) {
56
59
  this.activeToolState.set(null);
60
+ this.penType.set(null);
57
61
  return;
58
62
  }
59
63
  this.activateTool(tool);
@@ -62,7 +66,49 @@ class AXImageEditorService {
62
66
  this.activeToolState.set(tool);
63
67
  if (tool === 'pen' || tool === 'highlight') {
64
68
  this.penType.set(tool);
69
+ return;
70
+ }
71
+ this.penType.set(null);
72
+ }
73
+ undo() {
74
+ if (!this.canUndo()) {
75
+ return;
76
+ }
77
+ this.undoPressed.set(true);
78
+ }
79
+ redo() {
80
+ if (!this.canRedo()) {
81
+ return;
82
+ }
83
+ this.redoPressed.set(true);
84
+ }
85
+ /** Handles Ctrl/Cmd+Z and Ctrl/Cmd+Y history shortcuts. Returns true when handled. */
86
+ handleHistoryShortcut(event) {
87
+ const modifier = event.ctrlKey || event.metaKey;
88
+ if (!modifier) {
89
+ return false;
65
90
  }
91
+ const isUndo = event.code === 'KeyZ' && !event.shiftKey;
92
+ const isRedo = event.code === 'KeyY' || (event.code === 'KeyZ' && event.shiftKey);
93
+ if (isUndo) {
94
+ if (!this.canUndo()) {
95
+ return false;
96
+ }
97
+ event.preventDefault();
98
+ event.stopPropagation();
99
+ this.undo();
100
+ return true;
101
+ }
102
+ if (isRedo) {
103
+ if (!this.canRedo()) {
104
+ return false;
105
+ }
106
+ event.preventDefault();
107
+ event.stopPropagation();
108
+ this.redo();
109
+ return true;
110
+ }
111
+ return false;
66
112
  }
67
113
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
68
114
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorService }); }
@@ -91,63 +137,42 @@ class AXImageEditorCropperWindowComponent {
91
137
  this.elementRef = inject(ElementRef);
92
138
  this.cropperWindow = viewChild('r', /* @ts-ignore */
93
139
  ...(ngDevMode ? [{ debugName: "cropperWindow" }] : /* istanbul ignore next */ []));
94
- this.currentX = signal(-45, /* @ts-ignore */
140
+ this.currentX = signal(0, /* @ts-ignore */
95
141
  ...(ngDevMode ? [{ debugName: "currentX" }] : /* istanbul ignore next */ []));
96
- this.currentY = signal(-45, /* @ts-ignore */
142
+ this.currentY = signal(0, /* @ts-ignore */
97
143
  ...(ngDevMode ? [{ debugName: "currentY" }] : /* istanbul ignore next */ []));
98
- this.prevX = signal(0, /* @ts-ignore */
99
- ...(ngDevMode ? [{ debugName: "prevX" }] : /* istanbul ignore next */ []));
100
- this.prevY = signal(0, /* @ts-ignore */
101
- ...(ngDevMode ? [{ debugName: "prevY" }] : /* istanbul ignore next */ []));
102
- this.aspectWidth = signal(0, /* @ts-ignore */
144
+ this.aspectWidth = signal(1, /* @ts-ignore */
103
145
  ...(ngDevMode ? [{ debugName: "aspectWidth" }] : /* istanbul ignore next */ []));
104
- this.aspectHeight = signal(0, /* @ts-ignore */
146
+ this.aspectHeight = signal(1, /* @ts-ignore */
105
147
  ...(ngDevMode ? [{ debugName: "aspectHeight" }] : /* istanbul ignore next */ []));
106
148
  this.isMousePressed = signal(false, /* @ts-ignore */
107
149
  ...(ngDevMode ? [{ debugName: "isMousePressed" }] : /* istanbul ignore next */ []));
108
150
  this.renderer = inject(Renderer2);
109
151
  this.platformID = inject(PLATFORM_ID);
110
- this.#eff = effect(() => {
152
+ this.positioned = false;
153
+ this.#sizeEffect = effect(() => {
111
154
  const host = this.elementRef.nativeElement;
112
- let width;
113
- let height;
114
155
  const scale = this.size() ?? this.service.cropperSize();
115
- switch (this.service.cropperRatio()) {
116
- case '1:1':
117
- width = 1;
118
- height = 1;
119
- break;
120
- case '16:9':
121
- width = 1.777777777777778;
122
- height = 1;
123
- break;
124
- case null:
125
- width = 1;
126
- height = 1;
127
- break;
128
- default:
129
- [width, height] = this.service.cropperRatio().split(':').map(Number);
130
- }
131
- if (width >= height) {
132
- const aspect = width / height;
133
- width = aspect;
134
- height = 1;
135
- this.aspectWidth.set(aspect);
136
- this.aspectHeight.set(1);
137
- }
138
- else {
139
- const aspect = height / width;
140
- width = 1;
141
- height = aspect;
142
- this.aspectWidth.set(1);
143
- this.aspectHeight.set(aspect);
156
+ const ratio = this.service.cropperRatio();
157
+ const { width, height } = this.normalizeRatio(ratio);
158
+ this.aspectWidth.set(width);
159
+ this.aspectHeight.set(height);
160
+ const parent = host.parentElement;
161
+ const maxW = parent?.clientWidth || scale * width;
162
+ const maxH = parent?.clientHeight || scale * height;
163
+ const nextWidth = Math.min(width * scale, maxW * 0.9);
164
+ const nextHeight = (nextWidth * height) / width;
165
+ host.style.width = `${Math.max(20, nextWidth)}px`;
166
+ host.style.height = `${Math.max(20, Math.min(nextHeight, maxH * 0.9))}px`;
167
+ if (this.positioned) {
168
+ untracked(() => this.clampToParent());
144
169
  }
145
- host.style.width = `${width * scale}px`;
146
- host.style.height = `${height * scale}px`;
147
170
  }, /* @ts-ignore */
148
- ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
171
+ ...(ngDevMode ? [{ debugName: "#sizeEffect" }] : /* istanbul ignore next */ []));
149
172
  this.#init = afterNextRender(() => {
150
173
  const host = this.elementRef.nativeElement;
174
+ this.centerOnParent();
175
+ this.positioned = true;
151
176
  this.ngZone.runOutsideAngular(() => {
152
177
  this.mouseMoveEvent = this.renderer.listen(document, 'pointermove', this.changeSizeHandler.bind(this));
153
178
  this.mouseUpEvent = this.renderer.listen(document, 'pointerup', this.clearSelector.bind(this));
@@ -156,34 +181,26 @@ class AXImageEditorCropperWindowComponent {
156
181
  this.mouseUpEvent2 = this.renderer.listen(host, 'pointerup', this.panUp.bind(this));
157
182
  });
158
183
  });
159
- this.#positionChange = effect(() => {
160
- this.setPosition(this.currentX(), this.currentY());
161
- }, /* @ts-ignore */
162
- ...(ngDevMode ? [{ debugName: "#positionChange" }] : /* istanbul ignore next */ []));
184
+ this.startX = 0;
185
+ this.startY = 0;
186
+ this.startWidth = 0;
187
+ this.startHeight = 0;
188
+ this.startLeft = 0;
189
+ this.startTop = 0;
163
190
  }
164
- #eff;
191
+ #sizeEffect;
165
192
  #init;
166
193
  ngOnDestroy() {
167
- if (isPlatformBrowser(this.platformID) && this.mouseMoveEvent) {
168
- this.mouseMoveEvent();
169
- }
170
- if (isPlatformBrowser(this.platformID) && this.mouseUpEvent) {
171
- this.mouseUpEvent();
172
- }
173
- if (isPlatformBrowser(this.platformID) && this.mouseDownEvent) {
174
- this.mouseDownEvent();
175
- }
176
- if (isPlatformBrowser(this.platformID) && this.mouseMoveEvent2) {
177
- this.mouseMoveEvent2();
178
- }
179
- if (isPlatformBrowser(this.platformID) && this.mouseUpEvent2) {
180
- this.mouseUpEvent2();
181
- }
182
- if (isPlatformBrowser(this.platformID) && this.mouseLeaveEvent) {
183
- this.mouseLeaveEvent();
194
+ if (!isPlatformBrowser(this.platformID)) {
195
+ return;
184
196
  }
197
+ this.mouseMoveEvent?.();
198
+ this.mouseUpEvent?.();
199
+ this.mouseDownEvent?.();
200
+ this.mouseMoveEvent2?.();
201
+ this.mouseUpEvent2?.();
202
+ this.mouseLeaveEvent?.();
185
203
  }
186
- #positionChange;
187
204
  panMove(e) {
188
205
  if (!this.isMousePressed())
189
206
  return;
@@ -191,39 +208,27 @@ class AXImageEditorCropperWindowComponent {
191
208
  return;
192
209
  if (!this.panState())
193
210
  return;
194
- const currentX = e.clientX;
195
- const currentY = e.clientY;
196
- const deltaX = currentX - this.prevX();
197
- const deltaY = currentY - this.prevY();
198
- this.currentX.update((prev) => prev + deltaX);
199
- this.currentY.update((prev) => prev + deltaY);
200
- this.prevX.set(currentX);
201
- this.prevY.set(currentY);
211
+ this.currentX.update((prev) => prev + e.movementX);
212
+ this.currentY.update((prev) => prev + e.movementY);
213
+ this.clampToParent();
202
214
  }
203
215
  panDown(e) {
204
- this.isMousePressed.set(true);
205
216
  if (this.selectedHandle())
206
217
  return;
218
+ this.isMousePressed.set(true);
207
219
  this.panState.set(true);
208
- this.prevX.set(e.clientX);
209
- this.prevY.set(e.clientY);
220
+ e.currentTarget?.setPointerCapture?.(e.pointerId);
210
221
  }
211
222
  panUp() {
212
223
  this.isMousePressed.set(false);
213
- if (this.selectedHandle())
214
- return;
215
224
  this.panState.set(false);
216
225
  }
217
226
  getCropperArea() {
218
227
  return this.elementRef.nativeElement.getBoundingClientRect();
219
228
  }
220
- setPosition(posX, posY) {
221
- const el = this.elementRef.nativeElement;
222
- el.style.transform = `translate(${posX}px, ${posY}px)`;
223
- }
224
229
  handleSelector(e, value) {
225
- if (this.panState())
226
- return;
230
+ e.stopPropagation();
231
+ e.preventDefault();
227
232
  const host = this.elementRef.nativeElement;
228
233
  this.startX = e.clientX;
229
234
  this.startY = e.clientY;
@@ -232,6 +237,8 @@ class AXImageEditorCropperWindowComponent {
232
237
  this.startLeft = host.offsetLeft;
233
238
  this.startTop = host.offsetTop;
234
239
  this.selectedHandle.set(value);
240
+ this.isMousePressed.set(true);
241
+ e.currentTarget?.setPointerCapture?.(e.pointerId);
235
242
  }
236
243
  changeSizeHandler(e) {
237
244
  if (!this.isMousePressed())
@@ -243,164 +250,176 @@ class AXImageEditorCropperWindowComponent {
243
250
  const host = this.elementRef.nativeElement;
244
251
  const dy = e.clientY - this.startY;
245
252
  const dx = e.clientX - this.startX;
246
- let newWidth;
247
- let newHeight;
248
253
  const minSize = 20;
254
+ const locked = !!this.service.cropperRatio();
255
+ const aspect = this.aspectWidth() / this.aspectHeight();
256
+ let width = this.startWidth;
257
+ let height = this.startHeight;
258
+ let left = this.startLeft;
259
+ let top = this.startTop;
249
260
  switch (this.selectedHandle()) {
250
261
  case 'right':
251
- newWidth = Math.max(minSize, this.startWidth + dx);
252
- host.style.width = `${newWidth}px`;
262
+ width = Math.max(minSize, this.startWidth + dx);
253
263
  break;
254
264
  case 'top':
255
- newHeight = Math.max(minSize, this.startHeight - dy);
256
- host.style.height = `${newHeight}px`;
257
- host.style.top = `${this.startTop + (this.startHeight - newHeight)}px`;
265
+ height = Math.max(minSize, this.startHeight - dy);
266
+ top = this.startTop + (this.startHeight - height);
258
267
  break;
259
268
  case 'left':
260
- newWidth = Math.max(minSize, this.startWidth - dx);
261
- host.style.width = `${newWidth}px`;
262
- host.style.left = `${this.startLeft + (this.startWidth - newWidth)}px`;
269
+ width = Math.max(minSize, this.startWidth - dx);
270
+ left = this.startLeft + (this.startWidth - width);
263
271
  break;
264
272
  case 'bottom':
265
- newHeight = Math.max(minSize, this.startHeight + dy);
266
- host.style.height = `${newHeight}px`;
273
+ height = Math.max(minSize, this.startHeight + dy);
267
274
  break;
268
275
  case 'right-top':
269
- if (this.service.cropperRatio()) {
270
- newWidth = Math.max(minSize, this.startWidth + dx);
271
- newHeight = (newWidth * this.aspectHeight()) / this.aspectWidth();
272
- if (newHeight < minSize) {
273
- newHeight = minSize;
274
- newWidth = (newHeight * this.aspectWidth()) / this.aspectHeight();
275
- }
276
- host.style.width = `${newWidth}px`;
277
- host.style.height = `${newHeight}px`;
278
- host.style.top = `${this.startTop + (this.startHeight - newHeight)}px`;
279
- }
280
- else {
281
- newWidth = Math.max(minSize, this.startWidth + dx);
282
- newHeight = Math.max(minSize, this.startHeight - dy);
283
- host.style.width = `${newWidth}px`;
284
- host.style.height = `${newHeight}px`;
285
- host.style.top = `${this.startTop + (this.startHeight - newHeight)}px`;
276
+ width = Math.max(minSize, this.startWidth + dx);
277
+ height = locked ? width / aspect : Math.max(minSize, this.startHeight - dy);
278
+ if (locked && height < minSize) {
279
+ height = minSize;
280
+ width = height * aspect;
286
281
  }
282
+ top = this.startTop + (this.startHeight - height);
287
283
  break;
288
284
  case 'bottom-right':
289
- if (this.service.cropperRatio()) {
290
- newWidth = Math.max(minSize, this.startWidth + dx);
291
- newHeight = (newWidth * this.aspectHeight()) / this.aspectWidth();
292
- if (newHeight < minSize) {
293
- newHeight = minSize;
294
- newWidth = (newHeight * this.aspectWidth()) / this.aspectHeight();
295
- }
296
- host.style.width = `${newWidth}px`;
297
- host.style.height = `${newHeight}px`;
298
- }
299
- else {
300
- newWidth = Math.max(minSize, this.startWidth + dx);
301
- newHeight = Math.max(minSize, this.startHeight + dy);
302
- host.style.width = `${newWidth}px`;
303
- host.style.height = `${newHeight}px`;
285
+ width = Math.max(minSize, this.startWidth + dx);
286
+ height = locked ? width / aspect : Math.max(minSize, this.startHeight + dy);
287
+ if (locked && height < minSize) {
288
+ height = minSize;
289
+ width = height * aspect;
304
290
  }
305
291
  break;
306
292
  case 'bottom-left':
307
- if (this.service.cropperRatio()) {
308
- newWidth = Math.max(minSize, this.startWidth - dx);
309
- newHeight = (newWidth * this.aspectHeight()) / this.aspectWidth();
310
- if (newHeight < minSize) {
311
- newHeight = minSize;
312
- newWidth = (newHeight * this.aspectWidth()) / this.aspectHeight();
313
- }
314
- host.style.width = `${newWidth}px`;
315
- host.style.height = `${newHeight}px`;
316
- host.style.left = `${this.startLeft + (this.startWidth - newWidth)}px`;
317
- }
318
- else {
319
- newWidth = Math.max(minSize, this.startWidth - dx);
320
- newHeight = Math.max(minSize, this.startHeight + dy);
321
- host.style.width = `${newWidth}px`;
322
- host.style.height = `${newHeight}px`;
323
- host.style.left = `${this.startLeft + (this.startWidth - newWidth)}px`;
293
+ width = Math.max(minSize, this.startWidth - dx);
294
+ height = locked ? width / aspect : Math.max(minSize, this.startHeight + dy);
295
+ if (locked && height < minSize) {
296
+ height = minSize;
297
+ width = height * aspect;
324
298
  }
299
+ left = this.startLeft + (this.startWidth - width);
325
300
  break;
326
301
  case 'left-top':
327
- if (this.service.cropperRatio()) {
328
- newWidth = Math.max(minSize, this.startWidth - dx);
329
- newHeight = (newWidth * this.aspectHeight()) / this.aspectWidth();
330
- if (newHeight < minSize) {
331
- newHeight = minSize;
332
- newWidth = (newHeight * this.aspectWidth()) / this.aspectHeight();
333
- }
334
- host.style.width = `${newWidth}px`;
335
- host.style.height = `${newHeight}px`;
336
- host.style.left = `${this.startLeft + (this.startWidth - newWidth)}px`;
337
- host.style.top = `${this.startTop + (this.startHeight - newHeight)}px`;
338
- }
339
- else {
340
- newWidth = Math.max(minSize, this.startWidth - dx);
341
- newHeight = Math.max(minSize, this.startHeight - dy);
342
- host.style.width = `${newWidth}px`;
343
- host.style.height = `${newHeight}px`;
344
- host.style.left = `${this.startLeft + (this.startWidth - newWidth)}px`;
345
- host.style.top = `${this.startTop + (this.startHeight - newHeight)}px`;
302
+ width = Math.max(minSize, this.startWidth - dx);
303
+ height = locked ? width / aspect : Math.max(minSize, this.startHeight - dy);
304
+ if (locked && height < minSize) {
305
+ height = minSize;
306
+ width = height * aspect;
346
307
  }
308
+ left = this.startLeft + (this.startWidth - width);
309
+ top = this.startTop + (this.startHeight - height);
347
310
  break;
348
311
  }
312
+ host.style.width = `${width}px`;
313
+ host.style.height = `${height}px`;
314
+ this.currentX.set(left);
315
+ this.currentY.set(top);
316
+ this.clampToParent();
349
317
  }
350
318
  clearSelector() {
351
- if (this.panState())
352
- return;
353
319
  this.selectedHandle.set(null);
320
+ this.isMousePressed.set(false);
321
+ this.panState.set(false);
322
+ }
323
+ normalizeRatio(ratio) {
324
+ let width = 1;
325
+ let height = 1;
326
+ if (ratio && ratio.includes(':')) {
327
+ const [rawW, rawH] = ratio.split(':').map(Number);
328
+ if (rawW > 0 && rawH > 0) {
329
+ width = rawW;
330
+ height = rawH;
331
+ }
332
+ }
333
+ if (width >= height) {
334
+ return { width: width / height, height: 1 };
335
+ }
336
+ return { width: 1, height: height / width };
337
+ }
338
+ centerOnParent() {
339
+ const host = this.elementRef.nativeElement;
340
+ const parent = host.parentElement;
341
+ if (!parent) {
342
+ return;
343
+ }
344
+ const left = Math.max(0, (parent.clientWidth - host.offsetWidth) / 2);
345
+ const top = Math.max(0, (parent.clientHeight - host.offsetHeight) / 2);
346
+ this.currentX.set(left);
347
+ this.currentY.set(top);
348
+ this.applyPosition();
349
+ }
350
+ clampToParent() {
351
+ const host = this.elementRef.nativeElement;
352
+ const parent = host.parentElement;
353
+ if (!parent) {
354
+ this.applyPosition();
355
+ return;
356
+ }
357
+ const maxX = Math.max(0, parent.clientWidth - host.offsetWidth);
358
+ const maxY = Math.max(0, parent.clientHeight - host.offsetHeight);
359
+ this.currentX.set(Math.min(Math.max(0, this.currentX()), maxX));
360
+ this.currentY.set(Math.min(Math.max(0, this.currentY()), maxY));
361
+ this.applyPosition();
362
+ }
363
+ applyPosition() {
364
+ const host = this.elementRef.nativeElement;
365
+ host.style.left = `${this.currentX()}px`;
366
+ host.style.top = `${this.currentY()}px`;
354
367
  }
355
368
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropperWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
356
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorCropperWindowComponent, isStandalone: true, selector: "ax-image-editor-cropper-window", inputs: { showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropperWindowComponent }], viewQueries: [{ propertyName: "cropperWindow", first: true, predicate: ["r"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [style.border]=\"!showGrid() ? '1px dashed white' : null\" #r class=\"ax-image-cropper-window-container\">\n @if (!service.cropperRatio()) {\n <div (pointerdown)=\"handleSelector($event, 'right')\" class=\"ax-image-right-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'top')\" class=\"ax-image-top-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left')\" class=\"ax-image-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom')\" class=\"ax-image-bottom-handle ax-handle-shape\"></div>\n }\n\n <div (pointerdown)=\"handleSelector($event, 'right-top')\" class=\"ax-image-right-top-handle ax-handle-shape\"></div>\n <div\n (pointerdown)=\"handleSelector($event, 'bottom-right')\"\n class=\"ax-image-bottom-right-handle ax-handle-shape\"\n ></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom-left')\" class=\"ax-image-bottom-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left-top')\" class=\"ax-image-left-top-handle ax-handle-shape\"></div>\n\n @if (showGrid()) {\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-image-editor-cropper-window{position:absolute;top:45%;left:45%;touch-action:none;-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-cropper-window-container{position:relative;display:grid;height:100%;cursor:move;grid-template-columns:repeat(3,minmax(0,1fr));outline-style:var(--tw-outline-style);outline-width:100vmax;outline-color:color-mix(in srgb,#000 70%,transparent);--tw-outline-style: solid;outline-style:solid}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container{outline-color:color-mix(in oklab,var(--color-black, #000) 70%,transparent)}}ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:color-mix(in srgb,#fff 50%,transparent)}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-color:color-mix(in oklab,var(--color-white, #fff) 50%,transparent)}}ax-image-editor-cropper-window .ax-handle-shape{position:absolute;height:calc(var(--spacing, .25rem) * 4);width:calc(var(--spacing, .25rem) * 4);border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-white, #fff);background-color:var(--color-gray-500, oklch(55.1% .027 264.364));-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-right-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(var(--spacing, .25rem) * -2);cursor:ne-resize}ax-image-editor-cropper-window .ax-image-bottom-right-handle{right:calc(var(--spacing, .25rem) * -2);bottom:calc(var(--spacing, .25rem) * -2);cursor:se-resize}ax-image-editor-cropper-window .ax-image-right-handle{top:calc(50% - .5rem);right:calc(var(--spacing, .25rem) * -2);cursor:e-resize}ax-image-editor-cropper-window .ax-image-bottom-left-handle{bottom:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:sw-resize}ax-image-editor-cropper-window .ax-image-left-top-handle{top:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:nw-resize}ax-image-editor-cropper-window .ax-image-bottom-handle{right:calc(50% - .5rem);bottom:calc(var(--spacing, .25rem) * -2);cursor:s-resize}ax-image-editor-cropper-window .ax-image-left-handle{top:calc(50% - .5rem);left:calc(var(--spacing, .25rem) * -2);cursor:w-resize}ax-image-editor-cropper-window .ax-image-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(50% - .5rem);cursor:n-resize}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], encapsulation: i0.ViewEncapsulation.None }); }
369
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorCropperWindowComponent, isStandalone: true, selector: "ax-image-editor-cropper-window", inputs: { showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropperWindowComponent }], viewQueries: [{ propertyName: "cropperWindow", first: true, predicate: ["r"], descendants: true, isSignal: true }], ngImport: i0, template: "<div [style.border]=\"!showGrid() ? '1px dashed white' : null\" #r class=\"ax-image-cropper-window-container\">\n @if (!service.cropperRatio()) {\n <div (pointerdown)=\"handleSelector($event, 'right')\" class=\"ax-image-right-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'top')\" class=\"ax-image-top-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left')\" class=\"ax-image-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom')\" class=\"ax-image-bottom-handle ax-handle-shape\"></div>\n }\n\n <div (pointerdown)=\"handleSelector($event, 'right-top')\" class=\"ax-image-right-top-handle ax-handle-shape\"></div>\n <div\n (pointerdown)=\"handleSelector($event, 'bottom-right')\"\n class=\"ax-image-bottom-right-handle ax-handle-shape\"\n ></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom-left')\" class=\"ax-image-bottom-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left-top')\" class=\"ax-image-left-top-handle ax-handle-shape\"></div>\n\n @if (showGrid()) {\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-image-editor-cropper-window{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);touch-action:none;-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-cropper-window-container{position:relative;display:grid;height:100%;cursor:move;grid-template-columns:repeat(3,minmax(0,1fr));outline-style:var(--tw-outline-style);outline-width:100vmax;outline-color:color-mix(in srgb,#000 70%,transparent);--tw-outline-style: solid;outline-style:solid}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container{outline-color:color-mix(in oklab,var(--color-black, #000) 70%,transparent)}}ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:color-mix(in srgb,#fff 50%,transparent)}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-color:color-mix(in oklab,var(--color-white, #fff) 50%,transparent)}}ax-image-editor-cropper-window .ax-handle-shape{position:absolute;height:calc(var(--spacing, .25rem) * 4);width:calc(var(--spacing, .25rem) * 4);border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-white, #fff);background-color:var(--color-gray-500, oklch(55.1% .027 264.364));-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-right-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(var(--spacing, .25rem) * -2);cursor:ne-resize}ax-image-editor-cropper-window .ax-image-bottom-right-handle{right:calc(var(--spacing, .25rem) * -2);bottom:calc(var(--spacing, .25rem) * -2);cursor:se-resize}ax-image-editor-cropper-window .ax-image-right-handle{top:calc(50% - .5rem);right:calc(var(--spacing, .25rem) * -2);cursor:e-resize}ax-image-editor-cropper-window .ax-image-bottom-left-handle{bottom:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:sw-resize}ax-image-editor-cropper-window .ax-image-left-top-handle{top:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:nw-resize}ax-image-editor-cropper-window .ax-image-bottom-handle{right:calc(50% - .5rem);bottom:calc(var(--spacing, .25rem) * -2);cursor:s-resize}ax-image-editor-cropper-window .ax-image-left-handle{top:calc(50% - .5rem);left:calc(var(--spacing, .25rem) * -2);cursor:w-resize}ax-image-editor-cropper-window .ax-image-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(50% - .5rem);cursor:n-resize}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
357
370
  }
358
371
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropperWindowComponent, decorators: [{
359
372
  type: Component,
360
- args: [{ selector: 'ax-image-editor-cropper-window', encapsulation: ViewEncapsulation.None, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropperWindowComponent }], template: "<div [style.border]=\"!showGrid() ? '1px dashed white' : null\" #r class=\"ax-image-cropper-window-container\">\n @if (!service.cropperRatio()) {\n <div (pointerdown)=\"handleSelector($event, 'right')\" class=\"ax-image-right-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'top')\" class=\"ax-image-top-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left')\" class=\"ax-image-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom')\" class=\"ax-image-bottom-handle ax-handle-shape\"></div>\n }\n\n <div (pointerdown)=\"handleSelector($event, 'right-top')\" class=\"ax-image-right-top-handle ax-handle-shape\"></div>\n <div\n (pointerdown)=\"handleSelector($event, 'bottom-right')\"\n class=\"ax-image-bottom-right-handle ax-handle-shape\"\n ></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom-left')\" class=\"ax-image-bottom-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left-top')\" class=\"ax-image-left-top-handle ax-handle-shape\"></div>\n\n @if (showGrid()) {\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-image-editor-cropper-window{position:absolute;top:45%;left:45%;touch-action:none;-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-cropper-window-container{position:relative;display:grid;height:100%;cursor:move;grid-template-columns:repeat(3,minmax(0,1fr));outline-style:var(--tw-outline-style);outline-width:100vmax;outline-color:color-mix(in srgb,#000 70%,transparent);--tw-outline-style: solid;outline-style:solid}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container{outline-color:color-mix(in oklab,var(--color-black, #000) 70%,transparent)}}ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:color-mix(in srgb,#fff 50%,transparent)}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-color:color-mix(in oklab,var(--color-white, #fff) 50%,transparent)}}ax-image-editor-cropper-window .ax-handle-shape{position:absolute;height:calc(var(--spacing, .25rem) * 4);width:calc(var(--spacing, .25rem) * 4);border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-white, #fff);background-color:var(--color-gray-500, oklch(55.1% .027 264.364));-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-right-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(var(--spacing, .25rem) * -2);cursor:ne-resize}ax-image-editor-cropper-window .ax-image-bottom-right-handle{right:calc(var(--spacing, .25rem) * -2);bottom:calc(var(--spacing, .25rem) * -2);cursor:se-resize}ax-image-editor-cropper-window .ax-image-right-handle{top:calc(50% - .5rem);right:calc(var(--spacing, .25rem) * -2);cursor:e-resize}ax-image-editor-cropper-window .ax-image-bottom-left-handle{bottom:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:sw-resize}ax-image-editor-cropper-window .ax-image-left-top-handle{top:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:nw-resize}ax-image-editor-cropper-window .ax-image-bottom-handle{right:calc(50% - .5rem);bottom:calc(var(--spacing, .25rem) * -2);cursor:s-resize}ax-image-editor-cropper-window .ax-image-left-handle{top:calc(50% - .5rem);left:calc(var(--spacing, .25rem) * -2);cursor:w-resize}ax-image-editor-cropper-window .ax-image-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(50% - .5rem);cursor:n-resize}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
373
+ args: [{ selector: 'ax-image-editor-cropper-window', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropperWindowComponent }], template: "<div [style.border]=\"!showGrid() ? '1px dashed white' : null\" #r class=\"ax-image-cropper-window-container\">\n @if (!service.cropperRatio()) {\n <div (pointerdown)=\"handleSelector($event, 'right')\" class=\"ax-image-right-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'top')\" class=\"ax-image-top-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left')\" class=\"ax-image-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom')\" class=\"ax-image-bottom-handle ax-handle-shape\"></div>\n }\n\n <div (pointerdown)=\"handleSelector($event, 'right-top')\" class=\"ax-image-right-top-handle ax-handle-shape\"></div>\n <div\n (pointerdown)=\"handleSelector($event, 'bottom-right')\"\n class=\"ax-image-bottom-right-handle ax-handle-shape\"\n ></div>\n <div (pointerdown)=\"handleSelector($event, 'bottom-left')\" class=\"ax-image-bottom-left-handle ax-handle-shape\"></div>\n <div (pointerdown)=\"handleSelector($event, 'left-top')\" class=\"ax-image-left-top-handle ax-handle-shape\"></div>\n\n @if (showGrid()) {\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n <div class=\"ax-grid-item\"></div>\n }\n</div>\n", styles: ["@layer properties;@layer components{ax-image-editor-cropper-window{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);touch-action:none;-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-cropper-window-container{position:relative;display:grid;height:100%;cursor:move;grid-template-columns:repeat(3,minmax(0,1fr));outline-style:var(--tw-outline-style);outline-width:100vmax;outline-color:color-mix(in srgb,#000 70%,transparent);--tw-outline-style: solid;outline-style:solid}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container{outline-color:color-mix(in oklab,var(--color-black, #000) 70%,transparent)}}ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:color-mix(in srgb,#fff 50%,transparent)}@supports (color: color-mix(in lab,red,red)){ax-image-editor-cropper-window .ax-image-cropper-window-container .ax-grid-item{border-color:color-mix(in oklab,var(--color-white, #fff) 50%,transparent)}}ax-image-editor-cropper-window .ax-handle-shape{position:absolute;height:calc(var(--spacing, .25rem) * 4);width:calc(var(--spacing, .25rem) * 4);border-style:var(--tw-border-style);border-width:1px;--tw-border-style: dashed;border-style:dashed;border-color:var(--color-white, #fff);background-color:var(--color-gray-500, oklch(55.1% .027 264.364));-webkit-user-select:none;user-select:none}ax-image-editor-cropper-window .ax-image-right-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(var(--spacing, .25rem) * -2);cursor:ne-resize}ax-image-editor-cropper-window .ax-image-bottom-right-handle{right:calc(var(--spacing, .25rem) * -2);bottom:calc(var(--spacing, .25rem) * -2);cursor:se-resize}ax-image-editor-cropper-window .ax-image-right-handle{top:calc(50% - .5rem);right:calc(var(--spacing, .25rem) * -2);cursor:e-resize}ax-image-editor-cropper-window .ax-image-bottom-left-handle{bottom:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:sw-resize}ax-image-editor-cropper-window .ax-image-left-top-handle{top:calc(var(--spacing, .25rem) * -2);left:calc(var(--spacing, .25rem) * -2);cursor:nw-resize}ax-image-editor-cropper-window .ax-image-bottom-handle{right:calc(50% - .5rem);bottom:calc(var(--spacing, .25rem) * -2);cursor:s-resize}ax-image-editor-cropper-window .ax-image-left-handle{top:calc(50% - .5rem);left:calc(var(--spacing, .25rem) * -2);cursor:w-resize}ax-image-editor-cropper-window .ax-image-top-handle{top:calc(var(--spacing, .25rem) * -2);right:calc(50% - .5rem);cursor:n-resize}}@property --tw-outline-style{syntax: \"*\"; inherits: false; initial-value: solid;}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-outline-style: solid;--tw-border-style: solid}}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
361
374
  }], propDecorators: { showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], cropperWindow: [{ type: i0.ViewChild, args: ['r', { isSignal: true }] }] } });
362
375
 
363
- class AXImageEditorViewComponent extends MXBaseComponent {
364
- /** @ignore */
365
- resizeWindow() {
366
- this.canvasResponsive(this.service.newImage);
367
- }
368
- /** @ignore */
369
- canvasResponsive(image) {
370
- const imageWidth = image.width;
371
- const imageHeight = image.height;
372
- this.originalWidth.set(imageWidth);
373
- this.originalHeight.set(imageHeight);
374
- if (imageWidth > imageHeight) {
375
- this.canvasElem().nativeElement.width = imageWidth;
376
- this.canvasElem().nativeElement.height = imageWidth;
377
- }
378
- else {
379
- this.canvasElem().nativeElement.width = imageHeight;
380
- this.canvasElem().nativeElement.height = imageHeight;
376
+ const IMAGE_EDITOR_MAX_BUFFER = 4096;
377
+ const IMAGE_EDITOR_EXPORT_QUALITY = 1;
378
+ function loadHtmlImage(src) {
379
+ return new Promise((resolve, reject) => {
380
+ const image = new Image();
381
+ if (!src.startsWith('data:') && !src.startsWith('blob:')) {
382
+ image.crossOrigin = 'anonymous';
381
383
  }
382
- requestAnimationFrame(() => {
383
- //recalculate width after canvas size change
384
- const recalculateWidth = this.canvasElem().nativeElement.getClientRects()[0]?.width;
385
- let scale;
386
- if (imageWidth > imageHeight) {
387
- scale = this.originalWidth() / recalculateWidth;
388
- }
389
- else {
390
- scale = this.originalHeight() / recalculateWidth;
391
- }
392
- this.scale.set(scale);
393
- this.canvasElem().nativeElement.width = recalculateWidth;
394
- this.canvasElem().nativeElement.height = recalculateWidth;
395
- if (imageWidth > imageHeight) {
396
- this.ctx().drawImage(image, 0, recalculateWidth / 2 - this.originalHeight() / scale / 2, recalculateWidth, this.originalHeight() / scale);
397
- }
398
- else {
399
- this.ctx().drawImage(image, recalculateWidth / 2 - this.originalWidth() / scale / 2, 0, this.originalWidth() / scale, recalculateWidth);
384
+ image.onload = () => resolve(image);
385
+ image.onerror = () => reject(new Error('Failed to load image'));
386
+ image.src = src;
387
+ });
388
+ }
389
+ function canvasToBlob(canvas, type, quality) {
390
+ return new Promise((resolve, reject) => {
391
+ canvas.toBlob((blob) => {
392
+ if (blob) {
393
+ resolve(blob);
394
+ return;
400
395
  }
401
- });
396
+ reject(new Error(`toBlob returned null for type ${type}`));
397
+ }, type, quality);
398
+ });
399
+ }
400
+ async function canvasToEditorBlobs(canvas) {
401
+ const jpeg = await canvasToBlob(canvas, 'image/jpeg', IMAGE_EDITOR_EXPORT_QUALITY);
402
+ const extra = await Promise.allSettled([
403
+ canvasToBlob(canvas, 'image/png'),
404
+ canvasToBlob(canvas, 'image/webp', IMAGE_EDITOR_EXPORT_QUALITY),
405
+ ]);
406
+ const blobs = [jpeg];
407
+ for (const result of extra) {
408
+ if (result.status === 'fulfilled') {
409
+ blobs.push(result.value);
410
+ }
402
411
  }
403
- /** @ignore */
412
+ return blobs;
413
+ }
414
+ function bufferSizeForImage(width, height, max = IMAGE_EDITOR_MAX_BUFFER) {
415
+ const scale = Math.min(1, max / Math.max(width, height, 1));
416
+ return {
417
+ width: Math.max(1, Math.round(width * scale)),
418
+ height: Math.max(1, Math.round(height * scale)),
419
+ };
420
+ }
421
+
422
+ class AXImageEditorViewComponent extends MXBaseComponent {
404
423
  constructor() {
405
424
  super();
406
425
  /**
@@ -442,44 +461,80 @@ class AXImageEditorViewComponent extends MXBaseComponent {
442
461
  ...(ngDevMode ? [{ debugName: "isDrawRectangleCreated" }] : /* istanbul ignore next */ []));
443
462
  /** @ignore */
444
463
  this.parent = inject(AXImageEditorContainerComponent);
445
- /** @ignore */
446
- this.renderer = inject(Renderer2);
464
+ this.destroyRef = inject(DestroyRef);
465
+ this.host = inject((ElementRef));
447
466
  /** @ignore */
448
467
  this.imageEditorHistory = signal([], /* @ts-ignore */
449
468
  ...(ngDevMode ? [{ debugName: "imageEditorHistory" }] : /* istanbul ignore next */ []));
450
469
  /** @ignore */
451
- this.imageEditorHistoryPointer = linkedSignal(() => this.imageEditorHistory().length - 1, /* @ts-ignore */
470
+ this.imageEditorHistoryPointer = signal(-1, /* @ts-ignore */
452
471
  ...(ngDevMode ? [{ debugName: "imageEditorHistoryPointer" }] : /* istanbul ignore next */ []));
453
- this.originalWidth = signal(0, /* @ts-ignore */
454
- ...(ngDevMode ? [{ debugName: "originalWidth" }] : /* istanbul ignore next */ []));
455
- this.originalHeight = signal(0, /* @ts-ignore */
456
- ...(ngDevMode ? [{ debugName: "originalHeight" }] : /* istanbul ignore next */ []));
457
- this.scale = signal(0, /* @ts-ignore */
458
- ...(ngDevMode ? [{ debugName: "scale" }] : /* istanbul ignore next */ []));
472
+ this.canvasReady = signal(false, /* @ts-ignore */
473
+ ...(ngDevMode ? [{ debugName: "canvasReady" }] : /* istanbul ignore next */ []));
474
+ this.drawing = false;
475
+ this.didDraw = false;
476
+ this.highlightSnapshot = null;
477
+ this.highlightPoints = [];
478
+ this.persistGeneration = 0;
479
+ this.sourceToken = 0;
459
480
  afterNextRender(() => {
460
- this.resizeEventListener = this.renderer.listen(window, 'resize', this.resizeWindow.bind(this));
461
- //set canvas options
462
- this.ctx.set(this.canvasElem().nativeElement.getContext('2d', { willReadFrequently: true }));
463
- this.ctx().lineJoin = 'round';
464
- this.ctx().lineCap = 'round';
481
+ const canvas = this.canvasElem()?.nativeElement;
482
+ if (!canvas) {
483
+ return;
484
+ }
485
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
486
+ if (!ctx) {
487
+ return;
488
+ }
489
+ ctx.lineJoin = 'round';
490
+ ctx.lineCap = 'round';
491
+ this.ctx.set(ctx);
492
+ this.canvasReady.set(true);
493
+ const onResize = () => this.fitCanvasDisplay();
494
+ window.addEventListener('resize', onResize);
495
+ this.destroyRef.onDestroy(() => window.removeEventListener('resize', onResize));
465
496
  });
466
497
  effect(() => {
467
- this.setInitialImage(this.src());
498
+ const src = this.src();
499
+ const ready = this.canvasReady();
500
+ if (!ready) {
501
+ return;
502
+ }
503
+ untracked(() => void this.loadSource(src));
468
504
  });
469
505
  effect(() => {
470
506
  if (!this.service.undoPressed()) {
471
- this.imageChanged();
507
+ return;
472
508
  }
509
+ untracked(() => {
510
+ this.applyUndo();
511
+ this.service.undoPressed.set(false);
512
+ });
473
513
  });
474
514
  effect(() => {
475
515
  if (!this.service.redoPressed()) {
476
- this.imageChanged();
516
+ return;
477
517
  }
518
+ untracked(() => {
519
+ this.applyRedo();
520
+ this.service.redoPressed.set(false);
521
+ });
522
+ });
523
+ effect(() => {
524
+ const rotate = this.service.rotate();
525
+ const ctx = this.ctx();
526
+ if (!rotate.userInteract || !ctx) {
527
+ return;
528
+ }
529
+ untracked(() => void this.rotateImage());
478
530
  });
479
531
  effect(() => {
480
- if (!this.service.rotate().userInteract || !this.ctx())
532
+ if (!this.service.isImageLoad()) {
481
533
  return;
482
- this.rotateImage();
534
+ }
535
+ untracked(() => {
536
+ requestAnimationFrame(() => this.fitCanvasDisplay());
537
+ });
483
538
  });
484
539
  effect(() => {
485
540
  const size = this.cropperSize();
@@ -489,167 +544,363 @@ class AXImageEditorViewComponent extends MXBaseComponent {
489
544
  });
490
545
  }
491
546
  /** @ignore */
492
- ngOnDestroy() {
493
- if (this.resizeEventListener) {
494
- this.resizeEventListener();
495
- }
496
- if (this.imageLoadEventListener) {
497
- this.imageLoadEventListener();
498
- }
547
+ resizeWindow() {
548
+ this.fitCanvasDisplay();
499
549
  }
500
- setInitialImage(src, replaceHistory = true) {
501
- this.service.initialImage = new Image();
502
- this.service.initialImage.src = src;
503
- this.service.initialImage.crossOrigin = 'anonymous';
504
- this.imageLoadEventListener = this.renderer.listen(this.service.initialImage, 'load', () => {
505
- this.service.newImage = this.service.initialImage;
506
- this.service.isImageLoad.set(true);
507
- this.canvasResponsive(this.service.initialImage);
508
- if (replaceHistory) {
509
- this.imageEditorHistory.set([this.service.initialImage]);
550
+ /** @ignore */
551
+ canvasResponsive(image) {
552
+ this.paintImage(image);
553
+ }
554
+ async loadSource(src) {
555
+ const token = ++this.sourceToken;
556
+ this.service.loadError.set(false);
557
+ this.service.isImageLoad.set(false);
558
+ this.service.imageBlob.set([]);
559
+ this.service.newImage = null;
560
+ this.service.initialImage = null;
561
+ if (!src) {
562
+ return;
563
+ }
564
+ try {
565
+ const image = await loadHtmlImage(src);
566
+ if (token !== this.sourceToken) {
567
+ return;
510
568
  }
511
- else {
512
- this.imageEditorHistory.update((prev) => [...prev, this.service.initialImage]);
569
+ this.service.initialImage = image;
570
+ this.service.newImage = image;
571
+ this.replaceHistory(image);
572
+ this.paintImage(image);
573
+ this.service.isImageLoad.set(true);
574
+ }
575
+ catch {
576
+ if (token !== this.sourceToken) {
577
+ return;
513
578
  }
514
- });
579
+ this.service.loadError.set(true);
580
+ this.service.isImageLoad.set(false);
581
+ }
515
582
  }
516
- /** @ignore */
517
- imageChanged() {
518
- if (this.service.undoPressed() && this.imageEditorHistoryPointer() >= 1) {
519
- this.imageEditorHistoryPointer.update((prev) => prev - 1);
520
- this.service.newImage = this.imageEditorHistory()[this.imageEditorHistoryPointer()];
521
- this.canvasResponsive(this.imageEditorHistory()[this.imageEditorHistoryPointer()]);
583
+ paintImage(image) {
584
+ const canvas = this.canvasElem()?.nativeElement;
585
+ const ctx = this.ctx();
586
+ if (!canvas || !ctx || !image) {
587
+ return;
522
588
  }
523
- if (this.service.redoPressed() && this.imageEditorHistoryPointer() < this.imageEditorHistory().length - 1) {
524
- this.imageEditorHistoryPointer.update((prev) => prev + 1);
525
- this.service.newImage = this.imageEditorHistory()[this.imageEditorHistoryPointer()];
526
- this.canvasResponsive(this.imageEditorHistory()[this.imageEditorHistoryPointer()]);
589
+ const buffer = bufferSizeForImage(image.naturalWidth || image.width, image.naturalHeight || image.height);
590
+ canvas.width = buffer.width;
591
+ canvas.height = buffer.height;
592
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
593
+ ctx.globalAlpha = 1;
594
+ ctx.globalCompositeOperation = 'source-over';
595
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
596
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
597
+ this.fitCanvasDisplay();
598
+ }
599
+ fitCanvasDisplay() {
600
+ const canvas = this.canvasElem()?.nativeElement;
601
+ if (!canvas?.width || !canvas.height) {
602
+ return;
527
603
  }
528
- this.service.undoPressed.set(false);
529
- this.service.redoPressed.set(false);
604
+ const parent = this.host.nativeElement;
605
+ const maxW = Math.max(parent?.clientWidth || 0, 240);
606
+ const maxH = Math.min(window.innerHeight * 0.55, 560);
607
+ const scale = Math.min(maxW / canvas.width, maxH / canvas.height, 1);
608
+ canvas.style.width = `${Math.max(1, Math.round(canvas.width * scale))}px`;
609
+ canvas.style.height = `${Math.max(1, Math.round(canvas.height * scale))}px`;
610
+ }
611
+ replaceHistory(image) {
612
+ this.imageEditorHistory.set([image]);
613
+ this.imageEditorHistoryPointer.set(0);
614
+ this.syncHistoryFlags();
615
+ }
616
+ pushHistory(image) {
617
+ const pointer = this.imageEditorHistoryPointer();
618
+ const next = this.imageEditorHistory().slice(0, pointer + 1);
619
+ next.push(image);
620
+ this.imageEditorHistory.set(next);
621
+ this.imageEditorHistoryPointer.set(next.length - 1);
622
+ this.syncHistoryFlags();
623
+ }
624
+ syncHistoryFlags() {
625
+ const pointer = this.imageEditorHistoryPointer();
626
+ const length = this.imageEditorHistory().length;
627
+ this.service.canUndo.set(pointer > 0);
628
+ this.service.canRedo.set(pointer >= 0 && pointer < length - 1);
629
+ }
630
+ applyUndo() {
631
+ const pointer = this.imageEditorHistoryPointer();
632
+ if (pointer < 1) {
633
+ this.syncHistoryFlags();
634
+ return;
635
+ }
636
+ const nextPointer = pointer - 1;
637
+ const image = this.imageEditorHistory()[nextPointer];
638
+ this.imageEditorHistoryPointer.set(nextPointer);
639
+ this.service.newImage = image;
640
+ this.paintImage(image);
641
+ this.syncHistoryFlags();
642
+ void this.persistCanvas({ skipHistory: true });
643
+ }
644
+ applyRedo() {
645
+ const history = this.imageEditorHistory();
646
+ const pointer = this.imageEditorHistoryPointer();
647
+ if (pointer >= history.length - 1) {
648
+ this.syncHistoryFlags();
649
+ return;
650
+ }
651
+ const nextPointer = pointer + 1;
652
+ const image = history[nextPointer];
653
+ this.imageEditorHistoryPointer.set(nextPointer);
654
+ this.service.newImage = image;
655
+ this.paintImage(image);
656
+ this.syncHistoryFlags();
657
+ void this.persistCanvas({ skipHistory: true });
658
+ }
659
+ canvasPoint(event) {
660
+ const canvas = this.canvasElem().nativeElement;
661
+ const rect = canvas.getBoundingClientRect();
662
+ const width = rect.width || 1;
663
+ const height = rect.height || 1;
664
+ return {
665
+ x: ((event.clientX - rect.left) / width) * canvas.width,
666
+ y: ((event.clientY - rect.top) / height) * canvas.height,
667
+ };
668
+ }
669
+ isDrawTool() {
670
+ const tool = this.service.penType();
671
+ return tool === 'pen' || tool === 'highlight';
530
672
  }
531
673
  /** @ignore */
532
674
  penConfigHandler(penType) {
675
+ const ctx = this.ctx();
676
+ if (!ctx) {
677
+ return;
678
+ }
679
+ ctx.lineJoin = 'round';
680
+ ctx.lineCap = 'round';
533
681
  switch (penType) {
534
682
  case 'pen':
535
- this.ctx().globalAlpha = 1;
536
- this.ctx().strokeStyle = this.service.penColor();
537
- this.ctx().lineWidth = this.service.penWidth();
683
+ ctx.globalAlpha = 1;
684
+ ctx.globalCompositeOperation = 'source-over';
685
+ ctx.strokeStyle = this.service.penColor();
686
+ ctx.lineWidth = this.service.penWidth();
538
687
  break;
539
688
  case 'highlight':
540
- this.ctx().globalAlpha = 0.008;
541
- this.ctx().strokeStyle = this.service.highlightColor();
542
- this.ctx().lineWidth = this.service.highlightWidth();
689
+ ctx.globalAlpha = 0.4;
690
+ ctx.globalCompositeOperation = 'multiply';
691
+ ctx.strokeStyle = this.service.highlightColor();
692
+ ctx.lineWidth = this.service.highlightWidth();
543
693
  break;
544
694
  }
545
695
  }
696
+ strokeHighlight(points) {
697
+ const ctx = this.ctx();
698
+ if (!ctx || points.length === 0) {
699
+ return;
700
+ }
701
+ this.penConfigHandler('highlight');
702
+ ctx.beginPath();
703
+ ctx.moveTo(points[0].x, points[0].y);
704
+ for (let i = 1; i < points.length; i++) {
705
+ ctx.lineTo(points[i].x, points[i].y);
706
+ }
707
+ if (points.length === 1) {
708
+ ctx.lineTo(points[0].x, points[0].y);
709
+ }
710
+ ctx.stroke();
711
+ }
546
712
  /** @ignore */
547
- mouseDownHandler(e) {
548
- this.isMouseDown.set(true);
549
- if (!this.service.penType())
713
+ historyKeyDown(event) {
714
+ if (this.parent.disabled) {
550
715
  return;
551
- this.penConfigHandler(this.service.penType());
552
- this.ctx().beginPath();
553
- this.ctx().moveTo(e.offsetX, e.offsetY);
554
- this.ctx().lineTo(e.offsetX, e.offsetY);
555
- this.ctx().stroke();
716
+ }
717
+ this.service.handleHistoryShortcut(event);
556
718
  }
557
719
  /** @ignore */
558
- mouseMoveHandler(e) {
559
- if (!this.isMouseDown())
720
+ focusCanvas() {
721
+ this.canvasElem()?.nativeElement.focus({ preventScroll: true });
722
+ }
723
+ /** @ignore */
724
+ mouseDownHandler(event) {
725
+ this.focusCanvas();
726
+ if (!this.isDrawTool() || this.parent.disabled) {
560
727
  return;
561
- if (!this.service.penType())
728
+ }
729
+ const ctx = this.ctx();
730
+ const canvas = this.canvasElem()?.nativeElement;
731
+ const penType = this.service.penType();
732
+ if (!ctx || !canvas || !penType) {
562
733
  return;
563
- this.ctx().lineTo(e.offsetX, e.offsetY);
564
- this.ctx().stroke();
734
+ }
735
+ event.preventDefault();
736
+ canvas.setPointerCapture(event.pointerId);
737
+ this.isMouseDown.set(true);
738
+ this.drawing = true;
739
+ this.didDraw = false;
740
+ const point = this.canvasPoint(event);
741
+ this.penConfigHandler(penType);
742
+ if (penType === 'highlight') {
743
+ this.highlightSnapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
744
+ this.highlightPoints = [point];
745
+ this.strokeHighlight(this.highlightPoints);
746
+ }
747
+ else {
748
+ ctx.beginPath();
749
+ ctx.moveTo(point.x, point.y);
750
+ ctx.lineTo(point.x, point.y);
751
+ ctx.stroke();
752
+ }
753
+ this.didDraw = true;
565
754
  }
566
755
  /** @ignore */
567
- mouseUpHandler() {
568
- this.isMouseDown.set(false);
569
- this.ctx().closePath();
570
- this.saveImageChange();
756
+ mouseMoveHandler(event) {
757
+ if (!this.drawing || !this.isDrawTool()) {
758
+ return;
759
+ }
760
+ const ctx = this.ctx();
761
+ const canvas = this.canvasElem()?.nativeElement;
762
+ const penType = this.service.penType();
763
+ if (!ctx || !canvas || !penType) {
764
+ return;
765
+ }
766
+ const point = this.canvasPoint(event);
767
+ if (penType === 'highlight' && this.highlightSnapshot) {
768
+ ctx.putImageData(this.highlightSnapshot, 0, 0);
769
+ this.highlightPoints.push(point);
770
+ this.strokeHighlight(this.highlightPoints);
771
+ }
772
+ else {
773
+ ctx.lineTo(point.x, point.y);
774
+ ctx.stroke();
775
+ }
776
+ this.didDraw = true;
571
777
  }
572
778
  /** @ignore */
779
+ mouseUpHandler(event) {
780
+ const canvas = this.canvasElem()?.nativeElement;
781
+ if (event && canvas?.hasPointerCapture(event.pointerId)) {
782
+ canvas.releasePointerCapture(event.pointerId);
783
+ }
784
+ const wasDrawing = this.drawing && this.didDraw;
785
+ this.drawing = false;
786
+ this.didDraw = false;
787
+ this.isMouseDown.set(false);
788
+ this.highlightSnapshot = null;
789
+ this.highlightPoints = [];
790
+ const ctx = this.ctx();
791
+ if (ctx) {
792
+ ctx.closePath();
793
+ ctx.globalAlpha = 1;
794
+ ctx.globalCompositeOperation = 'source-over';
795
+ }
796
+ if (wasDrawing) {
797
+ void this.persistCanvas();
798
+ }
799
+ }
800
+ /** Applies the current cropper selection. */
573
801
  async crop() {
574
802
  const cropper = this.cropperWindow();
575
- if (this.service.activeToolState() !== 'crop' || !cropper) {
803
+ const ctx = this.ctx();
804
+ const canvas = this.canvasElem()?.nativeElement;
805
+ if (this.service.activeToolState() !== 'crop' || !cropper || !ctx || !canvas) {
576
806
  return;
577
807
  }
578
808
  const cropRect = cropper.getCropperArea();
579
- const canvas = this.canvasElem().nativeElement;
580
809
  const canvasRect = canvas.getBoundingClientRect();
581
- const scaleX = canvas.width / canvasRect.width;
582
- const scaleY = canvas.height / canvasRect.height;
583
- const dx = (cropRect.x - canvasRect.x) * scaleX;
584
- const dy = (cropRect.y - canvasRect.y) * scaleY;
585
- const cropWidth = cropRect.width * scaleX;
586
- const cropHeight = cropRect.height * scaleY;
587
- const imageData = this.ctx().getImageData(Math.round(dx), Math.round(dy), Math.round(cropWidth), Math.round(cropHeight));
588
- canvas.width = Math.round(cropWidth);
589
- canvas.height = Math.round(cropHeight);
590
- this.ctx().putImageData(imageData, 0, 0);
591
- const cropImage = this.canvasElem().nativeElement.toDataURL('image/jpeg', 1);
592
- await this.saveImageChange();
593
- this.setInitialImage(cropImage, false);
810
+ const scaleX = canvas.width / (canvasRect.width || 1);
811
+ const scaleY = canvas.height / (canvasRect.height || 1);
812
+ const dx = Math.max(0, Math.round((cropRect.x - canvasRect.x) * scaleX));
813
+ const dy = Math.max(0, Math.round((cropRect.y - canvasRect.y) * scaleY));
814
+ const cropWidth = Math.max(1, Math.round(cropRect.width * scaleX));
815
+ const cropHeight = Math.max(1, Math.round(cropRect.height * scaleY));
816
+ const width = Math.min(cropWidth, canvas.width - dx);
817
+ const height = Math.min(cropHeight, canvas.height - dy);
818
+ const imageData = ctx.getImageData(dx, dy, width, height);
819
+ canvas.width = width;
820
+ canvas.height = height;
821
+ ctx.putImageData(imageData, 0, 0);
822
+ this.fitCanvasDisplay();
594
823
  this.service.activeToolState.set(null);
824
+ this.service.penType.set(null);
825
+ await this.persistCanvas();
595
826
  }
596
- /** @ignore */
597
- rotateImage() {
598
- this.ctx().globalAlpha = 1;
599
- this.ctx().save();
600
- this.ctx().clearRect(0, 0, this.canvasElem().nativeElement.width, this.canvasElem().nativeElement.height);
601
- // Calculate scale factor to fit rotated image within canvas
602
- const rotationAngle = (this.service.rotate().state * Math.PI) / 180;
603
- const cos = Math.abs(Math.cos(rotationAngle));
604
- const sin = Math.abs(Math.sin(rotationAngle));
605
- const rotatedWidth = this.service.newImage.width * cos + this.service.newImage.height * sin;
606
- const rotatedHeight = this.service.newImage.width * sin + this.service.newImage.height * cos;
607
- const scaleX = this.canvasElem().nativeElement.width / rotatedWidth;
608
- const scaleY = this.canvasElem().nativeElement.height / rotatedHeight;
609
- const scale = Math.min(scaleX, scaleY);
610
- this.ctx().translate(this.canvasElem().nativeElement.width / 2, this.canvasElem().nativeElement.height / 2);
611
- this.ctx().rotate(rotationAngle);
612
- this.ctx().scale(scale, scale);
613
- this.ctx().drawImage(this.service.newImage, -this.service.newImage.width / 2, -this.service.newImage.height / 2);
614
- this.ctx().restore();
615
- this.saveImageChange();
616
- this.service.rotate.update((prev) => {
617
- return { state: prev.state, userInteract: false };
618
- });
827
+ /** Encodes the current canvas into JPEG, PNG, and WebP blobs. */
828
+ async commitChanges() {
829
+ await this.persistCanvas();
830
+ return this.service.imageBlob();
619
831
  }
620
832
  /** @ignore */
621
- async saveImageChange() {
622
- const canvas = this.canvasElem().nativeElement;
623
- const newImage = new Image();
624
- newImage.crossOrigin = 'anonymous';
625
- newImage.src = canvas.toDataURL('image/jpeg', 1);
626
- this.service.newImage = newImage;
627
- this.imageEditorHistory.update((prev) => [...prev, newImage]);
628
- const canvasToBlob = (type, quality) => {
629
- return new Promise((resolve, reject) => {
630
- canvas.toBlob((blob) => {
631
- if (blob) {
632
- resolve(blob);
633
- }
634
- else {
635
- reject(new Error(`toBlob returned null for type ${type}`));
636
- }
637
- }, type, quality);
638
- });
639
- };
640
- const blobs = await Promise.all([
641
- canvasToBlob('image/jpeg', 1),
642
- canvasToBlob('image/png', 1),
643
- canvasToBlob('image/webp', 1),
644
- ]);
645
- this.service.imageBlob.set(blobs);
833
+ async rotateImage() {
834
+ const canvas = this.canvasElem()?.nativeElement;
835
+ const ctx = this.ctx();
836
+ if (!canvas || !ctx || !canvas.width || !canvas.height) {
837
+ this.service.rotate.update((prev) => ({ state: prev.state, userInteract: false }));
838
+ return;
839
+ }
840
+ const source = document.createElement('canvas');
841
+ source.width = canvas.width;
842
+ source.height = canvas.height;
843
+ const sourceCtx = source.getContext('2d');
844
+ if (!sourceCtx) {
845
+ this.service.rotate.update((prev) => ({ state: prev.state, userInteract: false }));
846
+ return;
847
+ }
848
+ sourceCtx.drawImage(canvas, 0, 0);
849
+ const degrees = this.service.rotate().state;
850
+ const radians = (degrees * Math.PI) / 180;
851
+ const cos = Math.abs(Math.cos(radians));
852
+ const sin = Math.abs(Math.sin(radians));
853
+ const rotatedWidth = Math.max(1, Math.round(source.width * cos + source.height * sin));
854
+ const rotatedHeight = Math.max(1, Math.round(source.width * sin + source.height * cos));
855
+ canvas.width = rotatedWidth;
856
+ canvas.height = rotatedHeight;
857
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
858
+ ctx.globalAlpha = 1;
859
+ ctx.globalCompositeOperation = 'source-over';
860
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
861
+ ctx.save();
862
+ ctx.translate(canvas.width / 2, canvas.height / 2);
863
+ ctx.rotate(radians);
864
+ ctx.drawImage(source, -source.width / 2, -source.height / 2);
865
+ ctx.restore();
866
+ this.fitCanvasDisplay();
867
+ await this.persistCanvas();
868
+ this.service.rotate.update((prev) => ({ state: prev.state, userInteract: false }));
869
+ }
870
+ async persistCanvas(options) {
871
+ const canvas = this.canvasElem()?.nativeElement;
872
+ if (!canvas?.width || !canvas.height) {
873
+ return;
874
+ }
875
+ const generation = ++this.persistGeneration;
876
+ try {
877
+ const blobs = await canvasToEditorBlobs(canvas);
878
+ if (generation !== this.persistGeneration) {
879
+ return;
880
+ }
881
+ this.service.imageBlob.set(blobs);
882
+ const dataUrl = canvas.toDataURL('image/jpeg', 1);
883
+ const image = await loadHtmlImage(dataUrl);
884
+ if (generation !== this.persistGeneration) {
885
+ return;
886
+ }
887
+ this.service.newImage = image;
888
+ if (!options?.skipHistory) {
889
+ this.pushHistory(image);
890
+ }
891
+ }
892
+ catch {
893
+ if (generation !== this.persistGeneration) {
894
+ return;
895
+ }
896
+ }
646
897
  }
647
898
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
648
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorViewComponent, isStandalone: true, selector: "ax-image-editor-view", inputs: { showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, cropperSize: { classPropertyName: "cropperSize", publicName: "cropperSize", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorViewComponent }], viewQueries: [{ propertyName: "canvasElem", first: true, predicate: ["canvasElem"], descendants: true, isSignal: true }, { propertyName: "cropperWindow", first: true, predicate: ["f"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<canvas\n tabindex=\"1\"\n (pointerdown)=\"mouseDownHandler($event)\"\n (pointerup)=\"mouseUpHandler()\"\n (pointermove)=\"mouseMoveHandler($event)\"\n class=\"ax-canvas-element\"\n #canvasElem\n></canvas>\n\n@if (this.service.activeToolState() === 'crop') {\n <ax-image-editor-cropper-window [showGrid]=\"showGrid()\" #f></ax-image-editor-cropper-window>\n}\n", styles: ["@layer components{ax-image-editor-view{position:relative;display:flex;width:100%;touch-action:none;align-items:center;justify-content:center;overflow:hidden;background-color:var(--color-white, #fff)}ax-image-editor-view .ax-canvas-element{cursor:crosshair;background-color:var(--color-black, #000)}ax-image-editor-view .ax-crop-save{position:absolute;top:calc(var(--spacing, .25rem) * 0);right:calc(var(--spacing, .25rem) * 0);margin:calc(var(--spacing, .25rem) * 4)}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXImageEditorCropperWindowComponent, selector: "ax-image-editor-cropper-window", inputs: ["showGrid", "size"] }], encapsulation: i0.ViewEncapsulation.None }); }
899
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorViewComponent, isStandalone: true, selector: "ax-image-editor-view", inputs: { showGrid: { classPropertyName: "showGrid", publicName: "showGrid", isSignal: true, isRequired: false, transformFunction: null }, cropperSize: { classPropertyName: "cropperSize", publicName: "cropperSize", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorViewComponent }], viewQueries: [{ propertyName: "canvasElem", first: true, predicate: ["canvasElem"], descendants: true, isSignal: true }, { propertyName: "cropperWindow", first: true, predicate: ["f"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-image-editor-stage\">\n <canvas\n tabindex=\"0\"\n (pointerdown)=\"mouseDownHandler($event)\"\n (pointerup)=\"mouseUpHandler($event)\"\n (pointercancel)=\"mouseUpHandler($event)\"\n (pointermove)=\"mouseMoveHandler($event)\"\n (keydown)=\"historyKeyDown($event)\"\n class=\"ax-canvas-element\"\n #canvasElem\n ></canvas>\n\n @if (service.activeToolState() === 'crop') {\n <ax-image-editor-cropper-window [showGrid]=\"showGrid()\" #f></ax-image-editor-cropper-window>\n }\n</div>\n", styles: ["@layer components{ax-image-editor-view{position:relative;display:flex;width:100%;touch-action:none;align-items:center;justify-content:center;overflow:hidden;background-color:var(--color-white, #fff)}ax-image-editor-view .ax-image-editor-stage{position:relative;display:inline-flex;max-height:100%;max-width:100%;align-items:center;justify-content:center}ax-image-editor-view .ax-canvas-element{max-height:100%;max-width:100%;cursor:crosshair;background-color:var(--color-black, #000);touch-action:none}ax-image-editor-view .ax-crop-save{position:absolute;top:calc(var(--spacing, .25rem) * 0);right:calc(var(--spacing, .25rem) * 0);margin:calc(var(--spacing, .25rem) * 4)}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: AXImageEditorCropperWindowComponent, selector: "ax-image-editor-cropper-window", inputs: ["showGrid", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
649
900
  }
650
901
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorViewComponent, decorators: [{
651
902
  type: Component,
652
- args: [{ selector: 'ax-image-editor-view', encapsulation: ViewEncapsulation.None, imports: [AXImageEditorCropperWindowComponent], providers: [{ provide: AXComponent, useExisting: AXImageEditorViewComponent }], template: "<canvas\n tabindex=\"1\"\n (pointerdown)=\"mouseDownHandler($event)\"\n (pointerup)=\"mouseUpHandler()\"\n (pointermove)=\"mouseMoveHandler($event)\"\n class=\"ax-canvas-element\"\n #canvasElem\n></canvas>\n\n@if (this.service.activeToolState() === 'crop') {\n <ax-image-editor-cropper-window [showGrid]=\"showGrid()\" #f></ax-image-editor-cropper-window>\n}\n", styles: ["@layer components{ax-image-editor-view{position:relative;display:flex;width:100%;touch-action:none;align-items:center;justify-content:center;overflow:hidden;background-color:var(--color-white, #fff)}ax-image-editor-view .ax-canvas-element{cursor:crosshair;background-color:var(--color-black, #000)}ax-image-editor-view .ax-crop-save{position:absolute;top:calc(var(--spacing, .25rem) * 0);right:calc(var(--spacing, .25rem) * 0);margin:calc(var(--spacing, .25rem) * 4)}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
903
+ args: [{ selector: 'ax-image-editor-view', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [AXImageEditorCropperWindowComponent], providers: [{ provide: AXComponent, useExisting: AXImageEditorViewComponent }], template: "<div class=\"ax-image-editor-stage\">\n <canvas\n tabindex=\"0\"\n (pointerdown)=\"mouseDownHandler($event)\"\n (pointerup)=\"mouseUpHandler($event)\"\n (pointercancel)=\"mouseUpHandler($event)\"\n (pointermove)=\"mouseMoveHandler($event)\"\n (keydown)=\"historyKeyDown($event)\"\n class=\"ax-canvas-element\"\n #canvasElem\n ></canvas>\n\n @if (service.activeToolState() === 'crop') {\n <ax-image-editor-cropper-window [showGrid]=\"showGrid()\" #f></ax-image-editor-cropper-window>\n }\n</div>\n", styles: ["@layer components{ax-image-editor-view{position:relative;display:flex;width:100%;touch-action:none;align-items:center;justify-content:center;overflow:hidden;background-color:var(--color-white, #fff)}ax-image-editor-view .ax-image-editor-stage{position:relative;display:inline-flex;max-height:100%;max-width:100%;align-items:center;justify-content:center}ax-image-editor-view .ax-canvas-element{max-height:100%;max-width:100%;cursor:crosshair;background-color:var(--color-black, #000);touch-action:none}ax-image-editor-view .ax-crop-save{position:absolute;top:calc(var(--spacing, .25rem) * 0);right:calc(var(--spacing, .25rem) * 0);margin:calc(var(--spacing, .25rem) * 4)}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
653
904
  }], ctorParameters: () => [], propDecorators: { showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], cropperSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "cropperSize", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], canvasElem: [{ type: i0.ViewChild, args: ['canvasElem', { isSignal: true }] }], cropperWindow: [{ type: i0.ViewChild, args: ['f', { isSignal: true }] }] } });
654
905
 
655
906
  /**
@@ -658,25 +909,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
658
909
  */
659
910
  class AXImageEditorContainerComponent extends classes((MXInputBaseValueComponent), MXLookComponent) {
660
911
  constructor() {
661
- super(...arguments);
912
+ super();
662
913
  /** @ignore */
663
914
  this.service = inject(AXImageEditorService);
915
+ this.host = inject((ElementRef));
916
+ this.destroyRef = inject(DestroyRef);
664
917
  this.imageEditorContainer = viewChild('f', /* @ts-ignore */
665
918
  ...(ngDevMode ? [{ debugName: "imageEditorContainer" }] : /* istanbul ignore next */ []));
666
919
  this.view = contentChild(AXImageEditorViewComponent, /* @ts-ignore */
667
920
  ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
668
- this.#eff = effect(() => {
669
- void this.service.imageBlob();
670
- if (this.service.imageBlob().length) {
671
- this.commitValue(this.service.imageBlob(), true);
921
+ this.#syncValue = effect(() => {
922
+ const blobs = this.service.imageBlob();
923
+ if (blobs.length) {
924
+ this.commitValue(blobs, true);
672
925
  }
673
926
  else {
674
927
  this.commitValue(null);
675
928
  }
676
929
  }, /* @ts-ignore */
677
- ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
930
+ ...(ngDevMode ? [{ debugName: "#syncValue" }] : /* istanbul ignore next */ []));
931
+ afterNextRender(() => {
932
+ const onKeyDown = (event) => this.handleHistoryShortcut(event);
933
+ document.addEventListener('keydown', onKeyDown, true);
934
+ this.destroyRef.onDestroy(() => document.removeEventListener('keydown', onKeyDown, true));
935
+ });
936
+ }
937
+ #syncValue;
938
+ /** @ignore */
939
+ focusEditor() {
940
+ this.host.nativeElement.focus({ preventScroll: true });
678
941
  }
679
- #eff;
680
942
  /** Applies the current cropper selection. */
681
943
  async crop() {
682
944
  const oldValue = this.value;
@@ -686,16 +948,45 @@ class AXImageEditorContainerComponent extends classes((MXInputBaseValueComponent
686
948
  this.commitValue(blobs, true);
687
949
  }
688
950
  }
689
- get __hostClass() {
690
- if (this.disabled)
691
- return 'ax-state-disabled';
692
- return '';
951
+ /** @ignore */
952
+ onEditorPointerDown(event) {
953
+ const target = event.target;
954
+ if (target instanceof Element && target.closest('canvas.ax-canvas-element')) {
955
+ return;
956
+ }
957
+ this.focusEditor();
958
+ }
959
+ /** @ignore */
960
+ handleHistoryShortcut(event) {
961
+ if (this.disabled) {
962
+ return;
963
+ }
964
+ const root = this.host.nativeElement;
965
+ const activeElement = document.activeElement;
966
+ const isInsideEditor = root.contains(activeElement) || event.composedPath().some((node) => node === root);
967
+ if (!isInsideEditor) {
968
+ return;
969
+ }
970
+ const target = (event.target ?? activeElement);
971
+ if (target instanceof HTMLElement &&
972
+ (target.isContentEditable || target.closest('input, textarea, select, [contenteditable="true"]'))) {
973
+ return;
974
+ }
975
+ this.service.handleHistoryShortcut(event);
693
976
  }
694
- get __hostName() {
695
- return this.name;
977
+ /** Encodes the current canvas into JPEG, PNG, and WebP blobs. */
978
+ async commitChanges() {
979
+ const view = this.view();
980
+ if (!view) {
981
+ return this.service.imageBlob();
982
+ }
983
+ if (this.service.activeToolState() === 'crop') {
984
+ await view.crop();
985
+ }
986
+ return view.commitChanges();
696
987
  }
697
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorContainerComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
698
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorContainerComponent, isStandalone: true, selector: "ax-image-editor-container", inputs: { look: "look", disabled: "disabled" }, outputs: { onValueChanged: "onValueChanged" }, host: { properties: { "class": "this.__hostClass", "attr.name": "this.__hostName" } }, providers: [
988
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
989
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorContainerComponent, isStandalone: true, selector: "ax-image-editor-container", inputs: { look: "look", disabled: "disabled" }, outputs: { onValueChanged: "onValueChanged" }, host: { attributes: { "tabindex": "-1" }, listeners: { "keydown": "handleHistoryShortcut($event)" }, properties: { "class.ax-state-disabled": "disabled", "attr.name": "name" } }, providers: [
699
990
  AXImageEditorService,
700
991
  { provide: AXComponent, useExisting: AXImageEditorContainerComponent },
701
992
  { provide: AXFocusableComponent, useExisting: AXImageEditorContainerComponent },
@@ -705,11 +996,16 @@ class AXImageEditorContainerComponent extends classes((MXInputBaseValueComponent
705
996
  useExisting: forwardRef(() => AXImageEditorContainerComponent),
706
997
  multi: true,
707
998
  },
708
- ], queries: [{ propertyName: "view", first: true, predicate: AXImageEditorViewComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "imageEditorContainer", first: true, predicate: ["f"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (service.isImageLoad()) {\n <div #f class=\"ax-editor-container ax-default {{ look }}\">\n <ng-content select=\"ax-header\"></ng-content>\n\n <ng-content select=\"ax-image-editor-view\"> </ng-content>\n\n <ng-content select=\"ax-footer\"></ng-content>\n </div>\n <div class=\"ax-error-container\"></div>\n} @else {\n <div class=\"ax-loading-spinner-container\">\n <ax-loading-spinner [size]=\"16\" color=\"primary\" text=\"Loading Image ...\"></ax-loading-spinner>\n </div>\n}\n<ng-content select=\"ax-validation-rule\"> </ng-content>\n\n@if (disabled) {\n <div class=\"ax-disable-overlay\"></div>\n}\n", styles: ["@layer components{ax-image-editor-container{position:relative;display:block;width:fit-content}ax-image-editor-container.ax-state-disabled{opacity:50%}ax-image-editor-container .ax-disable-overlay{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);height:100%;width:100%;cursor:not-allowed}ax-image-editor-container>.ax-editor-container{display:block;height:auto!important;padding:calc(var(--spacing, .25rem) * 0)}ax-image-editor-container .ax-loading-spinner-container{display:flex;align-items:center;justify-content:center}ax-image-editor-container ax-footer{width:100%}ax-image-editor-container ax-header{width:100%}ax-image-editor-container ax-header ax-toolbar{justify-content:space-between}ax-image-editor-container ax-header .ax-start-toolbar-divider{display:none}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: AXLoadingSpinnerComponent, selector: "ax-loading-spinner", inputs: ["text", "color", "size", "stroke"] }], encapsulation: i0.ViewEncapsulation.None }); }
999
+ ], queries: [{ propertyName: "view", first: true, predicate: AXImageEditorViewComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "imageEditorContainer", first: true, predicate: ["f"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n #f\n class=\"ax-editor-container ax-default {{ look }}\"\n [class.ax-image-editor-pending]=\"!service.isImageLoad()\"\n (pointerdown)=\"onEditorPointerDown($event)\"\n>\n <ng-content select=\"ax-header\"></ng-content>\n\n <ng-content select=\"ax-image-editor-view\"> </ng-content>\n\n <ng-content select=\"ax-footer\"></ng-content>\n</div>\n\n@if (!service.isImageLoad()) {\n @if (service.loadError()) {\n <div class=\"ax-error-container\">{{ '@acorex:image-editor.load-error' | translate | async }}</div>\n } @else {\n <div class=\"ax-loading-spinner-container\">\n <ax-loading-spinner\n [size]=\"16\"\n color=\"primary\"\n [text]=\"('@acorex:image-editor.loading' | translate | async) || ''\"\n ></ax-loading-spinner>\n </div>\n }\n}\n\n<div class=\"ax-error-container\"></div>\n<ng-content select=\"ax-validation-rule\"> </ng-content>\n\n@if (disabled) {\n <div class=\"ax-disable-overlay\"></div>\n}\n", styles: ["@layer components{ax-image-editor-container{position:relative;display:block;width:fit-content}ax-image-editor-container.ax-state-disabled{opacity:50%}ax-image-editor-container .ax-disable-overlay{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);height:100%;width:100%;cursor:not-allowed}ax-image-editor-container>.ax-editor-container{display:block;height:auto!important;padding:calc(var(--spacing, .25rem) * 0)}ax-image-editor-container>.ax-editor-container.ax-image-editor-pending{position:absolute;height:calc(var(--spacing, .25rem) * 0);width:calc(var(--spacing, .25rem) * 0);overflow:hidden;opacity:0%}ax-image-editor-container .ax-loading-spinner-container,ax-image-editor-container .ax-error-container{display:flex;min-height:calc(var(--spacing, .25rem) * 40);align-items:center;justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 4);text-align:center}ax-image-editor-container ax-footer{width:100%}ax-image-editor-container ax-header{width:100%}ax-image-editor-container ax-header ax-toolbar{justify-content:space-between}ax-image-editor-container ax-header .ax-start-toolbar-divider{display:none}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: AXLoadingSpinnerComponent, selector: "ax-loading-spinner", inputs: ["text", "color", "size", "stroke"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
709
1000
  }
710
1001
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorContainerComponent, decorators: [{
711
1002
  type: Component,
712
- args: [{ selector: 'ax-image-editor-container', encapsulation: ViewEncapsulation.None, outputs: ['onValueChanged'], inputs: ['look', 'disabled'], providers: [
1003
+ args: [{ selector: 'ax-image-editor-container', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, outputs: ['onValueChanged'], inputs: ['look', 'disabled'], host: {
1004
+ '[class.ax-state-disabled]': 'disabled',
1005
+ '[attr.name]': 'name',
1006
+ tabindex: '-1',
1007
+ '(keydown)': 'handleHistoryShortcut($event)',
1008
+ }, providers: [
713
1009
  AXImageEditorService,
714
1010
  { provide: AXComponent, useExisting: AXImageEditorContainerComponent },
715
1011
  { provide: AXFocusableComponent, useExisting: AXImageEditorContainerComponent },
@@ -719,14 +1015,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
719
1015
  useExisting: forwardRef(() => AXImageEditorContainerComponent),
720
1016
  multi: true,
721
1017
  },
722
- ], imports: [AXDecoratorModule, AXLoadingSpinnerComponent], template: "@if (service.isImageLoad()) {\n <div #f class=\"ax-editor-container ax-default {{ look }}\">\n <ng-content select=\"ax-header\"></ng-content>\n\n <ng-content select=\"ax-image-editor-view\"> </ng-content>\n\n <ng-content select=\"ax-footer\"></ng-content>\n </div>\n <div class=\"ax-error-container\"></div>\n} @else {\n <div class=\"ax-loading-spinner-container\">\n <ax-loading-spinner [size]=\"16\" color=\"primary\" text=\"Loading Image ...\"></ax-loading-spinner>\n </div>\n}\n<ng-content select=\"ax-validation-rule\"> </ng-content>\n\n@if (disabled) {\n <div class=\"ax-disable-overlay\"></div>\n}\n", styles: ["@layer components{ax-image-editor-container{position:relative;display:block;width:fit-content}ax-image-editor-container.ax-state-disabled{opacity:50%}ax-image-editor-container .ax-disable-overlay{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);height:100%;width:100%;cursor:not-allowed}ax-image-editor-container>.ax-editor-container{display:block;height:auto!important;padding:calc(var(--spacing, .25rem) * 0)}ax-image-editor-container .ax-loading-spinner-container{display:flex;align-items:center;justify-content:center}ax-image-editor-container ax-footer{width:100%}ax-image-editor-container ax-header{width:100%}ax-image-editor-container ax-header ax-toolbar{justify-content:space-between}ax-image-editor-container ax-header .ax-start-toolbar-divider{display:none}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
723
- }], propDecorators: { imageEditorContainer: [{ type: i0.ViewChild, args: ['f', { isSignal: true }] }], view: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AXImageEditorViewComponent), { isSignal: true }] }], __hostClass: [{
724
- type: HostBinding,
725
- args: ['class']
726
- }], __hostName: [{
727
- type: HostBinding,
728
- args: ['attr.name']
729
- }] } });
1018
+ ], imports: [AXDecoratorModule, AXLoadingSpinnerComponent, AsyncPipe, AXTranslatorPipe], template: "<div\n #f\n class=\"ax-editor-container ax-default {{ look }}\"\n [class.ax-image-editor-pending]=\"!service.isImageLoad()\"\n (pointerdown)=\"onEditorPointerDown($event)\"\n>\n <ng-content select=\"ax-header\"></ng-content>\n\n <ng-content select=\"ax-image-editor-view\"> </ng-content>\n\n <ng-content select=\"ax-footer\"></ng-content>\n</div>\n\n@if (!service.isImageLoad()) {\n @if (service.loadError()) {\n <div class=\"ax-error-container\">{{ '@acorex:image-editor.load-error' | translate | async }}</div>\n } @else {\n <div class=\"ax-loading-spinner-container\">\n <ax-loading-spinner\n [size]=\"16\"\n color=\"primary\"\n [text]=\"('@acorex:image-editor.loading' | translate | async) || ''\"\n ></ax-loading-spinner>\n </div>\n }\n}\n\n<div class=\"ax-error-container\"></div>\n<ng-content select=\"ax-validation-rule\"> </ng-content>\n\n@if (disabled) {\n <div class=\"ax-disable-overlay\"></div>\n}\n", styles: ["@layer components{ax-image-editor-container{position:relative;display:block;width:fit-content}ax-image-editor-container.ax-state-disabled{opacity:50%}ax-image-editor-container .ax-disable-overlay{position:absolute;top:calc(var(--spacing, .25rem) * 0);left:calc(var(--spacing, .25rem) * 0);height:100%;width:100%;cursor:not-allowed}ax-image-editor-container>.ax-editor-container{display:block;height:auto!important;padding:calc(var(--spacing, .25rem) * 0)}ax-image-editor-container>.ax-editor-container.ax-image-editor-pending{position:absolute;height:calc(var(--spacing, .25rem) * 0);width:calc(var(--spacing, .25rem) * 0);overflow:hidden;opacity:0%}ax-image-editor-container .ax-loading-spinner-container,ax-image-editor-container .ax-error-container{display:flex;min-height:calc(var(--spacing, .25rem) * 40);align-items:center;justify-content:center;padding-inline:calc(var(--spacing, .25rem) * 4);text-align:center}ax-image-editor-container ax-footer{width:100%}ax-image-editor-container ax-header{width:100%}ax-image-editor-container ax-header ax-toolbar{justify-content:space-between}ax-image-editor-container ax-header .ax-start-toolbar-divider{display:none}}\n/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */\n"] }]
1019
+ }], ctorParameters: () => [], propDecorators: { imageEditorContainer: [{ type: i0.ViewChild, args: ['f', { isSignal: true }] }], view: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AXImageEditorViewComponent), { isSignal: true }] }] } });
730
1020
 
731
1021
  const IMAGE_EDITOR_ASPECT_RATIO_ACTIVE_SUFFIX = ':active';
732
1022
  function parseAspectRatioItem(item) {
@@ -801,7 +1091,7 @@ class AXImageEditorCropComponent extends AXImageEditorToolComponent {
801
1091
  this.container.crop();
802
1092
  }
803
1093
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
804
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXImageEditorCropComponent, isStandalone: true, selector: "ax-image-editor-crop", inputs: { aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.crop' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('crop')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'crop'\"\n [color]=\"service.activeToolState() === 'crop' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-crop\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1094
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.8", type: AXImageEditorCropComponent, isStandalone: true, selector: "ax-image-editor-crop", inputs: { aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.crop' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('crop')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'crop'\"\n [color]=\"service.activeToolState() === 'crop' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-crop\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
805
1095
  }
806
1096
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropComponent, decorators: [{
807
1097
  type: Component,
@@ -818,7 +1108,7 @@ class AXImageEditorHighlightComponent extends AXImageEditorToolComponent {
818
1108
  this.tool = 'highlight';
819
1109
  }
820
1110
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorHighlightComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
821
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorHighlightComponent, isStandalone: true, selector: "ax-image-editor-highlight", providers: [{ provide: AXComponent, useExisting: AXImageEditorHighlightComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:paint.tools.highlight' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('highlight')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'highlight'\"\n [color]=\"service.activeToolState() === 'highlight' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-highlight\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1111
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorHighlightComponent, isStandalone: true, selector: "ax-image-editor-highlight", providers: [{ provide: AXComponent, useExisting: AXImageEditorHighlightComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:paint.tools.highlight' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('highlight')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'highlight'\"\n [color]=\"service.activeToolState() === 'highlight' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-highlight\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
822
1112
  }
823
1113
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorHighlightComponent, decorators: [{
824
1114
  type: Component,
@@ -834,17 +1124,17 @@ class AXImageEditorHistoryComponent {
834
1124
  this.service = inject(AXImageEditorService);
835
1125
  }
836
1126
  UndoHandler() {
837
- this.service.undoPressed.set(true);
1127
+ this.service.undo();
838
1128
  }
839
1129
  RedoHandler() {
840
- this.service.redoPressed.set(true);
1130
+ this.service.redo();
841
1131
  }
842
1132
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorHistoryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
843
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorHistoryComponent, isStandalone: true, selector: "ax-image-editor-history", providers: [{ provide: AXComponent, useExisting: AXImageEditorHistoryComponent }], ngImport: i0, template: "<div class=\"flex\">\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.undo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"UndoHandler()\"\n look=\"blank\"\n >\n <ax-icon class=\"ax-icon ax-icon-undo\"></ax-icon>\n </ax-button>\n\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.redo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"RedoHandler()\"\n look=\"blank\"\n >\n <ax-icon class=\"ax-icon ax-icon-redo\"></ax-icon>\n </ax-button>\n</div>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1133
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorHistoryComponent, isStandalone: true, selector: "ax-image-editor-history", providers: [{ provide: AXComponent, useExisting: AXImageEditorHistoryComponent }], ngImport: i0, template: "<div class=\"flex\">\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.undo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"UndoHandler()\"\n look=\"blank\"\n [disabled]=\"!service.canUndo()\"\n >\n <ax-icon class=\"ax-icon ax-icon-undo\"></ax-icon>\n </ax-button>\n\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.redo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"RedoHandler()\"\n look=\"blank\"\n [disabled]=\"!service.canRedo()\"\n >\n <ax-icon class=\"ax-icon ax-icon-redo\"></ax-icon>\n </ax-button>\n</div>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
844
1134
  }
845
1135
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorHistoryComponent, decorators: [{
846
1136
  type: Component,
847
- args: [{ selector: 'ax-image-editor-history', encapsulation: ViewEncapsulation.None, imports: [AXButtonComponent, AXDecoratorModule, AXTooltipDirective, AsyncPipe, AXTranslatorPipe, AXTooltipModule], providers: [{ provide: AXComponent, useExisting: AXImageEditorHistoryComponent }], template: "<div class=\"flex\">\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.undo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"UndoHandler()\"\n look=\"blank\"\n >\n <ax-icon class=\"ax-icon ax-icon-undo\"></ax-icon>\n </ax-button>\n\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.redo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"RedoHandler()\"\n look=\"blank\"\n >\n <ax-icon class=\"ax-icon ax-icon-redo\"></ax-icon>\n </ax-button>\n</div>\n" }]
1137
+ args: [{ selector: 'ax-image-editor-history', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [AXButtonComponent, AXDecoratorModule, AXTooltipDirective, AsyncPipe, AXTranslatorPipe, AXTooltipModule], providers: [{ provide: AXComponent, useExisting: AXImageEditorHistoryComponent }], template: "<div class=\"flex\">\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.undo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"UndoHandler()\"\n look=\"blank\"\n [disabled]=\"!service.canUndo()\"\n >\n <ax-icon class=\"ax-icon ax-icon-undo\"></ax-icon>\n </ax-button>\n\n <ax-button\n class=\"ax-sm\"\n [axTooltip]=\"'@acorex:image-editor.redo' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"RedoHandler()\"\n look=\"blank\"\n [disabled]=\"!service.canRedo()\"\n >\n <ax-icon class=\"ax-icon ax-icon-redo\"></ax-icon>\n </ax-button>\n</div>\n" }]
848
1138
  }] });
849
1139
 
850
1140
  /**
@@ -857,7 +1147,7 @@ class AXImageEditorPenComponent extends AXImageEditorToolComponent {
857
1147
  this.tool = 'pen';
858
1148
  }
859
1149
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorPenComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
860
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorPenComponent, isStandalone: true, selector: "ax-image-editor-pen", providers: [{ provide: AXComponent, useExisting: AXImageEditorPenComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:paint.tools.pen' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('pen')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'pen'\"\n [color]=\"service.activeToolState() === 'pen' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-pen\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1150
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorPenComponent, isStandalone: true, selector: "ax-image-editor-pen", providers: [{ provide: AXComponent, useExisting: AXImageEditorPenComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:paint.tools.pen' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('pen')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'pen'\"\n [color]=\"service.activeToolState() === 'pen' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <ax-icon class=\"ax-icon ax-icon-pen\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "directive", type: i2.AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
861
1151
  }
862
1152
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorPenComponent, decorators: [{
863
1153
  type: Component,
@@ -874,7 +1164,7 @@ class AXImageEditorRotateComponent extends AXImageEditorToolComponent {
874
1164
  this.tool = 'rotate';
875
1165
  }
876
1166
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorRotateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
877
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorRotateComponent, isStandalone: true, selector: "ax-image-editor-rotate", providers: [{ provide: AXComponent, useExisting: AXImageEditorRotateComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('rotate')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'rotate'\"\n [color]=\"service.activeToolState() === 'rotate' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <i class=\"fa-solid fa-rotate\"></i>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1167
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorRotateComponent, isStandalone: true, selector: "ax-image-editor-rotate", providers: [{ provide: AXComponent, useExisting: AXImageEditorRotateComponent }], usesInheritance: true, ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate' | translate | async\"\n axTooltipPlacement=\"top\"\n (onClick)=\"service.toggleTool('rotate')\"\n look=\"blank\"\n [selected]=\"service.activeToolState() === 'rotate'\"\n [color]=\"service.activeToolState() === 'rotate' ? 'primary' : 'default'\"\n class=\"ax-sm\"\n>\n <i class=\"fa-solid fa-rotate\"></i>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
878
1168
  }
879
1169
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorRotateComponent, decorators: [{
880
1170
  type: Component,
@@ -905,7 +1195,7 @@ class AXImageEditorCropOptionsComponent {
905
1195
  }
906
1196
  }
907
1197
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
908
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorCropOptionsComponent, isStandalone: true, selector: "ax-image-editor-crop-options", inputs: { aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cropPressed: "cropPressed" }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropOptionsComponent }], ngImport: i0, template: "<div class=\"ax-image-editor-crop-ratios\">\n @for (item of aspectRatios(); track item) {\n <ax-button\n [selected]=\"service.cropperRatio() === item\"\n [color]=\"service.cropperRatio() === item ? 'primary' : 'default'\"\n (click)=\"ratioHandler(item)\"\n class=\"ax-crop-save ax-sm\"\n [text]=\"item\"\n ></ax-button>\n } @empty {\n <ax-button\n [selected]=\"service.cropperRatio() === '1:1'\"\n [color]=\"service.cropperRatio() === '1:1' ? 'primary' : 'default'\"\n (click)=\"ratioHandler('1:1')\"\n class=\"ax-crop-save ax-sm\"\n text=\"1:1\"\n ></ax-button>\n <ax-button\n [selected]=\"service.cropperRatio() === '16:9'\"\n [color]=\"service.cropperRatio() === '16:9' ? 'primary' : 'default'\"\n (click)=\"ratioHandler('16:9')\"\n class=\"ax-crop-save ax-sm\"\n text=\"16:9\"\n ></ax-button>\n }\n</div>\n\n<ax-button\n (click)=\"cropButtonHandler()\"\n class=\"ax-crop-save ax-sm\"\n [text]=\"'@acorex:image-editor.crop' | translate | async\"\n color=\"primary\"\n></ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1198
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXImageEditorCropOptionsComponent, isStandalone: true, selector: "ax-image-editor-crop-options", inputs: { aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cropPressed: "cropPressed" }, providers: [{ provide: AXComponent, useExisting: AXImageEditorCropOptionsComponent }], ngImport: i0, template: "<div class=\"ax-image-editor-crop-ratios\">\n @for (item of aspectRatios(); track item) {\n <ax-button\n [selected]=\"service.cropperRatio() === item\"\n [color]=\"service.cropperRatio() === item ? 'primary' : 'default'\"\n (click)=\"ratioHandler(item)\"\n class=\"ax-crop-save ax-sm\"\n [text]=\"item\"\n ></ax-button>\n } @empty {\n <ax-button\n [selected]=\"service.cropperRatio() === '1:1'\"\n [color]=\"service.cropperRatio() === '1:1' ? 'primary' : 'default'\"\n (click)=\"ratioHandler('1:1')\"\n class=\"ax-crop-save ax-sm\"\n text=\"1:1\"\n ></ax-button>\n <ax-button\n [selected]=\"service.cropperRatio() === '16:9'\"\n [color]=\"service.cropperRatio() === '16:9' ? 'primary' : 'default'\"\n (click)=\"ratioHandler('16:9')\"\n class=\"ax-crop-save ax-sm\"\n text=\"16:9\"\n ></ax-button>\n }\n</div>\n\n<ax-button\n (click)=\"cropButtonHandler()\"\n class=\"ax-crop-save ax-sm\"\n [text]=\"'@acorex:image-editor.crop' | translate | async\"\n color=\"primary\"\n></ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
909
1199
  }
910
1200
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorCropOptionsComponent, decorators: [{
911
1201
  type: Component,
@@ -976,7 +1266,7 @@ class AXImageEditorRotateOptionsComponent {
976
1266
  this.service.rotate.set({ state: -90, userInteract: true });
977
1267
  }
978
1268
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorRotateOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
979
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorRotateOptionsComponent, isStandalone: true, selector: "ax-image-editor-rotate-options", providers: [{ provide: AXComponent, useExisting: AXImageEditorRotateOptionsComponent }], ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate-left' | translate | async\"\n axTooltipPlacement=\"top\"\n (click)=\"rotateLeftHandler()\"\n look=\"blank\"\n>\n <ax-icon class=\"ax-icon ax-icon-arrow-turn-left\"></ax-icon>\n</ax-button>\n\n<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate-right' | translate | async\"\n axTooltipPlacement=\"top\"\n (click)=\"rotateRightHandler()\"\n look=\"blank\"\n>\n <ax-icon class=\"ax-icon ax-icon-arrow-turn-right\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
1269
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.8", type: AXImageEditorRotateOptionsComponent, isStandalone: true, selector: "ax-image-editor-rotate-options", providers: [{ provide: AXComponent, useExisting: AXImageEditorRotateOptionsComponent }], ngImport: i0, template: "<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate-left' | translate | async\"\n axTooltipPlacement=\"top\"\n (click)=\"rotateLeftHandler()\"\n look=\"blank\"\n>\n <ax-icon class=\"ax-icon ax-icon-arrow-turn-left\"></ax-icon>\n</ax-button>\n\n<ax-button\n [axTooltip]=\"'@acorex:image-editor.rotate-right' | translate | async\"\n axTooltipPlacement=\"top\"\n (click)=\"rotateRightHandler()\"\n look=\"blank\"\n>\n <ax-icon class=\"ax-icon ax-icon-arrow-turn-right\"></ax-icon>\n</ax-button>\n", dependencies: [{ kind: "component", type: AXButtonComponent, selector: "ax-button", inputs: ["disabled", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "directive", type: AXTooltipDirective, selector: "[axTooltip]", inputs: ["axTooltipDisabled", "axTooltip", "axTooltipContext", "axTooltipPlacement", "axTooltipOffsetX", "axTooltipOffsetY", "axTooltipOpenAfter", "axTooltipCloseAfter"] }, { kind: "component", type: AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTooltipModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: AXTranslatorPipe, name: "translate" }], encapsulation: i0.ViewEncapsulation.None }); }
980
1270
  }
981
1271
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorRotateOptionsComponent, decorators: [{
982
1272
  type: Component,
@@ -1061,7 +1351,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
1061
1351
  ], providers: [{ provide: AXComponent, useExisting: AXImageEditorToolSelectorComponent }], template: "<ax-image-editor-tools-bar>\n <ax-image-editor-pen></ax-image-editor-pen>\n <ax-image-editor-highlight></ax-image-editor-highlight>\n <ax-image-editor-crop [aspectRatio]=\"aspectRatio()\"></ax-image-editor-crop>\n <ax-image-editor-rotate></ax-image-editor-rotate>\n</ax-image-editor-tools-bar>\n" }]
1062
1352
  }], propDecorators: { aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }] } });
1063
1353
 
1064
- const COMPONENT = [
1354
+ const PUBLIC_COMPONENTS = [
1065
1355
  AXImageEditorViewComponent,
1066
1356
  AXImageEditorContainerComponent,
1067
1357
  AXImageEditorCropComponent,
@@ -1073,28 +1363,9 @@ const COMPONENT = [
1073
1363
  AXImageEditorRotateComponent,
1074
1364
  AXImageEditorCropperWindowComponent,
1075
1365
  ];
1076
- const MODULES = [
1077
- AXButtonModule,
1078
- FormsModule,
1079
- AXRangeSliderModule,
1080
- AXSelectBoxModule,
1081
- AXColorBoxModule,
1082
- AXPopoverModule,
1083
- AXDecoratorModule,
1084
- AXLoadingModule,
1085
- AXDialogModule,
1086
- ];
1087
1366
  class AXImageEditorModule {
1088
1367
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1089
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, imports: [AXButtonModule,
1090
- FormsModule,
1091
- AXRangeSliderModule,
1092
- AXSelectBoxModule,
1093
- AXColorBoxModule,
1094
- AXPopoverModule,
1095
- AXDecoratorModule,
1096
- AXLoadingModule,
1097
- AXDialogModule, AXImageEditorViewComponent,
1368
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, imports: [AXImageEditorViewComponent,
1098
1369
  AXImageEditorContainerComponent,
1099
1370
  AXImageEditorCropComponent,
1100
1371
  AXImageEditorHistoryComponent,
@@ -1113,7 +1384,7 @@ class AXImageEditorModule {
1113
1384
  AXImageEditorHighlightComponent,
1114
1385
  AXImageEditorRotateComponent,
1115
1386
  AXImageEditorCropperWindowComponent] }); }
1116
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, imports: [MODULES, AXImageEditorContainerComponent,
1387
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, imports: [AXImageEditorContainerComponent,
1117
1388
  AXImageEditorCropComponent,
1118
1389
  AXImageEditorHistoryComponent,
1119
1390
  AXImageEditorToolSelectorComponent,
@@ -1125,9 +1396,8 @@ class AXImageEditorModule {
1125
1396
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXImageEditorModule, decorators: [{
1126
1397
  type: NgModule,
1127
1398
  args: [{
1128
- imports: [...MODULES, ...COMPONENT],
1129
- exports: [...COMPONENT],
1130
- providers: [],
1399
+ imports: [...PUBLIC_COMPONENTS],
1400
+ exports: [...PUBLIC_COMPONENTS],
1131
1401
  }]
1132
1402
  }] });
1133
1403