@underlayerdev/ui 0.10.1 → 0.12.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@underlayerdev/ui",
3
- "version": "0.10.1",
3
+ "version": "0.12.0",
4
4
  "peerDependencies": {
5
5
  "@angular/cdk": "^22.0.0",
6
6
  "@angular/common": "^22.0.0",
@@ -4,6 +4,7 @@ import * as i1 from '@angular/cdk/accordion';
4
4
  import { CdkAccordionItem } from '@angular/cdk/accordion';
5
5
  import { FormValueControl, WithOptionalFieldTree, ValidationError, FormCheckboxControl } from '@angular/forms/signals';
6
6
  import Splide, { Options } from '@splidejs/splide';
7
+ import { ImageTransform, Dimensions } from 'ngx-image-cropper';
7
8
  import { CurrencyPipe } from '@angular/common';
8
9
  import * as _underlayerdev_ui from '@underlayerdev/ui';
9
10
 
@@ -108,15 +109,25 @@ declare class AvatarComponent {
108
109
  alt: _angular_core.InputSignal<string>;
109
110
  /** Show a skeleton placeholder instead of content while loading. */
110
111
  loading: _angular_core.InputSignal<boolean>;
112
+ /**
113
+ * Marks this avatar as a photo picker: when there's no photo to show, a
114
+ * default person icon fills in for initials/icon, and a dimmed overlay
115
+ * with a photo icon is layered on top to signal it's clickable — shown
116
+ * even once a photo is set, so the user can tell they can still change
117
+ * it, not just add one the first time. Purely visual — wrap the avatar
118
+ * in your own button/link to actually handle the click.
119
+ */
120
+ editable: _angular_core.InputSignal<boolean>;
111
121
  /** Internal signal to track image loading errors */
112
122
  protected readonly imageError: _angular_core.WritableSignal<boolean>;
123
+ protected readonly hasPhoto: _angular_core.Signal<boolean>;
113
124
  /** Computed icon size based on avatar size */
114
125
  readonly iconSize: _angular_core.Signal<IconSize$1>;
115
126
  constructor();
116
127
  /** Handles image loading errors */
117
128
  protected handleImageError(): void;
118
129
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AvatarComponent, never>;
119
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AvatarComponent, "ul-avatar", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
130
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AvatarComponent, "ul-avatar", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
120
131
  }
121
132
 
122
133
  /** Icon size keys from design tokens (iconography.font.size). */
@@ -887,11 +898,16 @@ type ImageCropperShape = 'rectangle' | 'round';
887
898
  *
888
899
  * Two interaction modes, chosen by whether `aspectRatio` is set:
889
900
  * - **Locked** (`aspectRatio` given, e.g. `1` for an avatar): a fixed-size
890
- * frame; the user drags/zooms the photo underneath it. Best when every
891
- * output needs the same ratio (avatars, thumbnails).
901
+ * frame; the user drags/zooms the photo underneath it. ngx-image-cropper
902
+ * applies that pan with *no bounds checking at all* (unlike its
903
+ * frame-resize path, which does call `checkCropperWithinMaxSizeBounds`),
904
+ * so this component clamps translateH/translateV itself — see
905
+ * `clampPan()` — snapping the photo back to fully cover the frame the
906
+ * moment a drag would otherwise leave a gap.
892
907
  * - **Free-form** (`aspectRatio` omitted, the default): a resizable frame
893
- * over a static photo; the user drags its corners to trim/straighten with
894
- * no ratio imposed. Best for listing photos, which keep their own shape
908
+ * over a static photo; the user drags its corners to trim/straighten
909
+ * with no ratio imposed. This path is already bounds-checked by the
910
+ * library itself. Best for listing photos, which keep their own shape
895
911
  * today (see `um-image-upload`'s auto-width carousel).
896
912
  *
897
913
  * Emits a resized `File`, ready to hand to any upload call that accepts
@@ -934,15 +950,13 @@ declare class ImageCropperComponent {
934
950
  readonly title: _angular_core.InputSignal<string>;
935
951
  /** Label for the confirm button. */
936
952
  readonly confirmLabel: _angular_core.InputSignal<string>;
937
- /** Label for the cancel button. */
938
- readonly cancelLabel: _angular_core.InputSignal<string>;
939
953
  /** Label next to the zoom slider (locked-aspect mode only). */
940
954
  readonly zoomLabel: _angular_core.InputSignal<string>;
941
955
  /** Accessible label for the crop frame. */
942
956
  readonly frameAriaLabel: _angular_core.InputSignal<string>;
943
957
  /** Emitted with the cropped, resized photo once the user confirms. */
944
958
  readonly cropped: _angular_core.OutputEmitterRef<File>;
945
- /** Emitted when the user cancels or dismisses the modal without cropping. */
959
+ /** Emitted when the user dismisses the modal (backdrop, Escape, close button) without cropping. */
946
960
  readonly cancelled: _angular_core.OutputEmitterRef<void>;
947
961
  /** Emitted when the source photo fails to load in the cropper. */
948
962
  readonly loadFailed: _angular_core.OutputEmitterRef<void>;
@@ -958,16 +972,23 @@ declare class ImageCropperComponent {
958
972
  protected readonly maxZoom = 3;
959
973
  protected readonly zoomStep = 0.01;
960
974
  protected readonly zoom: _angular_core.WritableSignal<number>;
961
- protected readonly transform: _angular_core.Signal<{
962
- scale: number;
963
- }>;
964
975
  protected readonly cropperReady: _angular_core.WritableSignal<boolean>;
976
+ private readonly baseImageSize;
977
+ private readonly pan;
978
+ protected readonly clampedPan: _angular_core.Signal<{
979
+ h: number;
980
+ v: number;
981
+ }>;
982
+ protected readonly transform: _angular_core.Signal<ImageTransform>;
965
983
  constructor();
984
+ protected onTransformChange(event: ImageTransform): void;
966
985
  protected onZoomInput(event: Event): void;
986
+ protected onCropperReady(size: Dimensions): void;
967
987
  protected onConfirm(): Promise<void>;
968
- protected onCancel(): void;
988
+ protected onClosed(): void;
989
+ private clampPan;
969
990
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageCropperComponent, never>;
970
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ImageCropperComponent, "ul-image-cropper", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "imageFile": { "alias": "imageFile"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "aspectRatio": { "alias": "aspectRatio"; "required": false; "isSignal": true; }; "maxOutputSize": { "alias": "maxOutputSize"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "confirmLabel": { "alias": "confirmLabel"; "required": false; "isSignal": true; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; "isSignal": true; }; "zoomLabel": { "alias": "zoomLabel"; "required": false; "isSignal": true; }; "frameAriaLabel": { "alias": "frameAriaLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "cropped": "cropped"; "cancelled": "cancelled"; "loadFailed": "loadFailed"; }, never, never, true, never>;
991
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ImageCropperComponent, "ul-image-cropper", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "imageFile": { "alias": "imageFile"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "aspectRatio": { "alias": "aspectRatio"; "required": false; "isSignal": true; }; "maxOutputSize": { "alias": "maxOutputSize"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "confirmLabel": { "alias": "confirmLabel"; "required": false; "isSignal": true; }; "zoomLabel": { "alias": "zoomLabel"; "required": false; "isSignal": true; }; "frameAriaLabel": { "alias": "frameAriaLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; "cropped": "cropped"; "cancelled": "cancelled"; "loadFailed": "loadFailed"; }, never, never, true, never>;
971
992
  }
972
993
 
973
994
  type InputType = 'text' | 'password' | 'email' | 'number' | 'tel' | 'url';