@underlayerdev/ui 0.11.0 → 0.13.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
package/styles/foundations.css
CHANGED
|
@@ -298,6 +298,33 @@ a:active {
|
|
|
298
298
|
color: #a781ff;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
.ul-image {
|
|
302
|
+
height: 100%;
|
|
303
|
+
}
|
|
304
|
+
.ul-image--not-found {
|
|
305
|
+
position: relative;
|
|
306
|
+
}
|
|
307
|
+
.ul-image--not-found::after {
|
|
308
|
+
display: flex;
|
|
309
|
+
place-items: center;
|
|
310
|
+
place-content: center;
|
|
311
|
+
position: absolute;
|
|
312
|
+
z-index: 2;
|
|
313
|
+
top: 0;
|
|
314
|
+
left: 0;
|
|
315
|
+
width: 100%;
|
|
316
|
+
height: 100%;
|
|
317
|
+
font-family: "Ultra Icons";
|
|
318
|
+
font-size: 48px;
|
|
319
|
+
line-height: 48px;
|
|
320
|
+
color: rgba(255, 255, 255, 0.3);
|
|
321
|
+
content: "image_dashed";
|
|
322
|
+
visibility: visible;
|
|
323
|
+
}
|
|
324
|
+
.ul-image--not-found::after {
|
|
325
|
+
background-color: #232323;
|
|
326
|
+
}
|
|
327
|
+
|
|
301
328
|
@font-face {
|
|
302
329
|
font-family: "Roboto Mono";
|
|
303
330
|
src: url("fonts/RobotoMono.ttf") format("truetype");
|
|
@@ -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
|
|
|
@@ -79,7 +80,7 @@ declare class AreaCodeComponent {
|
|
|
79
80
|
|
|
80
81
|
type UiSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
81
82
|
type UiCheckboxSize = 'default' | 'sm' | 'lg';
|
|
82
|
-
type UiTheme = 'fill-purple' | 'fill-white' | 'fill-yellow' | 'fill-red' | 'transparent-purple' | 'transparent-white' | 'transparent-red' | 'transparent-black' | 'ghost-purple' | 'ghost-white' | 'ghost-red' | 'ghost-green' | 'outline-white' | 'outline-purple' | 'outline-red' | 'outline-green';
|
|
83
|
+
type UiTheme = 'fill-purple' | 'fill-white' | 'fill-yellow' | 'fill-red' | 'fill-green' | 'transparent-purple' | 'transparent-white' | 'transparent-red' | 'transparent-black' | 'ghost-purple' | 'ghost-white' | 'ghost-red' | 'ghost-green' | 'outline-white' | 'outline-purple' | 'outline-red' | 'outline-green';
|
|
83
84
|
|
|
84
85
|
type IconSize$1 = '5' | '6' | '7' | '8' | '16';
|
|
85
86
|
/**
|
|
@@ -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). */
|
|
@@ -885,14 +896,19 @@ type ImageCropperShape = 'rectangle' | 'round';
|
|
|
885
896
|
* third-party crop mechanics never leak outside the design system —
|
|
886
897
|
* consumers only ever see `ul-image-cropper`.
|
|
887
898
|
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
*
|
|
899
|
+
* Two interaction modes, chosen by whether `aspectRatio` is set:
|
|
900
|
+
* - **Locked** (`aspectRatio` given, e.g. `1` for an avatar): a fixed-size
|
|
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.
|
|
907
|
+
* - **Free-form** (`aspectRatio` omitted, the default): a resizable frame
|
|
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
|
|
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
|
|
898
914
|
* one — this component only produces the file, it never uploads it.
|
|
@@ -922,7 +938,11 @@ declare class ImageCropperComponent {
|
|
|
922
938
|
readonly imageFile: _angular_core.InputSignal<File | null>;
|
|
923
939
|
/** Visual shape of the crop mask. */
|
|
924
940
|
readonly shape: _angular_core.InputSignal<ImageCropperShape>;
|
|
925
|
-
/**
|
|
941
|
+
/**
|
|
942
|
+
* Locks the crop frame to this width/height ratio and switches to
|
|
943
|
+
* pan-and-zoom mode. Omit (or pass null) for a free-form resizable frame
|
|
944
|
+
* with no ratio imposed.
|
|
945
|
+
*/
|
|
926
946
|
readonly aspectRatio: _angular_core.InputSignal<number | null>;
|
|
927
947
|
/** Longest side (px) of the final output image; scales down without distorting. */
|
|
928
948
|
readonly maxOutputSize: _angular_core.InputSignal<number>;
|
|
@@ -930,6 +950,8 @@ declare class ImageCropperComponent {
|
|
|
930
950
|
readonly title: _angular_core.InputSignal<string>;
|
|
931
951
|
/** Label for the confirm button. */
|
|
932
952
|
readonly confirmLabel: _angular_core.InputSignal<string>;
|
|
953
|
+
/** Label next to the zoom slider (locked-aspect mode only). */
|
|
954
|
+
readonly zoomLabel: _angular_core.InputSignal<string>;
|
|
933
955
|
/** Accessible label for the crop frame. */
|
|
934
956
|
readonly frameAriaLabel: _angular_core.InputSignal<string>;
|
|
935
957
|
/** Emitted with the cropped, resized photo once the user confirms. */
|
|
@@ -944,13 +966,29 @@ declare class ImageCropperComponent {
|
|
|
944
966
|
protected readonly isLocked: _angular_core.Signal<boolean>;
|
|
945
967
|
/** ngx-image-cropper always wants a number here; unused while isLocked() is false. */
|
|
946
968
|
protected readonly effectiveAspectRatio: _angular_core.Signal<number>;
|
|
947
|
-
protected readonly
|
|
969
|
+
protected readonly frameWidth: _angular_core.Signal<number | undefined>;
|
|
970
|
+
protected readonly frameHeight: _angular_core.Signal<number | undefined>;
|
|
971
|
+
protected readonly minZoom = 1;
|
|
972
|
+
protected readonly maxZoom = 3;
|
|
973
|
+
protected readonly zoomStep = 0.01;
|
|
974
|
+
protected readonly zoom: _angular_core.WritableSignal<number>;
|
|
948
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>;
|
|
949
983
|
constructor();
|
|
984
|
+
protected onTransformChange(event: ImageTransform): void;
|
|
985
|
+
protected onZoomInput(event: Event): void;
|
|
986
|
+
protected onCropperReady(size: Dimensions): void;
|
|
950
987
|
protected onConfirm(): Promise<void>;
|
|
951
988
|
protected onClosed(): void;
|
|
989
|
+
private clampPan;
|
|
952
990
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageCropperComponent, never>;
|
|
953
|
-
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; }; "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>;
|
|
954
992
|
}
|
|
955
993
|
|
|
956
994
|
type InputType = 'text' | 'password' | 'email' | 'number' | 'tel' | 'url';
|