@acorex/components 22.2.19 → 22.2.21

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.
@@ -157,13 +157,17 @@ class AXImageEditorCropperWindowComponent {
157
157
  const { width, height } = this.normalizeRatio(ratio);
158
158
  this.aspectWidth.set(width);
159
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;
160
+ const { width: maxW, height: maxH } = this.getImageBounds();
161
+ const boundsW = maxW || scale * width;
162
+ const boundsH = maxH || scale * height;
163
+ let nextWidth = Math.min(width * scale, boundsW * 0.9);
164
+ let nextHeight = (nextWidth * height) / width;
165
+ if (nextHeight > boundsH * 0.9) {
166
+ nextHeight = boundsH * 0.9;
167
+ nextWidth = (nextHeight * width) / height;
168
+ }
165
169
  host.style.width = `${Math.max(20, nextWidth)}px`;
166
- host.style.height = `${Math.max(20, Math.min(nextHeight, maxH * 0.9))}px`;
170
+ host.style.height = `${Math.max(20, nextHeight)}px`;
167
171
  if (this.positioned) {
168
172
  untracked(() => this.clampToParent());
169
173
  }
@@ -309,11 +313,12 @@ class AXImageEditorCropperWindowComponent {
309
313
  top = this.startTop + (this.startHeight - height);
310
314
  break;
311
315
  }
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();
316
+ const constrained = this.constrainCropBox(left, top, width, height);
317
+ host.style.width = `${constrained.width}px`;
318
+ host.style.height = `${constrained.height}px`;
319
+ this.currentX.set(constrained.left);
320
+ this.currentY.set(constrained.top);
321
+ this.applyPosition();
317
322
  }
318
323
  clearSelector() {
319
324
  this.selectedHandle.set(null);
@@ -337,27 +342,81 @@ class AXImageEditorCropperWindowComponent {
337
342
  }
338
343
  centerOnParent() {
339
344
  const host = this.elementRef.nativeElement;
340
- const parent = host.parentElement;
341
- if (!parent) {
345
+ const { width: maxW, height: maxH } = this.getImageBounds();
346
+ if (!maxW || !maxH) {
342
347
  return;
343
348
  }
344
- const left = Math.max(0, (parent.clientWidth - host.offsetWidth) / 2);
345
- const top = Math.max(0, (parent.clientHeight - host.offsetHeight) / 2);
349
+ const left = Math.max(0, (maxW - host.offsetWidth) / 2);
350
+ const top = Math.max(0, (maxH - host.offsetHeight) / 2);
346
351
  this.currentX.set(left);
347
352
  this.currentY.set(top);
348
- this.applyPosition();
353
+ this.clampToParent();
354
+ }
355
+ getImageBounds() {
356
+ const stage = this.elementRef.nativeElement.parentElement;
357
+ const canvas = stage?.querySelector('canvas.ax-canvas-element');
358
+ if (canvas instanceof HTMLCanvasElement) {
359
+ return {
360
+ width: canvas.clientWidth,
361
+ height: canvas.clientHeight,
362
+ };
363
+ }
364
+ return {
365
+ width: stage?.clientWidth ?? 0,
366
+ height: stage?.clientHeight ?? 0,
367
+ };
368
+ }
369
+ constrainCropBox(left, top, width, height) {
370
+ const { width: maxW, height: maxH } = this.getImageBounds();
371
+ if (!maxW || !maxH) {
372
+ return { left, top, width, height };
373
+ }
374
+ const minSize = 20;
375
+ const locked = !!this.service.cropperRatio();
376
+ const aspect = this.aspectWidth() / this.aspectHeight();
377
+ width = Math.max(minSize, width);
378
+ height = Math.max(minSize, height);
379
+ if (locked) {
380
+ height = width / aspect;
381
+ if (height < minSize) {
382
+ height = minSize;
383
+ width = height * aspect;
384
+ }
385
+ }
386
+ const fitScale = Math.min(1, maxW / width, maxH / height);
387
+ width *= fitScale;
388
+ height *= fitScale;
389
+ if (locked) {
390
+ height = width / aspect;
391
+ if (height > maxH) {
392
+ height = maxH;
393
+ width = height * aspect;
394
+ }
395
+ if (width > maxW) {
396
+ width = maxW;
397
+ height = width / aspect;
398
+ }
399
+ }
400
+ width = Math.max(minSize, Math.min(width, maxW));
401
+ height = Math.max(minSize, Math.min(height, maxH));
402
+ left = Math.max(0, Math.min(left, maxW - width));
403
+ top = Math.max(0, Math.min(top, maxH - height));
404
+ width = Math.min(width, maxW - left);
405
+ height = Math.min(height, maxH - top);
406
+ if (locked) {
407
+ const scale = Math.min(1, (maxW - left) / width, (maxH - top) / height);
408
+ width = Math.max(minSize, width * scale);
409
+ height = width / aspect;
410
+ }
411
+ return { left, top, width, height };
349
412
  }
350
413
  clampToParent() {
351
414
  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));
415
+ const constrained = this.constrainCropBox(this.currentX(), this.currentY(), host.offsetWidth, host.offsetHeight);
416
+ host.style.width = `${constrained.width}px`;
417
+ host.style.height = `${constrained.height}px`;
418
+ this.currentX.set(constrained.left);
419
+ this.currentY.set(constrained.top);
361
420
  this.applyPosition();
362
421
  }
363
422
  applyPosition() {
@@ -896,11 +955,11 @@ class AXImageEditorViewComponent extends MXBaseComponent {
896
955
  }
897
956
  }
898
957
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AXImageEditorViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
899
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", 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 }); }
958
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", 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 canvas{height:auto!important;width:auto!important}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 }); }
900
959
  }
901
960
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: AXImageEditorViewComponent, decorators: [{
902
961
  type: Component,
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"] }]
962
+ 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 canvas{height:auto!important;width:auto!important}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"] }]
904
963
  }], 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 }] }] } });
905
964
 
906
965
  /**