@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.
@@ -395,9 +395,21 @@ class AvatarComponent {
395
395
  /** Show a skeleton placeholder instead of content while loading. */
396
396
  loading = input(false, /* @ts-ignore */
397
397
  ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
398
+ /**
399
+ * Marks this avatar as a photo picker: when there's no photo to show, a
400
+ * default person icon fills in for initials/icon, and a dimmed overlay
401
+ * with a photo icon is layered on top to signal it's clickable — shown
402
+ * even once a photo is set, so the user can tell they can still change
403
+ * it, not just add one the first time. Purely visual — wrap the avatar
404
+ * in your own button/link to actually handle the click.
405
+ */
406
+ editable = input(false, /* @ts-ignore */
407
+ ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
398
408
  /** Internal signal to track image loading errors */
399
409
  imageError = signal(false, /* @ts-ignore */
400
410
  ...(ngDevMode ? [{ debugName: "imageError" }] : /* istanbul ignore next */ []));
411
+ hasPhoto = computed(() => !!this.src() && !this.imageError(), /* @ts-ignore */
412
+ ...(ngDevMode ? [{ debugName: "hasPhoto" }] : /* istanbul ignore next */ []));
401
413
  /** Computed icon size based on avatar size */
402
414
  iconSize = computed(() => {
403
415
  const sizeMap = {
@@ -422,7 +434,7 @@ class AvatarComponent {
422
434
  this.imageError.set(true);
423
435
  }
424
436
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
425
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: AvatarComponent, isStandalone: true, selector: "ul-avatar", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
437
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: AvatarComponent, isStandalone: true, selector: "ul-avatar", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "initials", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
426
438
  <ul-skeleton
427
439
  class="ul-avatar ul-avatar--{{ size() }}"
428
440
  [show]="loading()"
@@ -430,16 +442,24 @@ class AvatarComponent {
430
442
  borderRadius="full"
431
443
  >
432
444
  <div class="ul-avatar ul-avatar--{{ size() }}">
433
- @if (src() && !imageError()) {
445
+ @if (hasPhoto()) {
434
446
  <img [src]="src()" [alt]="alt()" (error)="handleImageError()" class="ul-avatar__image" />
435
447
  } @else if (initials()) {
436
448
  <span class="ul-avatar__initials">{{ initials() }}</span>
437
449
  } @else if (icon()) {
438
450
  <ul-icon [icon]="icon()!" [size]="iconSize()" />
451
+ } @else if (editable()) {
452
+ <ul-icon icon="user" [size]="iconSize()" />
453
+ }
454
+
455
+ @if (editable()) {
456
+ <div class="ul-avatar__edit-overlay" aria-hidden="true">
457
+ <ul-icon icon="photo" [size]="iconSize()" />
458
+ </div>
439
459
  }
440
460
  </div>
441
461
  </ul-skeleton>
442
- `, isInline: true, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border-radius:4096px;background-color:#ffffff1a;color:#fff}.ul-avatar--sm{width:32px;height:32px;min-width:32px}.ul-avatar--md{width:40px;height:40px;min-width:40px}.ul-avatar--lg{width:48px;height:48px;min-width:48px}.ul-avatar--xl{width:64px;height:64px;min-width:64px}.ul-avatar--2xl{width:128px;height:128px;min-width:128px}.ul-avatar__image{width:100%;height:100%;object-fit:cover;object-position:center}.ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800;text-transform:uppercase;-webkit-user-select:none;user-select:none;line-height:1}.ul-avatar--sm .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar--md .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar--lg .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar--xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar--2xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:800}.ul-avatar .ul-icon{color:#ffffffb3}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "ul-icon", inputs: ["size", "weight", "icon"] }, { kind: "component", type: SkeletonComponent, selector: "ul-skeleton", inputs: ["variant", "width", "height", "lines", "borderRadius", "show"] }], encapsulation: i0.ViewEncapsulation.None });
462
+ `, isInline: true, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border-radius:4096px;background-color:#ffffff1a;color:#fff}.ul-avatar--sm{width:32px;height:32px;min-width:32px}.ul-avatar--md{width:40px;height:40px;min-width:40px}.ul-avatar--lg{width:48px;height:48px;min-width:48px}.ul-avatar--xl{width:64px;height:64px;min-width:64px}.ul-avatar--2xl{width:128px;height:128px;min-width:128px}.ul-avatar__image{width:100%;height:100%;object-fit:cover;object-position:center}.ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800;text-transform:uppercase;-webkit-user-select:none;user-select:none;line-height:1}.ul-avatar--sm .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar--md .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar--lg .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar--xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar--2xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:800}.ul-avatar .ul-icon{color:#ffffffb3}.ul-avatar__edit-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0a0a0a8c;pointer-events:none;opacity:1;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.ul-avatar__edit-overlay .ul-icon{color:#fff;transition:transform .15s cubic-bezier(.4,0,.2,1)}@media(hover:hover)and (pointer:fine){.ul-avatar__edit-overlay{opacity:0}.ul-avatar:hover .ul-avatar__edit-overlay{opacity:1}.ul-avatar:hover .ul-avatar__edit-overlay .ul-icon{transform:scale(.75)}}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "ul-icon", inputs: ["size", "weight", "icon"] }, { kind: "component", type: SkeletonComponent, selector: "ul-skeleton", inputs: ["variant", "width", "height", "lines", "borderRadius", "show"] }], encapsulation: i0.ViewEncapsulation.None });
443
463
  }
444
464
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AvatarComponent, decorators: [{
445
465
  type: Component,
@@ -451,17 +471,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
451
471
  borderRadius="full"
452
472
  >
453
473
  <div class="ul-avatar ul-avatar--{{ size() }}">
454
- @if (src() && !imageError()) {
474
+ @if (hasPhoto()) {
455
475
  <img [src]="src()" [alt]="alt()" (error)="handleImageError()" class="ul-avatar__image" />
456
476
  } @else if (initials()) {
457
477
  <span class="ul-avatar__initials">{{ initials() }}</span>
458
478
  } @else if (icon()) {
459
479
  <ul-icon [icon]="icon()!" [size]="iconSize()" />
480
+ } @else if (editable()) {
481
+ <ul-icon icon="user" [size]="iconSize()" />
482
+ }
483
+
484
+ @if (editable()) {
485
+ <div class="ul-avatar__edit-overlay" aria-hidden="true">
486
+ <ul-icon icon="photo" [size]="iconSize()" />
487
+ </div>
460
488
  }
461
489
  </div>
462
490
  </ul-skeleton>
463
- `, encapsulation: ViewEncapsulation.None, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border-radius:4096px;background-color:#ffffff1a;color:#fff}.ul-avatar--sm{width:32px;height:32px;min-width:32px}.ul-avatar--md{width:40px;height:40px;min-width:40px}.ul-avatar--lg{width:48px;height:48px;min-width:48px}.ul-avatar--xl{width:64px;height:64px;min-width:64px}.ul-avatar--2xl{width:128px;height:128px;min-width:128px}.ul-avatar__image{width:100%;height:100%;object-fit:cover;object-position:center}.ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800;text-transform:uppercase;-webkit-user-select:none;user-select:none;line-height:1}.ul-avatar--sm .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar--md .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar--lg .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar--xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar--2xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:800}.ul-avatar .ul-icon{color:#ffffffb3}\n"] }]
464
- }], ctorParameters: () => [], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
491
+ `, encapsulation: ViewEncapsulation.None, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;border-radius:4096px;background-color:#ffffff1a;color:#fff}.ul-avatar--sm{width:32px;height:32px;min-width:32px}.ul-avatar--md{width:40px;height:40px;min-width:40px}.ul-avatar--lg{width:48px;height:48px;min-width:48px}.ul-avatar--xl{width:64px;height:64px;min-width:64px}.ul-avatar--2xl{width:128px;height:128px;min-width:128px}.ul-avatar__image{width:100%;height:100%;object-fit:cover;object-position:center}.ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800;text-transform:uppercase;-webkit-user-select:none;user-select:none;line-height:1}.ul-avatar--sm .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar--md .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar--lg .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar--xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar--2xl .ul-avatar__initials{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:800}.ul-avatar .ul-icon{color:#ffffffb3}.ul-avatar__edit-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background-color:#0a0a0a8c;pointer-events:none;opacity:1;transition:opacity .15s cubic-bezier(.4,0,.2,1)}.ul-avatar__edit-overlay .ul-icon{color:#fff;transition:transform .15s cubic-bezier(.4,0,.2,1)}@media(hover:hover)and (pointer:fine){.ul-avatar__edit-overlay{opacity:0}.ul-avatar:hover .ul-avatar__edit-overlay{opacity:1}.ul-avatar:hover .ul-avatar__edit-overlay .ul-icon{transform:scale(.75)}}\n"] }]
492
+ }], ctorParameters: () => [], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }] } });
465
493
 
466
494
  /**
467
495
  * A reusable avatar group component that displays multiple avatars with overflow handling.
@@ -522,7 +550,7 @@ class AvatarGroupComponent {
522
550
  </div>
523
551
  }
524
552
  </div>
525
- `, isInline: true, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar-group{display:inline-flex;align-items:center;flex-wrap:wrap}.ul-avatar-group__avatar{margin-left:-8px}.ul-avatar-group--sm .ul-avatar-group__avatar{margin-left:-4px}.ul-avatar-group--md .ul-avatar-group__avatar{margin-left:-8px}.ul-avatar-group--lg .ul-avatar-group__avatar,.ul-avatar-group--xl .ul-avatar-group__avatar{margin-left:-12px}.ul-avatar-group--stacked .ul-avatar{margin-left:0!important;border:2px solid #0a0a0a}.ul-avatar-group--stacked .ul-avatar:not(:first-child){margin-left:-8px}.ul-avatar-group--stacked.ul-avatar-group--sm .ul-avatar:not(:first-child){margin-left:-4px}.ul-avatar-group--stacked.ul-avatar-group--md .ul-avatar:not(:first-child){margin-left:-8px}.ul-avatar-group--stacked.ul-avatar-group--lg .ul-avatar:not(:first-child){margin-left:-12px}.ul-avatar-group--stacked.ul-avatar-group--xl .ul-avatar:not(:first-child){margin-left:-16px}.ul-avatar-group__overflow{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;border-radius:4096px;background-color:#ffffff1a;color:#fff;border:2px solid #0a0a0a}.ul-avatar-group__overflow--sm{width:32px;height:32px;min-width:32px;font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar-group__overflow--md{width:40px;height:40px;min-width:40px;font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar-group__overflow--lg{width:48px;height:48px;min-width:48px;font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar-group__overflow--xl{width:64px;height:64px;min-width:64px;font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar-group__overflow-text{-webkit-user-select:none;user-select:none;line-height:1}\n"], dependencies: [{ kind: "component", type: AvatarComponent, selector: "ul-avatar", inputs: ["size", "src", "initials", "icon", "alt", "loading"] }], encapsulation: i0.ViewEncapsulation.None });
553
+ `, isInline: true, styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-avatar-group{display:inline-flex;align-items:center;flex-wrap:wrap}.ul-avatar-group__avatar{margin-left:-8px}.ul-avatar-group--sm .ul-avatar-group__avatar{margin-left:-4px}.ul-avatar-group--md .ul-avatar-group__avatar{margin-left:-8px}.ul-avatar-group--lg .ul-avatar-group__avatar,.ul-avatar-group--xl .ul-avatar-group__avatar{margin-left:-12px}.ul-avatar-group--stacked .ul-avatar{margin-left:0!important;border:2px solid #0a0a0a}.ul-avatar-group--stacked .ul-avatar:not(:first-child){margin-left:-8px}.ul-avatar-group--stacked.ul-avatar-group--sm .ul-avatar:not(:first-child){margin-left:-4px}.ul-avatar-group--stacked.ul-avatar-group--md .ul-avatar:not(:first-child){margin-left:-8px}.ul-avatar-group--stacked.ul-avatar-group--lg .ul-avatar:not(:first-child){margin-left:-12px}.ul-avatar-group--stacked.ul-avatar-group--xl .ul-avatar:not(:first-child){margin-left:-16px}.ul-avatar-group__overflow{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;border-radius:4096px;background-color:#ffffff1a;color:#fff;border:2px solid #0a0a0a}.ul-avatar-group__overflow--sm{width:32px;height:32px;min-width:32px;font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:800}.ul-avatar-group__overflow--md{width:40px;height:40px;min-width:40px;font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:800}.ul-avatar-group__overflow--lg{width:48px;height:48px;min-width:48px;font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:800}.ul-avatar-group__overflow--xl{width:64px;height:64px;min-width:64px;font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:800}.ul-avatar-group__overflow-text{-webkit-user-select:none;user-select:none;line-height:1}\n"], dependencies: [{ kind: "component", type: AvatarComponent, selector: "ul-avatar", inputs: ["size", "src", "initials", "icon", "alt", "loading", "editable"] }], encapsulation: i0.ViewEncapsulation.None });
526
554
  }
527
555
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: AvatarGroupComponent, decorators: [{
528
556
  type: Component,
@@ -2738,14 +2766,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
2738
2766
  args: ['document:keydown.escape']
2739
2767
  }] } });
2740
2768
 
2769
+ const DEFAULT_MAX_OUTPUT_SIZE = 1024;
2741
2770
  // Matches $spacing-spacing-60 (240px) — the longer side of the fixed crop
2742
2771
  // frame used in locked-aspect-ratio mode. Kept as a plain constant since
2743
2772
  // ngx-image-cropper's inputs take numbers, not Sass tokens.
2744
2773
  const FRAME_MAX_DIMENSION = 240;
2745
- const DEFAULT_MAX_OUTPUT_SIZE = 1024;
2746
2774
  const MIN_ZOOM = 1;
2747
2775
  const MAX_ZOOM = 3;
2748
2776
  const ZOOM_STEP = 0.01;
2777
+ function clamp(value, min, max) {
2778
+ return Math.min(Math.max(value, min), max);
2779
+ }
2749
2780
  /**
2750
2781
  * Modal flow for cropping/resizing a photo. Wraps ngx-image-cropper so the
2751
2782
  * third-party crop mechanics never leak outside the design system —
@@ -2753,11 +2784,16 @@ const ZOOM_STEP = 0.01;
2753
2784
  *
2754
2785
  * Two interaction modes, chosen by whether `aspectRatio` is set:
2755
2786
  * - **Locked** (`aspectRatio` given, e.g. `1` for an avatar): a fixed-size
2756
- * frame; the user drags/zooms the photo underneath it. Best when every
2757
- * output needs the same ratio (avatars, thumbnails).
2787
+ * frame; the user drags/zooms the photo underneath it. ngx-image-cropper
2788
+ * applies that pan with *no bounds checking at all* (unlike its
2789
+ * frame-resize path, which does call `checkCropperWithinMaxSizeBounds`),
2790
+ * so this component clamps translateH/translateV itself — see
2791
+ * `clampPan()` — snapping the photo back to fully cover the frame the
2792
+ * moment a drag would otherwise leave a gap.
2758
2793
  * - **Free-form** (`aspectRatio` omitted, the default): a resizable frame
2759
- * over a static photo; the user drags its corners to trim/straighten with
2760
- * no ratio imposed. Best for listing photos, which keep their own shape
2794
+ * over a static photo; the user drags its corners to trim/straighten
2795
+ * with no ratio imposed. This path is already bounds-checked by the
2796
+ * library itself. Best for listing photos, which keep their own shape
2761
2797
  * today (see `um-image-upload`'s auto-width carousel).
2762
2798
  *
2763
2799
  * Emits a resized `File`, ready to hand to any upload call that accepts
@@ -2807,9 +2843,6 @@ class ImageCropperComponent {
2807
2843
  /** Label for the confirm button. */
2808
2844
  confirmLabel = input('Save', /* @ts-ignore */
2809
2845
  ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
2810
- /** Label for the cancel button. */
2811
- cancelLabel = input('Cancel', /* @ts-ignore */
2812
- ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
2813
2846
  /** Label next to the zoom slider (locked-aspect mode only). */
2814
2847
  zoomLabel = input('Zoom', /* @ts-ignore */
2815
2848
  ...(ngDevMode ? [{ debugName: "zoomLabel" }] : /* istanbul ignore next */ []));
@@ -2818,7 +2851,7 @@ class ImageCropperComponent {
2818
2851
  ...(ngDevMode ? [{ debugName: "frameAriaLabel" }] : /* istanbul ignore next */ []));
2819
2852
  /** Emitted with the cropped, resized photo once the user confirms. */
2820
2853
  cropped = output();
2821
- /** Emitted when the user cancels or dismisses the modal without cropping. */
2854
+ /** Emitted when the user dismisses the modal (backdrop, Escape, close button) without cropping. */
2822
2855
  cancelled = output();
2823
2856
  /** Emitted when the source photo fails to load in the cropper. */
2824
2857
  loadFailed = output();
@@ -2855,23 +2888,53 @@ class ImageCropperComponent {
2855
2888
  zoomStep = ZOOM_STEP;
2856
2889
  zoom = signal(MIN_ZOOM, /* @ts-ignore */
2857
2890
  ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
2858
- transform = computed(() => ({ scale: this.zoom() }), /* @ts-ignore */
2859
- ...(ngDevMode ? [{ debugName: "transform" }] : /* istanbul ignore next */ []));
2860
2891
  cropperReady = signal(false, /* @ts-ignore */
2861
2892
  ...(ngDevMode ? [{ debugName: "cropperReady" }] : /* istanbul ignore next */ []));
2893
+ // The photo's own rendered size (at zoom 1, before any pan), reported by
2894
+ // ngx-image-cropper once it has fit the photo to cover the frame — the
2895
+ // basis for clampPan()'s bounds. `pan` is what the user's drag *asked*
2896
+ // for; `clampedPan` is what actually reaches the child, corrected to
2897
+ // never uncover the frame.
2898
+ baseImageSize = signal(null, /* @ts-ignore */
2899
+ ...(ngDevMode ? [{ debugName: "baseImageSize" }] : /* istanbul ignore next */ []));
2900
+ pan = signal({ h: 0, v: 0 }, /* @ts-ignore */
2901
+ ...(ngDevMode ? [{ debugName: "pan" }] : /* istanbul ignore next */ []));
2902
+ clampedPan = computed(() => this.clampPan(this.pan(), this.zoom()), /* @ts-ignore */
2903
+ ...(ngDevMode ? [{ debugName: "clampedPan" }] : /* istanbul ignore next */ []));
2904
+ transform = computed(() => ({
2905
+ scale: this.zoom(),
2906
+ translateH: this.clampedPan().h,
2907
+ translateV: this.clampedPan().v,
2908
+ }), /* @ts-ignore */
2909
+ ...(ngDevMode ? [{ debugName: "transform" }] : /* istanbul ignore next */ []));
2862
2910
  constructor() {
2863
2911
  // A fresh file (new selection, or re-opening for a retry) always starts
2864
- // from a neutral zoom rather than carrying over the previous session's,
2865
- // and isn't croppable again until ngx-image-cropper reports it loaded.
2912
+ // from a neutral zoom/pan rather than carrying over the previous
2913
+ // session's, and isn't croppable again until ngx-image-cropper reports
2914
+ // it loaded.
2866
2915
  effect(() => {
2867
2916
  this.imageFile();
2868
2917
  this.zoom.set(MIN_ZOOM);
2918
+ this.pan.set({ h: 0, v: 0 });
2919
+ this.baseImageSize.set(null);
2869
2920
  this.cropperReady.set(false);
2870
2921
  });
2871
2922
  }
2923
+ // ngx-image-cropper only bounds-checks its frame-resize path; dragging the
2924
+ // photo itself (locked mode) applies translateH/V with no clamping and
2925
+ // reports the result here on release. Re-deriving from `pan` (rather than
2926
+ // trusting the event's translate values outright) keeps this the single
2927
+ // source of truth clampedPan reads from.
2928
+ onTransformChange(event) {
2929
+ this.pan.set({ h: event.translateH ?? 0, v: event.translateV ?? 0 });
2930
+ }
2872
2931
  onZoomInput(event) {
2873
2932
  this.zoom.set(event.target.valueAsNumber);
2874
2933
  }
2934
+ onCropperReady(size) {
2935
+ this.baseImageSize.set(size);
2936
+ this.cropperReady.set(true);
2937
+ }
2875
2938
  async onConfirm() {
2876
2939
  const croppedEvent = await this.cropperRef()?.crop('blob');
2877
2940
  const blob = croppedEvent?.blob;
@@ -2880,17 +2943,38 @@ class ImageCropperComponent {
2880
2943
  this.cropped.emit(new File([blob], 'photo.jpg', { type: blob.type }));
2881
2944
  this.open.set(false);
2882
2945
  }
2883
- onCancel() {
2946
+ onClosed() {
2884
2947
  this.cancelled.emit();
2885
- this.open.set(false);
2948
+ }
2949
+ // translateH/V are percentages of the photo's own (unscaled) box, per
2950
+ // ngx-image-cropper's default translateUnit — so the on-screen pixel
2951
+ // shift is (percent/100 * baseSize) * scale (CSS applies scale() *after*
2952
+ // translate() in the same transform, magnifying it). Solving
2953
+ // |shift| <= (baseSize*scale - frameSize) / 2 for the percent gives the
2954
+ // bound below; it comes out to exactly 0 on whichever axis the photo was
2955
+ // fit tightest to the frame (no slack to pan there without zooming in
2956
+ // first), and the true remaining slack on the other axis.
2957
+ clampPan(pan, scale) {
2958
+ const base = this.baseImageSize();
2959
+ const frameWidth = this.frameWidth();
2960
+ const frameHeight = this.frameHeight();
2961
+ if (!base || !frameWidth || !frameHeight || base.width <= 0 || base.height <= 0) {
2962
+ return { h: 0, v: 0 };
2963
+ }
2964
+ const maxH = Math.max(50 * (1 - frameWidth / (base.width * scale)), 0);
2965
+ const maxV = Math.max(50 * (1 - frameHeight / (base.height * scale)), 0);
2966
+ return {
2967
+ h: clamp(pan.h, -maxH, maxH),
2968
+ v: clamp(pan.v, -maxV, maxV),
2969
+ };
2886
2970
  }
2887
2971
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ImageCropperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2888
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ImageCropperComponent, isStandalone: true, selector: "ul-image-cropper", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, imageFile: { classPropertyName: "imageFile", publicName: "imageFile", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, maxOutputSize: { classPropertyName: "maxOutputSize", publicName: "maxOutputSize", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, zoomLabel: { classPropertyName: "zoomLabel", publicName: "zoomLabel", isSignal: true, isRequired: false, transformFunction: null }, frameAriaLabel: { classPropertyName: "frameAriaLabel", publicName: "frameAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", cropped: "cropped", cancelled: "cancelled", loadFailed: "loadFailed" }, viewQueries: [{ propertyName: "cropperRef", first: true, predicate: ImageCropperComponent$1, descendants: true, isSignal: true }], ngImport: i0, template: "<ul-modal [(open)]=\"open\" variant=\"custom\" [title]=\"title()\" (closed)=\"onCancel()\">\n @if (imageFile()) {\n <div class=\"ul-image-cropper\">\n <image-cropper\n class=\"ul-image-cropper__frame\"\n [imageFile]=\"cropperImageFile()\"\n [maintainAspectRatio]=\"isLocked()\"\n [aspectRatio]=\"effectiveAspectRatio()\"\n [roundCropper]=\"isRound()\"\n [cropperStaticWidth]=\"frameWidth()\"\n [cropperStaticHeight]=\"frameHeight()\"\n [allowMoveImage]=\"isLocked()\"\n [transform]=\"transform()\"\n [resizeToWidth]=\"maxOutputSize()\"\n [resizeToHeight]=\"maxOutputSize()\"\n [cropperFrameAriaLabel]=\"frameAriaLabel()\"\n format=\"jpeg\"\n output=\"blob\"\n (cropperReady)=\"cropperReady.set(true)\"\n (loadImageFailed)=\"loadFailed.emit()\"\n />\n\n @if (isLocked()) {\n <label class=\"ul-image-cropper__zoom\">\n <span class=\"ul-image-cropper__zoom-label\">{{ zoomLabel() }}</span>\n <input\n type=\"range\"\n class=\"ul-image-cropper__zoom-input\"\n [min]=\"minZoom\"\n [max]=\"maxZoom\"\n [step]=\"zoomStep\"\n [value]=\"zoom()\"\n (input)=\"onZoomInput($event)\"\n />\n </label>\n }\n </div>\n }\n\n <div ul-modal-footer>\n <ul-button theme=\"ghost-white\" size=\"md\" (buttonClick)=\"onCancel()\">\n {{ cancelLabel() }}\n </ul-button>\n <ul-button\n theme=\"fill-purple\"\n size=\"md\"\n [disabled]=\"!cropperReady()\"\n (buttonClick)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </ul-button>\n </div>\n</ul-modal>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-image-cropper{display:flex;flex-direction:column;align-items:center;gap:16px}.ul-image-cropper__frame{display:block;width:100%;height:320px;--cropper-overlay-color: rgba(10, 10, 10, .85);--cropper-outline-color: rgba(10, 10, 10, .85);--cropper-color: #6f3de0;--cropper-border: 2px solid #6f3de0;--cropper-hover-border: 2px solid #6f3de0;--cropper-focus-border: 2px solid #6f3de0;--cropper-resize-square-bg: #6f3de0;--cropper-resize-square-border: 1px solid #0a0a0a}.ul-image-cropper__zoom{display:flex;align-items:center;gap:12px;width:100%}.ul-image-cropper__zoom-label{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:400;color:#ffffffb3;flex-shrink:0}.ul-image-cropper__zoom-input{flex:1;accent-color:#6f3de0}\n"], dependencies: [{ kind: "component", type: ImageCropperComponent$1, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }, { kind: "component", type: ModalComponent, selector: "ul-modal", inputs: ["open", "variant", "title", "closeOnBackdropClick", "showCloseButton", "confirmLabel", "cancelLabel", "customClass", "fullScreenInMobile"], outputs: ["openChange", "closed", "confirmed", "cancelled"] }, { kind: "component", type: ButtonComponent, selector: "ul-button", inputs: ["size", "theme", "iconOnly", "vertical", "disabled", "type", "ariaLabel"], outputs: ["buttonClick"] }] });
2972
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ImageCropperComponent, isStandalone: true, selector: "ul-image-cropper", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, imageFile: { classPropertyName: "imageFile", publicName: "imageFile", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, maxOutputSize: { classPropertyName: "maxOutputSize", publicName: "maxOutputSize", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, zoomLabel: { classPropertyName: "zoomLabel", publicName: "zoomLabel", isSignal: true, isRequired: false, transformFunction: null }, frameAriaLabel: { classPropertyName: "frameAriaLabel", publicName: "frameAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", cropped: "cropped", cancelled: "cancelled", loadFailed: "loadFailed" }, viewQueries: [{ propertyName: "cropperRef", first: true, predicate: ImageCropperComponent$1, descendants: true, isSignal: true }], ngImport: i0, template: "<ul-modal [(open)]=\"open\" variant=\"custom\" [title]=\"title()\" (closed)=\"onClosed()\">\n @if (imageFile()) {\n <div class=\"ul-image-cropper\">\n <image-cropper\n class=\"ul-image-cropper__frame\"\n [imageFile]=\"cropperImageFile()\"\n [maintainAspectRatio]=\"isLocked()\"\n [aspectRatio]=\"effectiveAspectRatio()\"\n [roundCropper]=\"isRound()\"\n [cropperStaticWidth]=\"frameWidth()\"\n [cropperStaticHeight]=\"frameHeight()\"\n [allowMoveImage]=\"isLocked()\"\n [transform]=\"transform()\"\n [resizeToWidth]=\"maxOutputSize()\"\n [resizeToHeight]=\"maxOutputSize()\"\n [cropperFrameAriaLabel]=\"frameAriaLabel()\"\n format=\"jpeg\"\n output=\"blob\"\n (cropperReady)=\"onCropperReady($event)\"\n (transformChange)=\"onTransformChange($event)\"\n (loadImageFailed)=\"loadFailed.emit()\"\n />\n\n @if (isLocked()) {\n <label class=\"ul-image-cropper__zoom\">\n <span class=\"ul-image-cropper__zoom-label\">{{ zoomLabel() }}</span>\n <input\n type=\"range\"\n class=\"ul-image-cropper__zoom-input\"\n [min]=\"minZoom\"\n [max]=\"maxZoom\"\n [step]=\"zoomStep\"\n [value]=\"zoom()\"\n (input)=\"onZoomInput($event)\"\n />\n </label>\n }\n </div>\n }\n\n <div class=\"ul-modal__foter-content\" ul-modal-footer>\n <ul-button\n theme=\"fill-purple\"\n size=\"md\"\n [disabled]=\"!cropperReady()\"\n (buttonClick)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </ul-button>\n </div>\n</ul-modal>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-image-cropper{display:flex;flex-direction:column;align-items:center;gap:16px}.ul-image-cropper__frame{display:block;width:100%;height:320px;--cropper-overlay-color: rgba(10, 10, 10, .85);--cropper-outline-color: rgba(10, 10, 10, .85);--cropper-color: #6f3de0;--cropper-border: 2px solid #6f3de0;--cropper-hover-border: 2px solid #6f3de0;--cropper-focus-border: 2px solid #6f3de0;--cropper-resize-square-bg: #6f3de0;--cropper-resize-square-border: 1px solid #0a0a0a}.ul-image-cropper__zoom{display:flex;align-items:center;gap:12px;width:100%}.ul-image-cropper__zoom-label{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:400;color:#ffffffb3;flex-shrink:0}.ul-image-cropper__zoom-input{flex:1;accent-color:#6f3de0}.ul-modal__foter-content{width:100%;margin-top:32px}\n"], dependencies: [{ kind: "component", type: ImageCropperComponent$1, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }, { kind: "component", type: ModalComponent, selector: "ul-modal", inputs: ["open", "variant", "title", "closeOnBackdropClick", "showCloseButton", "confirmLabel", "cancelLabel", "customClass", "fullScreenInMobile"], outputs: ["openChange", "closed", "confirmed", "cancelled"] }, { kind: "component", type: ButtonComponent, selector: "ul-button", inputs: ["size", "theme", "iconOnly", "vertical", "disabled", "type", "ariaLabel"], outputs: ["buttonClick"] }] });
2889
2973
  }
2890
2974
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ImageCropperComponent, decorators: [{
2891
2975
  type: Component,
2892
- args: [{ selector: 'ul-image-cropper', imports: [ImageCropperComponent$1, ModalComponent, ButtonComponent], template: "<ul-modal [(open)]=\"open\" variant=\"custom\" [title]=\"title()\" (closed)=\"onCancel()\">\n @if (imageFile()) {\n <div class=\"ul-image-cropper\">\n <image-cropper\n class=\"ul-image-cropper__frame\"\n [imageFile]=\"cropperImageFile()\"\n [maintainAspectRatio]=\"isLocked()\"\n [aspectRatio]=\"effectiveAspectRatio()\"\n [roundCropper]=\"isRound()\"\n [cropperStaticWidth]=\"frameWidth()\"\n [cropperStaticHeight]=\"frameHeight()\"\n [allowMoveImage]=\"isLocked()\"\n [transform]=\"transform()\"\n [resizeToWidth]=\"maxOutputSize()\"\n [resizeToHeight]=\"maxOutputSize()\"\n [cropperFrameAriaLabel]=\"frameAriaLabel()\"\n format=\"jpeg\"\n output=\"blob\"\n (cropperReady)=\"cropperReady.set(true)\"\n (loadImageFailed)=\"loadFailed.emit()\"\n />\n\n @if (isLocked()) {\n <label class=\"ul-image-cropper__zoom\">\n <span class=\"ul-image-cropper__zoom-label\">{{ zoomLabel() }}</span>\n <input\n type=\"range\"\n class=\"ul-image-cropper__zoom-input\"\n [min]=\"minZoom\"\n [max]=\"maxZoom\"\n [step]=\"zoomStep\"\n [value]=\"zoom()\"\n (input)=\"onZoomInput($event)\"\n />\n </label>\n }\n </div>\n }\n\n <div ul-modal-footer>\n <ul-button theme=\"ghost-white\" size=\"md\" (buttonClick)=\"onCancel()\">\n {{ cancelLabel() }}\n </ul-button>\n <ul-button\n theme=\"fill-purple\"\n size=\"md\"\n [disabled]=\"!cropperReady()\"\n (buttonClick)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </ul-button>\n </div>\n</ul-modal>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-image-cropper{display:flex;flex-direction:column;align-items:center;gap:16px}.ul-image-cropper__frame{display:block;width:100%;height:320px;--cropper-overlay-color: rgba(10, 10, 10, .85);--cropper-outline-color: rgba(10, 10, 10, .85);--cropper-color: #6f3de0;--cropper-border: 2px solid #6f3de0;--cropper-hover-border: 2px solid #6f3de0;--cropper-focus-border: 2px solid #6f3de0;--cropper-resize-square-bg: #6f3de0;--cropper-resize-square-border: 1px solid #0a0a0a}.ul-image-cropper__zoom{display:flex;align-items:center;gap:12px;width:100%}.ul-image-cropper__zoom-label{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:400;color:#ffffffb3;flex-shrink:0}.ul-image-cropper__zoom-input{flex:1;accent-color:#6f3de0}\n"] }]
2893
- }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], imageFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageFile", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], maxOutputSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxOutputSize", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], zoomLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomLabel", required: false }] }], frameAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "frameAriaLabel", required: false }] }], cropped: [{ type: i0.Output, args: ["cropped"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], loadFailed: [{ type: i0.Output, args: ["loadFailed"] }], cropperRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ImageCropperComponent$1), { isSignal: true }] }] } });
2976
+ args: [{ selector: 'ul-image-cropper', imports: [ImageCropperComponent$1, ModalComponent, ButtonComponent], template: "<ul-modal [(open)]=\"open\" variant=\"custom\" [title]=\"title()\" (closed)=\"onClosed()\">\n @if (imageFile()) {\n <div class=\"ul-image-cropper\">\n <image-cropper\n class=\"ul-image-cropper__frame\"\n [imageFile]=\"cropperImageFile()\"\n [maintainAspectRatio]=\"isLocked()\"\n [aspectRatio]=\"effectiveAspectRatio()\"\n [roundCropper]=\"isRound()\"\n [cropperStaticWidth]=\"frameWidth()\"\n [cropperStaticHeight]=\"frameHeight()\"\n [allowMoveImage]=\"isLocked()\"\n [transform]=\"transform()\"\n [resizeToWidth]=\"maxOutputSize()\"\n [resizeToHeight]=\"maxOutputSize()\"\n [cropperFrameAriaLabel]=\"frameAriaLabel()\"\n format=\"jpeg\"\n output=\"blob\"\n (cropperReady)=\"onCropperReady($event)\"\n (transformChange)=\"onTransformChange($event)\"\n (loadImageFailed)=\"loadFailed.emit()\"\n />\n\n @if (isLocked()) {\n <label class=\"ul-image-cropper__zoom\">\n <span class=\"ul-image-cropper__zoom-label\">{{ zoomLabel() }}</span>\n <input\n type=\"range\"\n class=\"ul-image-cropper__zoom-input\"\n [min]=\"minZoom\"\n [max]=\"maxZoom\"\n [step]=\"zoomStep\"\n [value]=\"zoom()\"\n (input)=\"onZoomInput($event)\"\n />\n </label>\n }\n </div>\n }\n\n <div class=\"ul-modal__foter-content\" ul-modal-footer>\n <ul-button\n theme=\"fill-purple\"\n size=\"md\"\n [disabled]=\"!cropperReady()\"\n (buttonClick)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </ul-button>\n </div>\n</ul-modal>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-image-cropper{display:flex;flex-direction:column;align-items:center;gap:16px}.ul-image-cropper__frame{display:block;width:100%;height:320px;--cropper-overlay-color: rgba(10, 10, 10, .85);--cropper-outline-color: rgba(10, 10, 10, .85);--cropper-color: #6f3de0;--cropper-border: 2px solid #6f3de0;--cropper-hover-border: 2px solid #6f3de0;--cropper-focus-border: 2px solid #6f3de0;--cropper-resize-square-bg: #6f3de0;--cropper-resize-square-border: 1px solid #0a0a0a}.ul-image-cropper__zoom{display:flex;align-items:center;gap:12px;width:100%}.ul-image-cropper__zoom-label{font-family:Ultra Nunito Sans;line-height:20px;font-size:14px;letter-spacing:.14px;font-weight:400;color:#ffffffb3;flex-shrink:0}.ul-image-cropper__zoom-input{flex:1;accent-color:#6f3de0}.ul-modal__foter-content{width:100%;margin-top:32px}\n"] }]
2977
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], imageFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageFile", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], maxOutputSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxOutputSize", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], zoomLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomLabel", required: false }] }], frameAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "frameAriaLabel", required: false }] }], cropped: [{ type: i0.Output, args: ["cropped"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], loadFailed: [{ type: i0.Output, args: ["loadFailed"] }], cropperRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ImageCropperComponent$1), { isSignal: true }] }] } });
2894
2978
 
2895
2979
  /**
2896
2980
  * A reusable input component that follows UDS design system guidelines.
@@ -3313,7 +3397,7 @@ class NavbarComponent {
3313
3397
  this.sidebarToggle.emit();
3314
3398
  }
3315
3399
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NavbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3316
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: NavbarComponent, isStandalone: true, selector: "ul-navbar", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, logoHref: { classPropertyName: "logoHref", publicName: "logoHref", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoAlt: { classPropertyName: "logoAlt", publicName: "logoAlt", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarAlt: { classPropertyName: "avatarAlt", publicName: "avatarAlt", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, showSidebarToggle: { classPropertyName: "showSidebarToggle", publicName: "showSidebarToggle", isSignal: true, isRequired: false, transformFunction: null }, searchCollapsible: { classPropertyName: "searchCollapsible", publicName: "searchCollapsible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sidebarToggle: "sidebarToggle" }, queries: [{ propertyName: "logoSlot", first: true, predicate: NavbarLogoSlotDirective, descendants: true, isSignal: true }, { propertyName: "searchSlot", first: true, predicate: NavbarSearchSlotDirective, descendants: true, isSignal: true }, { propertyName: "avatarSlot", first: true, predicate: NavbarAvatarSlotDirective, descendants: true, isSignal: true }, { propertyName: "appNameSlot", first: true, predicate: NavbarAppNameSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<nav class=\"ul-navbar\" role=\"navigation\">\n <div class=\"ul-navbar__bar\">\n <div class=\"ul-navbar__logo\">\n @if (showToggle()) {\n <!-- Default 'md' size, matching the search toggle below and the\n sidebar's own close button: `sm` capped the icon at 20px (the\n `.ul-btn--sm .ul-icon` rule outranks `.ul-icon-size-*`, so the icon's\n own size input cannot raise it) and gave a 32px tap target. -->\n <ul-button\n theme=\"ghost-white\"\n [iconOnly]=\"true\"\n class=\"ul-navbar__sidebar_toggle\"\n ariaLabel=\"Open menu\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n (buttonClick)=\"toggleSidebar()\"\n >\n <ul-icon icon=\"menu\" size=\"6\" />\n </ul-button>\n }\n @if (hasLogoSlot()) {\n <ng-content select=\"[ul-navbar-logo]\" />\n } @else {\n <img [src]=\"logoSrc()\" [alt]=\"logoAlt()\" class=\"ul-navbar__logo-img\" height=\"64\" />\n @if (hasAppNameSlot()) {\n <a [href]=\"logoHref()\" class=\"ul-navbar__logo-link ul-navbar__logo-text\">\n <ng-content select=\"[ul-navbar-app-name]\" />\n </a>\n }\n }\n </div>\n\n @if (!isMinimal()) {\n @if (showSearchToggle()) {\n <ul-button\n theme=\"ghost-white\"\n [iconOnly]=\"true\"\n class=\"ul-navbar__search-toggle\"\n ariaLabel=\"Toggle search\"\n [attr.aria-expanded]=\"searchExpanded()\"\n (buttonClick)=\"toggleSearch()\"\n >\n <ul-icon icon=\"search\" size=\"6\" />\n </ul-button>\n }\n\n <div\n class=\"ul-navbar__search-wrapper\"\n [class.ul-navbar__search-wrapper--expanded]=\"searchExpanded() || !searchCollapsible()\"\n >\n <ng-content select=\"[ul-navbar-search]\" />\n </div>\n\n <div class=\"ul-navbar__avatar\">\n @if (hasAvatarSlot()) {\n <ng-content select=\"[ul-navbar-avatar]\" />\n } @else if (avatarSrc() || avatarInitials()) {\n <ul-avatar\n [src]=\"avatarSrc()\"\n [initials]=\"avatarInitials()\"\n [alt]=\"avatarAlt()\"\n size=\"md\"\n />\n }\n </div>\n }\n </div>\n</nav>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-navbar{width:100%}.ul-navbar__bar{min-height:48px;display:flex;align-items:center;gap:16px;padding:8px 16px;background-color:#0a0a0a;border-bottom:1px solid rgba(255,255,255,.1)}@media(min-width:1024px){.ul-navbar__bar{padding:12px 24px;gap:24px}}@media(min-width:1280px){.ul-navbar__bar{padding:12px 32px}}.ul-navbar__bar{flex-wrap:wrap}@media(max-width:1279px){.ul-navbar__bar{position:relative;z-index:100}}@media(max-width:1023px){.ul-navbar__sidebar_toggle{display:inline-flex}}.ul-navbar__sidebar_toggle .ul-icon,.ul-navbar__sidebar_toggle .ul-icon:before{background-color:transparent}.ul-navbar__sidebar_toggle :focus-visible{outline:none;box-shadow:0 0 0 2px #ffffff1a}@media(min-width:1024px){.ul-navbar__sidebar_toggle{display:none}}.ul-navbar__logo{display:flex;align-items:center;flex-shrink:0;margin-right:auto;gap:8px;min-width:0}.ul-navbar__logo-link{display:flex;align-items:center;color:inherit;text-decoration:none}.ul-navbar__logo-text{font-family:Ultra Brand;line-height:36px;font-size:28px;letter-spacing:2.8px;font-weight:1000;text-transform:uppercase;color:inherit;display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(min-width:1024px){.ul-navbar__logo-text{display:none}}.ul-navbar__logo-img{display:block;width:auto;object-fit:contain}@media(max-width:1023px){.ul-navbar__search-toggle{display:inline-flex}}@media(min-width:1024px){.ul-navbar__search-toggle{display:none}}.ul-navbar__search-wrapper{flex:1;min-width:0;display:flex;align-items:center}@media(max-width:1023px){.ul-navbar__search-wrapper{flex-basis:100%;order:10;max-height:0;overflow:hidden;opacity:0;transition:max-height .25s cubic-bezier(.4,0,.2,1),opacity .25s cubic-bezier(.4,0,.2,1)}.ul-navbar__search-wrapper--expanded{max-height:80px;opacity:1}}@media(min-width:1024px){.ul-navbar__search-wrapper{flex-basis:auto;order:0;max-height:none;opacity:1}}.ul-navbar__avatar{display:flex;align-items:center;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: AvatarComponent, selector: "ul-avatar", inputs: ["size", "src", "initials", "icon", "alt", "loading"] }, { kind: "component", type: ButtonComponent, selector: "ul-button", inputs: ["size", "theme", "iconOnly", "vertical", "disabled", "type", "ariaLabel"], outputs: ["buttonClick"] }, { kind: "component", type: IconComponent, selector: "ul-icon", inputs: ["size", "weight", "icon"] }], encapsulation: i0.ViewEncapsulation.None });
3400
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: NavbarComponent, isStandalone: true, selector: "ul-navbar", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, logoHref: { classPropertyName: "logoHref", publicName: "logoHref", isSignal: true, isRequired: false, transformFunction: null }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoAlt: { classPropertyName: "logoAlt", publicName: "logoAlt", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarAlt: { classPropertyName: "avatarAlt", publicName: "avatarAlt", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, showSidebarToggle: { classPropertyName: "showSidebarToggle", publicName: "showSidebarToggle", isSignal: true, isRequired: false, transformFunction: null }, searchCollapsible: { classPropertyName: "searchCollapsible", publicName: "searchCollapsible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sidebarToggle: "sidebarToggle" }, queries: [{ propertyName: "logoSlot", first: true, predicate: NavbarLogoSlotDirective, descendants: true, isSignal: true }, { propertyName: "searchSlot", first: true, predicate: NavbarSearchSlotDirective, descendants: true, isSignal: true }, { propertyName: "avatarSlot", first: true, predicate: NavbarAvatarSlotDirective, descendants: true, isSignal: true }, { propertyName: "appNameSlot", first: true, predicate: NavbarAppNameSlotDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<nav class=\"ul-navbar\" role=\"navigation\">\n <div class=\"ul-navbar__bar\">\n <div class=\"ul-navbar__logo\">\n @if (showToggle()) {\n <!-- Default 'md' size, matching the search toggle below and the\n sidebar's own close button: `sm` capped the icon at 20px (the\n `.ul-btn--sm .ul-icon` rule outranks `.ul-icon-size-*`, so the icon's\n own size input cannot raise it) and gave a 32px tap target. -->\n <ul-button\n theme=\"ghost-white\"\n [iconOnly]=\"true\"\n class=\"ul-navbar__sidebar_toggle\"\n ariaLabel=\"Open menu\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n (buttonClick)=\"toggleSidebar()\"\n >\n <ul-icon icon=\"menu\" size=\"6\" />\n </ul-button>\n }\n @if (hasLogoSlot()) {\n <ng-content select=\"[ul-navbar-logo]\" />\n } @else {\n <img [src]=\"logoSrc()\" [alt]=\"logoAlt()\" class=\"ul-navbar__logo-img\" height=\"64\" />\n @if (hasAppNameSlot()) {\n <a [href]=\"logoHref()\" class=\"ul-navbar__logo-link ul-navbar__logo-text\">\n <ng-content select=\"[ul-navbar-app-name]\" />\n </a>\n }\n }\n </div>\n\n @if (!isMinimal()) {\n @if (showSearchToggle()) {\n <ul-button\n theme=\"ghost-white\"\n [iconOnly]=\"true\"\n class=\"ul-navbar__search-toggle\"\n ariaLabel=\"Toggle search\"\n [attr.aria-expanded]=\"searchExpanded()\"\n (buttonClick)=\"toggleSearch()\"\n >\n <ul-icon icon=\"search\" size=\"6\" />\n </ul-button>\n }\n\n <div\n class=\"ul-navbar__search-wrapper\"\n [class.ul-navbar__search-wrapper--expanded]=\"searchExpanded() || !searchCollapsible()\"\n >\n <ng-content select=\"[ul-navbar-search]\" />\n </div>\n\n <div class=\"ul-navbar__avatar\">\n @if (hasAvatarSlot()) {\n <ng-content select=\"[ul-navbar-avatar]\" />\n } @else if (avatarSrc() || avatarInitials()) {\n <ul-avatar\n [src]=\"avatarSrc()\"\n [initials]=\"avatarInitials()\"\n [alt]=\"avatarAlt()\"\n size=\"md\"\n />\n }\n </div>\n }\n </div>\n</nav>\n", styles: [".ul-rich-text{font-family:Ultra Nunito Sans;line-height:28px;font-size:18px;letter-spacing:0px;font-weight:400;color:#fff}.ul-rich-text p,.ul-rich-text ul,.ul-rich-text ol,.ul-rich-text blockquote,.ul-rich-text pre{margin-top:0;margin-bottom:16px}.ul-rich-text:last-child,.ul-rich-text p:last-child,.ul-rich-text ul:last-child,.ul-rich-text ol:last-child,.ul-rich-text blockquote:last-child,.ul-rich-text pre:last-child{margin-bottom:0}.ul-rich-text h1,.ul-rich-text h2,.ul-rich-text h3,.ul-rich-text h4,.ul-rich-text h5,.ul-rich-text h6{margin-top:32px;margin-bottom:12px;font-weight:1000}.ul-rich-text h1{font-family:Ultra Nunito Sans;line-height:48px;font-size:48px;letter-spacing:-.48px;font-weight:1000}.ul-rich-text h2{font-family:Ultra Nunito Sans;line-height:48px;font-size:40px;letter-spacing:-.4px;font-weight:1000}.ul-rich-text h3{font-family:Ultra Nunito Sans;line-height:44px;font-size:36px;letter-spacing:-.36px;font-weight:1000}.ul-rich-text h4{font-family:Ultra Nunito Sans;line-height:40px;font-size:32px;letter-spacing:-.32px;font-weight:1000}.ul-rich-text h5{font-family:Ultra Nunito Sans;line-height:36px;font-size:28px;letter-spacing:-.28px;font-weight:1000}.ul-rich-text h6{font-family:Ultra Nunito Sans;line-height:36px;font-size:24px;letter-spacing:-.24px;font-weight:1000}.ul-rich-text ul,.ul-rich-text ol{padding-left:24px}.ul-rich-text ul ul,.ul-rich-text ul ol,.ul-rich-text ol ul,.ul-rich-text ol ol{margin-top:8px;margin-bottom:8px}.ul-rich-text li{margin-top:4px;margin-bottom:4px}.ul-rich-text strong{font-weight:800}.ul-rich-text code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text em{font-style:italic}.ul-rich-text a{color:#c2a8ff;text-decoration:underline;text-decoration-thickness:1px}.ul-rich-text a:hover{color:#d6c5ff}.ul-rich-text a:active{color:#a781ff}.ul-rich-text pre{padding:16px;border-radius:8px;background-color:#3b3b3b;overflow-x:auto}.ul-rich-text pre code{font-family:Roboto Mono;line-height:20px;font-size:14px;letter-spacing:0px;font-weight:400}.ul-rich-text blockquote{margin-left:0;padding-left:16px;border-left-width:2px;border-left-style:solid;border-left-color:#ffffff1a;color:#ffffffb3}.ul-rich-text--muted{color:#ffffffb3}.ul-rich-text--inverse,.ul-rich-text--inverse a{color:#000}.ul-rich-text--inverse a:hover,.ul-rich-text--inverse a:active{color:#000000b3}.ul-rich-text--body-m{font-family:Ultra Nunito Sans;line-height:24px;font-size:16px;letter-spacing:0px;font-weight:400}.ul-rich-text--compact p,.ul-rich-text--compact ul,.ul-rich-text--compact ol,.ul-rich-text--compact blockquote,.ul-rich-text--compact pre{margin-bottom:8px}.ul-rich-text--compact h1,.ul-rich-text--compact h2,.ul-rich-text--compact h3,.ul-rich-text--compact h4,.ul-rich-text--compact h5,.ul-rich-text--compact h6{margin-top:16px;margin-bottom:8px}.ul-navbar{width:100%}.ul-navbar__bar{min-height:48px;display:flex;align-items:center;gap:16px;padding:8px 16px;background-color:#0a0a0a;border-bottom:1px solid rgba(255,255,255,.1)}@media(min-width:1024px){.ul-navbar__bar{padding:12px 24px;gap:24px}}@media(min-width:1280px){.ul-navbar__bar{padding:12px 32px}}.ul-navbar__bar{flex-wrap:wrap}@media(max-width:1279px){.ul-navbar__bar{position:relative;z-index:100}}@media(max-width:1023px){.ul-navbar__sidebar_toggle{display:inline-flex}}.ul-navbar__sidebar_toggle .ul-icon,.ul-navbar__sidebar_toggle .ul-icon:before{background-color:transparent}.ul-navbar__sidebar_toggle :focus-visible{outline:none;box-shadow:0 0 0 2px #ffffff1a}@media(min-width:1024px){.ul-navbar__sidebar_toggle{display:none}}.ul-navbar__logo{display:flex;align-items:center;flex-shrink:0;margin-right:auto;gap:8px;min-width:0}.ul-navbar__logo-link{display:flex;align-items:center;color:inherit;text-decoration:none}.ul-navbar__logo-text{font-family:Ultra Brand;line-height:36px;font-size:28px;letter-spacing:2.8px;font-weight:1000;text-transform:uppercase;color:inherit;display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(min-width:1024px){.ul-navbar__logo-text{display:none}}.ul-navbar__logo-img{display:block;width:auto;object-fit:contain}@media(max-width:1023px){.ul-navbar__search-toggle{display:inline-flex}}@media(min-width:1024px){.ul-navbar__search-toggle{display:none}}.ul-navbar__search-wrapper{flex:1;min-width:0;display:flex;align-items:center}@media(max-width:1023px){.ul-navbar__search-wrapper{flex-basis:100%;order:10;max-height:0;overflow:hidden;opacity:0;transition:max-height .25s cubic-bezier(.4,0,.2,1),opacity .25s cubic-bezier(.4,0,.2,1)}.ul-navbar__search-wrapper--expanded{max-height:80px;opacity:1}}@media(min-width:1024px){.ul-navbar__search-wrapper{flex-basis:auto;order:0;max-height:none;opacity:1}}.ul-navbar__avatar{display:flex;align-items:center;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: AvatarComponent, selector: "ul-avatar", inputs: ["size", "src", "initials", "icon", "alt", "loading", "editable"] }, { kind: "component", type: ButtonComponent, selector: "ul-button", inputs: ["size", "theme", "iconOnly", "vertical", "disabled", "type", "ariaLabel"], outputs: ["buttonClick"] }, { kind: "component", type: IconComponent, selector: "ul-icon", inputs: ["size", "weight", "icon"] }], encapsulation: i0.ViewEncapsulation.None });
3317
3401
  }
3318
3402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: NavbarComponent, decorators: [{
3319
3403
  type: Component,