@3ddv/software-division-components 2.0.10 → 2.0.14

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.
@@ -1,16 +1,17 @@
1
1
  import { NgOptimizedImage, NgClass, CommonModule } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, viewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { input, viewChild, effect, ChangeDetectionStrategy, Component } from '@angular/core';
4
4
  import { createTimeline } from 'animejs';
5
5
  import { mergeMap, skip, tap, merge, catchError } from 'rxjs';
6
6
 
7
7
  class MapLoaderComponent {
8
8
  // INPUTS
9
- viewerService = input.required(...(ngDevMode ? [{ debugName: "viewerService" }] : []));
9
+ viewerService = input(...(ngDevMode ? [undefined, { debugName: "viewerService" }] : []));
10
10
  logo = input.required(...(ngDevMode ? [{ debugName: "logo" }] : []));
11
11
  color = input('255 165 0', ...(ngDevMode ? [{ debugName: "color" }] : []));
12
12
  duration = input(500, ...(ngDevMode ? [{ debugName: "duration" }] : []));
13
13
  size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
14
+ isLoading = input(null, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
14
15
  // SIGNALS
15
16
  pill = viewChild('pill', ...(ngDevMode ? [{ debugName: "pill" }] : []));
16
17
  pillContainer = viewChild('pillContainer', ...(ngDevMode ? [{ debugName: "pillContainer" }] : []));
@@ -19,6 +20,20 @@ class MapLoaderComponent {
19
20
  isFirstLoad = true;
20
21
  varName = '--ripple-color';
21
22
  handlers = [];
23
+ _isLoading = effect(() => {
24
+ // If we use the service instead of the external loading state, do not execute loader
25
+ if (this.isLoading() == null) {
26
+ return;
27
+ }
28
+ // Here we start with the loader hidden by default
29
+ this.container().nativeElement.classList.add('hidden');
30
+ if (this.isLoading() === true) {
31
+ this.showLoader();
32
+ }
33
+ else if (this.isLoading() === false) {
34
+ this.hideLoader();
35
+ }
36
+ }, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
22
37
  // GETTERS
23
38
  /**
24
39
  * Retorna la variable CSS para el color del ripple.
@@ -105,29 +120,34 @@ class MapLoaderComponent {
105
120
  * para futuras ocasiones y delegamos en los handlers start y success para mostrar u ocultar el loader respectivamente.
106
121
  */
107
122
  initComponent() {
108
- const subscription = this.viewerService()
109
- .waitInitialize()
110
- .pipe(mergeMap(() => {
111
- // when initialized, we listen to both load_start and load_success
112
- const loadStart$ = this.viewerService()
113
- .getObservable('load_start')
114
- .pipe(skip(1), // skip first emission
115
- tap(() => this.showLoader()));
116
- const loadSuccess$ = this.viewerService()
117
- .getObservable('load_success')
118
- .pipe(tap(() => {
119
- if (this.isFirstLoad) {
120
- this.isFirstLoad = false;
121
- }
122
- }), tap(() => this.handleLoadSuccess()));
123
- // merge both into one stream
124
- return merge(loadStart$, loadSuccess$);
125
- }), catchError((error) => {
126
- console.error('viewer init error', error);
127
- return []; // swallow error or return EMPTY
128
- }))
129
- .subscribe();
130
- this.handlers.push(subscription);
123
+ if (this.isLoading() !== null) {
124
+ return;
125
+ }
126
+ if (typeof this.viewerService() !== 'undefined') {
127
+ const subscription = this.viewerService()
128
+ .waitInitialize()
129
+ .pipe(mergeMap(() => {
130
+ // when initialized, we listen to both load_start and load_success
131
+ const loadStart$ = this.viewerService()
132
+ .getObservable('load_start')
133
+ .pipe(skip(1), // skip first emission
134
+ tap(() => this.showLoader()));
135
+ const loadSuccess$ = this.viewerService()
136
+ .getObservable('load_success')
137
+ .pipe(tap(() => {
138
+ if (this.isFirstLoad) {
139
+ this.isFirstLoad = false;
140
+ }
141
+ }), tap(() => this.handleLoadSuccess()));
142
+ // merge both into one stream
143
+ return merge(loadStart$, loadSuccess$);
144
+ }), catchError((error) => {
145
+ console.error('viewer init error', error);
146
+ return []; // swallow error or return EMPTY
147
+ }))
148
+ .subscribe();
149
+ this.handlers.push(subscription);
150
+ }
131
151
  }
132
152
  formatRgb(rgbString, wrapped = true) {
133
153
  return wrapped ? 'rgb(' + rgbString.split(' ').join(', ') + ')' : rgbString.split(' ').join(', ');
@@ -142,12 +162,12 @@ class MapLoaderComponent {
142
162
  }
143
163
  }
144
164
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: MapLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
145
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: MapLoaderComponent, isStandalone: true, selector: "sdc-map-loader", inputs: { viewerService: { classPropertyName: "viewerService", publicName: "viewerService", isSignal: true, isRequired: true, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "pill", first: true, predicate: ["pill"], descendants: true, isSignal: true }, { propertyName: "pillContainer", first: true, predicate: ["pillContainer"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #container class=\"container\" [style]=\"ripple\">\n <!-- Container -->\n <div #pillContainer class=\"pill-container\">\n <!-- PILL -->\n <div #pill class=\"pill ripple-animation\">\n <!-- LOGO -->\n <img\n [ngClass]=\"size() === 'sm' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"70\"\n priority\n width=\"50\"\n [ngSrc]=\"logo()\" />\n <img\n [ngClass]=\"size() === 'md' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"90\"\n priority\n width=\"70\"\n [ngSrc]=\"logo()\" />\n </div>\n </div>\n</div>\n", styles: [".ripple-animation{animation:ripple 1s linear infinite}@-webkit-keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1),0 0 0 50px rgba(var(--ripple-color),0)}}@keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),0)}}.container{position:relative;height:100%;width:100%;max-width:100%;overflow:visible}.pill-container{position:absolute;z-index:20;display:flex;align-items:center;justify-content:center;height:100%;width:100%}.pill{display:flex;align-items:center;justify-content:center;width:8rem;height:8rem;border-radius:50%;background-color:#111827e6}@media(min-width:1024px){.pill{width:12rem;height:12rem}}@media(min-width:1920px){.pill{width:13.5rem;height:13.5rem}}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
165
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: MapLoaderComponent, isStandalone: true, selector: "sdc-map-loader", inputs: { viewerService: { classPropertyName: "viewerService", publicName: "viewerService", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: true, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, duration: { classPropertyName: "duration", publicName: "duration", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "pill", first: true, predicate: ["pill"], descendants: true, isSignal: true }, { propertyName: "pillContainer", first: true, predicate: ["pillContainer"], descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #container class=\"container\" [style]=\"ripple\">\n <!-- Container -->\n <div #pillContainer class=\"pill-container\">\n <!-- PILL -->\n <div #pill class=\"pill ripple-animation\">\n <!-- LOGO -->\n <img\n [ngClass]=\"size() === 'sm' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"70\"\n priority\n width=\"50\"\n [ngSrc]=\"logo()\" />\n <img\n [ngClass]=\"size() === 'md' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"90\"\n priority\n width=\"70\"\n [ngSrc]=\"logo()\" />\n </div>\n </div>\n</div>\n", styles: [".ripple-animation{animation:ripple 1s linear infinite}@-webkit-keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1),0 0 0 50px rgba(var(--ripple-color),0)}}@keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),0)}}.container{position:relative;height:100%;width:100%;max-width:100%;overflow:visible}.pill-container{position:absolute;z-index:20;display:flex;align-items:center;justify-content:center;height:100%;width:100%}.pill{display:flex;align-items:center;justify-content:center;width:8rem;height:8rem;border-radius:50%;background-color:#111827e6}@media(min-width:1024px){.pill{width:12rem;height:12rem}}@media(min-width:1920px){.pill{width:13.5rem;height:13.5rem}}\n"], dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
146
166
  }
147
167
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: MapLoaderComponent, decorators: [{
148
168
  type: Component,
149
169
  args: [{ selector: 'sdc-map-loader', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgOptimizedImage, NgClass, CommonModule], template: "<div #container class=\"container\" [style]=\"ripple\">\n <!-- Container -->\n <div #pillContainer class=\"pill-container\">\n <!-- PILL -->\n <div #pill class=\"pill ripple-animation\">\n <!-- LOGO -->\n <img\n [ngClass]=\"size() === 'sm' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"70\"\n priority\n width=\"50\"\n [ngSrc]=\"logo()\" />\n <img\n [ngClass]=\"size() === 'md' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"90\"\n priority\n width=\"70\"\n [ngSrc]=\"logo()\" />\n </div>\n </div>\n</div>\n", styles: [".ripple-animation{animation:ripple 1s linear infinite}@-webkit-keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),.1),0 0 0 40px rgba(var(--ripple-color),.1),0 0 0 50px rgba(var(--ripple-color),0)}}@keyframes ripple{0%{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1)}to{box-shadow:0 4px 10px rgba(var(--ripple-color),.1),0 0 0 5px rgba(var(--ripple-color),.1),0 0 0 10px rgba(var(--ripple-color),.1),0 0 0 20px rgba(var(--ripple-color),.1),0 0 0 30px rgba(var(--ripple-color),0)}}.container{position:relative;height:100%;width:100%;max-width:100%;overflow:visible}.pill-container{position:absolute;z-index:20;display:flex;align-items:center;justify-content:center;height:100%;width:100%}.pill{display:flex;align-items:center;justify-content:center;width:8rem;height:8rem;border-radius:50%;background-color:#111827e6}@media(min-width:1024px){.pill{width:12rem;height:12rem}}@media(min-width:1920px){.pill{width:13.5rem;height:13.5rem}}\n"] }]
150
- }], propDecorators: { viewerService: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewerService", required: true }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], duration: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], pill: [{ type: i0.ViewChild, args: ['pill', { isSignal: true }] }], pillContainer: [{ type: i0.ViewChild, args: ['pillContainer', { isSignal: true }] }], container: [{ type: i0.ViewChild, args: ['container', { isSignal: true }] }] } });
170
+ }], propDecorators: { viewerService: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewerService", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: true }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], duration: [{ type: i0.Input, args: [{ isSignal: true, alias: "duration", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], pill: [{ type: i0.ViewChild, args: ['pill', { isSignal: true }] }], pillContainer: [{ type: i0.ViewChild, args: ['pillContainer', { isSignal: true }] }], container: [{ type: i0.ViewChild, args: ['container', { isSignal: true }] }] } });
151
171
 
152
172
  /*
153
173
  * Public API Surface of software-division-components
@@ -1 +1 @@
1
- {"version":3,"file":"3ddv-software-division-components-dvm-map-loader.mjs","sources":["../../dvm/map-loader/map-loader.component.ts","../../dvm/map-loader/map-loader.component.html","../../dvm/map-loader/public-api.ts","../../dvm/map-loader/3ddv-software-division-components-dvm-map-loader.ts"],"sourcesContent":["import { CommonModule, NgClass, NgOptimizedImage } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, ElementRef, input, OnDestroy, OnInit, viewChild } from '@angular/core';\nimport { createTimeline } from 'animejs';\nimport { catchError, merge, mergeMap, Observable, skip, Subscription, tap } from 'rxjs';\n\n@Component({\n selector: 'sdc-map-loader',\n templateUrl: './map-loader.component.html',\n styleUrl: './map-loader.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgOptimizedImage, NgClass, CommonModule],\n})\nexport class MapLoaderComponent implements OnInit, OnDestroy {\n // INPUTS\n public viewerService = input.required<{\n waitInitialize: () => Observable<any>;\n getObservable: (value: 'load_success' | 'load_start') => Observable<any>;\n }>();\n public logo = input.required<string>();\n public color = input<string>('255 165 0');\n public duration = input<number>(500);\n public size = input<'sm' | 'md'>('md');\n\n // SIGNALS\n public pill = viewChild<ElementRef>('pill');\n public pillContainer = viewChild<ElementRef>('pillContainer');\n public container = viewChild<ElementRef>('container');\n\n // CLASS PROPERTIES\n private isFirstLoad = true;\n private varName = '--ripple-color';\n private readonly handlers: Subscription[] = [];\n\n // GETTERS\n /**\n * Retorna la variable CSS para el color del ripple.\n * Necesitamos setear la variable css --ripple-color con el color accent de la configuración.\n * Como este valor originalmente viene sin comas para ser usado en Tailwind, debemos reemplazar los espacios por comas.\n * Finalmente retornamos el valor y lo implementamos en el div contenedor del ripple para ser usado por el scss.\n */\n public get ripple(): string {\n const rgb = this.formatRgb(this.color(), false);\n\n return `${this.varName}:${rgb};`;\n }\n\n // LC METHODS\n public ngOnInit(): void {\n this.initComponent();\n }\n\n public ngOnDestroy(): void {\n this.destroyComponent();\n }\n\n // CLASS METHODS\n\n /**\n * Muestra el loader.\n * Mediante una animación de animejs, mostramos el loader.\n * Al comenzar, verificará si el contenedor tiene la clase hidden y la removerá si es así.\n */\n private showLoader(): void {\n const pill = this.pill();\n const container = this.container();\n const pillContainer = this.pillContainer();\n\n if (!container || !pill || !pillContainer) {\n return console.error('Loader could not be loaded');\n }\n\n const containerClasses: string = container.nativeElement.classList.value;\n\n if (containerClasses.includes('hidden')) {\n container.nativeElement.classList.remove('hidden');\n }\n\n createTimeline({\n duration: this.duration,\n })\n .add(pillContainer.nativeElement, {\n opacity: [0, 1],\n duration: 100,\n })\n .add(\n pill.nativeElement,\n {\n scale: [0, 1],\n opacity: [0, 1],\n },\n '+=10'\n );\n }\n\n /**\n * Oculta el loader.\n * Cuando termina aplica la clase hidden al contenedor para ocultarlo.\n */\n private hideLoader(): void {\n const pill = this.pill();\n const container = this.container();\n const pillContainer = this.pillContainer();\n\n if (!container || !pill || !pillContainer) {\n return console.error('Loader could not be hidden');\n }\n createTimeline({\n delay: 400,\n duration: this.duration,\n playbackEase: 'outQuad',\n onComplete: () => container.nativeElement.classList.add('hidden'),\n })\n .add(pill.nativeElement, {\n opacity: [1, 0],\n scale: [1, 0],\n })\n .add(pillContainer.nativeElement, {\n opacity: [1, 0],\n });\n }\n\n private handleLoadSuccess(): void {\n if (this.isFirstLoad) {\n this.hideLoader();\n return;\n }\n\n setTimeout(() => this.hideLoader(), this.duration());\n }\n\n /**\n * Inicializa el componente y sus handlers.\n * Haciendo uso de waitInitialize, esperamos a que el mapa se inicialice para poder suscribirnos a los eventos de carga del mapa.\n * Excepto en la carga inicial del mapa y para evitar superponer animaciones, skipeamos la primera carga y esperamos a que el mapa se recargue\n * para futuras ocasiones y delegamos en los handlers start y success para mostrar u ocultar el loader respectivamente.\n */\n private initComponent(): void {\n const subscription = this.viewerService()\n .waitInitialize()\n .pipe(\n mergeMap(() => {\n // when initialized, we listen to both load_start and load_success\n const loadStart$ = this.viewerService()\n .getObservable('load_start')\n .pipe(\n skip(1), // skip first emission\n tap(() => this.showLoader())\n );\n\n const loadSuccess$ = this.viewerService()\n .getObservable('load_success')\n .pipe(\n tap(() => {\n if (this.isFirstLoad) {\n this.isFirstLoad = false;\n }\n }),\n tap(() => this.handleLoadSuccess())\n );\n\n // merge both into one stream\n return merge(loadStart$, loadSuccess$);\n }),\n catchError((error: unknown) => {\n console.error('viewer init error', error);\n return []; // swallow error or return EMPTY\n })\n )\n .subscribe();\n\n this.handlers.push(subscription);\n }\n\n private formatRgb(rgbString: string, wrapped = true): string {\n return wrapped ? 'rgb(' + rgbString.split(' ').join(', ') + ')' : rgbString.split(' ').join(', ');\n }\n\n /**\n * Destruye el componente y sus handlers.\n * Recorre el array de handlers y se desuscribe de cada uno de ellos.\n */\n private destroyComponent(): void {\n for (const h of this.handlers) {\n h.unsubscribe();\n }\n }\n}\n","<div #container class=\"container\" [style]=\"ripple\">\n <!-- Container -->\n <div #pillContainer class=\"pill-container\">\n <!-- PILL -->\n <div #pill class=\"pill ripple-animation\">\n <!-- LOGO -->\n <img\n [ngClass]=\"size() === 'sm' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"70\"\n priority\n width=\"50\"\n [ngSrc]=\"logo()\" />\n <img\n [ngClass]=\"size() === 'md' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"90\"\n priority\n width=\"70\"\n [ngSrc]=\"logo()\" />\n </div>\n </div>\n</div>\n","/*\n * Public API Surface of software-division-components\n */\n\nexport * from './map-loader.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"3ddv-software-division-components-dvm-map-loader.mjs","sources":["../../dvm/map-loader/map-loader.component.ts","../../dvm/map-loader/map-loader.component.html","../../dvm/map-loader/public-api.ts","../../dvm/map-loader/3ddv-software-division-components-dvm-map-loader.ts"],"sourcesContent":["import { CommonModule, NgClass, NgOptimizedImage } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n effect,\n ElementRef,\n input,\n OnDestroy,\n OnInit,\n viewChild,\n} from '@angular/core';\nimport { createTimeline } from 'animejs';\nimport { catchError, merge, mergeMap, Observable, skip, Subscription, tap } from 'rxjs';\n\n@Component({\n selector: 'sdc-map-loader',\n templateUrl: './map-loader.component.html',\n styleUrl: './map-loader.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgOptimizedImage, NgClass, CommonModule],\n})\nexport class MapLoaderComponent implements OnInit, OnDestroy {\n // INPUTS\n public viewerService = input<{\n waitInitialize: () => Observable<any>;\n getObservable: (value: 'load_success' | 'load_start') => Observable<any>;\n }>();\n\n public logo = input.required<string>();\n public color = input<string>('255 165 0');\n public duration = input<number>(500);\n public size = input<'sm' | 'md'>('md');\n public isLoading = input<boolean | null>(null);\n\n // SIGNALS\n public pill = viewChild<ElementRef>('pill');\n public pillContainer = viewChild<ElementRef>('pillContainer');\n public container = viewChild<ElementRef>('container');\n\n // CLASS PROPERTIES\n private isFirstLoad = true;\n private varName = '--ripple-color';\n private readonly handlers: Subscription[] = [];\n\n private _isLoading = effect(() => {\n // If we use the service instead of the external loading state, do not execute loader\n if (this.isLoading() == null) {\n return;\n }\n // Here we start with the loader hidden by default\n this.container()!.nativeElement.classList.add('hidden');\n\n if (this.isLoading() === true) {\n this.showLoader();\n } else if (this.isLoading() === false) {\n this.hideLoader();\n }\n });\n\n // GETTERS\n /**\n * Retorna la variable CSS para el color del ripple.\n * Necesitamos setear la variable css --ripple-color con el color accent de la configuración.\n * Como este valor originalmente viene sin comas para ser usado en Tailwind, debemos reemplazar los espacios por comas.\n * Finalmente retornamos el valor y lo implementamos en el div contenedor del ripple para ser usado por el scss.\n */\n public get ripple(): string {\n const rgb = this.formatRgb(this.color(), false);\n\n return `${this.varName}:${rgb};`;\n }\n\n // LC METHODS\n public ngOnInit(): void {\n this.initComponent();\n }\n\n public ngOnDestroy(): void {\n this.destroyComponent();\n }\n\n // CLASS METHODS\n\n /**\n * Muestra el loader.\n * Mediante una animación de animejs, mostramos el loader.\n * Al comenzar, verificará si el contenedor tiene la clase hidden y la removerá si es así.\n */\n private showLoader(): void {\n const pill = this.pill();\n const container = this.container();\n const pillContainer = this.pillContainer();\n\n if (!container || !pill || !pillContainer) {\n return console.error('Loader could not be loaded');\n }\n\n const containerClasses: string = container.nativeElement.classList.value;\n\n if (containerClasses.includes('hidden')) {\n container.nativeElement.classList.remove('hidden');\n }\n\n createTimeline({\n duration: this.duration,\n })\n .add(pillContainer.nativeElement, {\n opacity: [0, 1],\n duration: 100,\n })\n .add(\n pill.nativeElement,\n {\n scale: [0, 1],\n opacity: [0, 1],\n },\n '+=10'\n );\n }\n\n /**\n * Oculta el loader.\n * Cuando termina aplica la clase hidden al contenedor para ocultarlo.\n */\n private hideLoader(): void {\n const pill = this.pill();\n const container = this.container();\n const pillContainer = this.pillContainer();\n\n if (!container || !pill || !pillContainer) {\n return console.error('Loader could not be hidden');\n }\n createTimeline({\n delay: 400,\n duration: this.duration,\n playbackEase: 'outQuad',\n onComplete: () => container.nativeElement.classList.add('hidden'),\n })\n .add(pill.nativeElement, {\n opacity: [1, 0],\n scale: [1, 0],\n })\n .add(pillContainer.nativeElement, {\n opacity: [1, 0],\n });\n }\n\n private handleLoadSuccess(): void {\n if (this.isFirstLoad) {\n this.hideLoader();\n return;\n }\n\n setTimeout(() => this.hideLoader(), this.duration());\n }\n\n /**\n * Inicializa el componente y sus handlers.\n * Haciendo uso de waitInitialize, esperamos a que el mapa se inicialice para poder suscribirnos a los eventos de carga del mapa.\n * Excepto en la carga inicial del mapa y para evitar superponer animaciones, skipeamos la primera carga y esperamos a que el mapa se recargue\n * para futuras ocasiones y delegamos en los handlers start y success para mostrar u ocultar el loader respectivamente.\n */\n private initComponent(): void {\n if (this.isLoading() !== null) {\n return;\n }\n\n if (typeof this.viewerService() !== 'undefined') {\n const subscription = this.viewerService()!\n .waitInitialize()\n .pipe(\n mergeMap(() => {\n // when initialized, we listen to both load_start and load_success\n const loadStart$ = this.viewerService()!\n .getObservable('load_start')\n .pipe(\n skip(1), // skip first emission\n tap(() => this.showLoader())\n );\n\n const loadSuccess$ = this.viewerService()!\n .getObservable('load_success')\n .pipe(\n tap(() => {\n if (this.isFirstLoad) {\n this.isFirstLoad = false;\n }\n }),\n tap(() => this.handleLoadSuccess())\n );\n\n // merge both into one stream\n return merge(loadStart$, loadSuccess$);\n }),\n catchError((error: unknown) => {\n console.error('viewer init error', error);\n return []; // swallow error or return EMPTY\n })\n )\n .subscribe();\n this.handlers.push(subscription);\n }\n }\n\n private formatRgb(rgbString: string, wrapped = true): string {\n return wrapped ? 'rgb(' + rgbString.split(' ').join(', ') + ')' : rgbString.split(' ').join(', ');\n }\n\n /**\n * Destruye el componente y sus handlers.\n * Recorre el array de handlers y se desuscribe de cada uno de ellos.\n */\n private destroyComponent(): void {\n for (const h of this.handlers) {\n h.unsubscribe();\n }\n }\n}\n","<div #container class=\"container\" [style]=\"ripple\">\n <!-- Container -->\n <div #pillContainer class=\"pill-container\">\n <!-- PILL -->\n <div #pill class=\"pill ripple-animation\">\n <!-- LOGO -->\n <img\n [ngClass]=\"size() === 'sm' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"70\"\n priority\n width=\"50\"\n [ngSrc]=\"logo()\" />\n <img\n [ngClass]=\"size() === 'md' ? 'block' : 'hidden'\"\n alt=\"Club logo loader\"\n height=\"90\"\n priority\n width=\"70\"\n [ngSrc]=\"logo()\" />\n </div>\n </div>\n</div>\n","/*\n * Public API Surface of software-division-components\n */\n\nexport * from './map-loader.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ class NeighborsComponent {
8
8
  currentSection3d;
9
9
  leftSectionElement;
10
10
  rightSectionElement;
11
- // SERVICES
11
+ // SERVICES changes
12
12
  cdr = inject(ChangeDetectorRef);
13
13
  // REQUIRED INPUTS
14
14
  neighborsData = input.required(...(ngDevMode ? [{ debugName: "neighborsData" }] : []));
@@ -1 +1 @@
1
- {"version":3,"file":"3ddv-software-division-components-dvm-neighbors.mjs","sources":["../../dvm/neighbors/neighbors.component.ts","../../dvm/neighbors/neighbors.component.html","../../dvm/neighbors/3ddv-software-division-components-dvm-neighbors.ts"],"sourcesContent":["import { ThemeClass } from '@3ddv/software-division-components/shared';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n computed,\r\n ElementRef,\r\n EventEmitter,\r\n inject,\r\n input,\r\n OnDestroy,\r\n Output,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { Subscription } from 'rxjs';\r\nimport { Direction, HasAvailabilityFn, NeighborsData, SectionChangeEvent, TranslateSectionIdFn } from './types';\r\n\r\n@Component({\r\n selector: 'sdc-neighbors',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './neighbors.component.html',\r\n styleUrl: './neighbors.component.css',\r\n})\r\nexport class NeighborsComponent implements OnDestroy {\r\n // VIEWCHILD\r\n @ViewChild('neighborsContainer')\r\n public readonly neighborsContainer!: ElementRef<HTMLDivElement>;\r\n\r\n @ViewChild('currentSection', { static: false })\r\n public readonly currentSection!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('currentSection3d', { static: false })\r\n public readonly currentSection3d!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('leftSectionNumber', { static: false })\r\n public readonly leftSectionElement!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('rightSectionNumber', { static: false })\r\n public readonly rightSectionElement!: ElementRef<HTMLSpanElement>;\r\n\r\n // SERVICES\r\n private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef);\r\n\r\n // REQUIRED INPUTS\r\n public readonly neighborsData = input.required<NeighborsData>();\r\n public readonly currentSectionId = input.required<string | null>();\r\n public readonly translateTdcToMmc = input.required<TranslateSectionIdFn>();\r\n public readonly translateMmcToTdc = input.required<TranslateSectionIdFn>();\r\n public readonly hasAvailability = input.required<HasAvailabilityFn>();\r\n\r\n // OPTIONAL INPUTS\r\n public readonly isLoading = input<boolean>(false);\r\n public readonly showElement = input<boolean>(true);\r\n public readonly modeLr = input<boolean>(false);\r\n public readonly className = input<string>('');\r\n public readonly theme = input<ThemeClass>('theme-sdc');\r\n\r\n // OUTPUTS\r\n @Output() sectionChange = new EventEmitter<SectionChangeEvent>();\r\n\r\n /**\r\n * Computed class string that combines theme and user classes.\r\n */\r\n protected readonly computedClass = computed(() => {\r\n const themeClass = this.theme();\r\n const className = this.className();\r\n return Array.from(new Set(['sdc-neighbors', themeClass, className]))\r\n .filter(Boolean)\r\n .join(' ');\r\n });\r\n\r\n // COMPONENT STATE\r\n private readonly subscriptions: Subscription[] = [];\r\n\r\n // COMPUTED STATE\r\n protected readonly leftSectionId = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return null;\r\n return this.findAvailableNeighbor('prev', currentId);\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly rightSectionId = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return null;\r\n return this.findAvailableNeighbor('next', currentId);\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly currentSectionLabel = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return 'Loading';\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(currentId);\r\n if (!mmcId) return currentId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID (assuming format like \"S_31\" or similar)\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return 'Loading';\r\n }\r\n });\r\n\r\n protected readonly leftSectionLabel = computed(() => {\r\n try {\r\n const leftId = this.leftSectionId();\r\n if (!leftId) return null;\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(leftId);\r\n if (!mmcId) return leftId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly rightSectionLabel = computed(() => {\r\n try {\r\n const rightId = this.rightSectionId();\r\n if (!rightId) return null;\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(rightId);\r\n if (!mmcId) return rightId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n // NEIGHBOR RESOLUTION ALGORITHM\r\n private findAvailableNeighbor(type: Direction, currentSectionTdcId: string): string | null {\r\n try {\r\n const neighborKey = type === 'prev' ? 'l' : 'r';\r\n const noNeighborValue = 'none';\r\n\r\n // 1. Translate TDC → MMC\r\n const currentSectionMmcId = this.translateTdcToMmc()(currentSectionTdcId);\r\n if (!currentSectionMmcId) return null;\r\n\r\n // 2. Look up neighbor in data\r\n const neighbors = this.neighborsData();\r\n const neighbor = neighbors[currentSectionMmcId];\r\n const neighborSectionMmcId = neighbor ? neighbor[neighborKey] : noNeighborValue;\r\n\r\n // 3. Handle 'none' boundary\r\n if (neighborSectionMmcId === noNeighborValue) {\r\n return null;\r\n }\r\n\r\n // 4. Translate MMC → TDC\r\n const neighborSectionTdcId = this.translateMmcToTdc()(neighborSectionMmcId);\r\n if (!neighborSectionTdcId) return null;\r\n\r\n // 5. Check availability\r\n if (this.hasAvailability()(neighborSectionTdcId)) {\r\n return neighborSectionTdcId;\r\n }\r\n\r\n // 6. Recursively find next available (skip unavailable sections)\r\n return this.findAvailableNeighbor(type, neighborSectionTdcId);\r\n } catch (error) {\r\n return null;\r\n }\r\n }\r\n\r\n // GETTERS\r\n public get leftSection(): string | null {\r\n return this.leftSectionId();\r\n }\r\n\r\n public get rightSection(): string | null {\r\n return this.rightSectionId();\r\n }\r\n\r\n public get section(): string | null {\r\n return this.currentSectionLabel();\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.subscriptions.forEach((sub: Subscription): void => sub.unsubscribe());\r\n }\r\n\r\n // METHODS\r\n public navigateToSection(section: string): void {\r\n if (section === 'none' || !section) {\r\n return;\r\n }\r\n\r\n const leftId = this.leftSectionId();\r\n const rightId = this.rightSectionId();\r\n\r\n let direction: Direction;\r\n if (section === leftId) {\r\n direction = 'prev';\r\n } else if (section === rightId) {\r\n direction = 'next';\r\n } else {\r\n return;\r\n }\r\n\r\n this.sectionChange.emit({ direction, sectionId: section });\r\n }\r\n}\r\n","<div #neighborsContainer class=\"neighbors-container\" [class]=\"computedClass()\" [class.hidden]=\"!showElement()\">\n <!-- LEFT SECTION BUTTON -->\n @if (leftSection) {\n <div\n class=\"neighbor-button-left neighbor-button\"\n [class.is-none]=\"leftSection === 'none'\"\n tabindex=\"0\"\n (click)=\"navigateToSection(leftSection!)\"\n (keypress.enter)=\"navigateToSection(leftSection!)\">\n <!-- ARROW ICON -->\n <div class=\"neighbor-button-icon\">\n <i>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"3\"\n stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\n </svg>\n </i>\n </div>\n\n <!-- SECTION LABEL -->\n <div class=\"neighbor-button-label\">\n <p>\n <span class=\"prefix\"> {{ modeLr() ? 'Left Section' : 'Section' }}&nbsp; </span>\n @if (leftSection !== 'none' && !modeLr()) {\n <span #leftSectionNumber class=\"section-number\">\n {{ leftSectionLabel() }}\n </span>\n }\n </p>\n </div>\n </div>\n }\n\n <!-- CURRENT SECTION TEXT -->\n <div class=\"current-section-wrapper\">\n @if (isLoading()) {\n <p class=\"current-section-loading\">\n <span>Loading</span>\n </p>\n } @else {\n @if (section) {\n <h3 class=\"current-section-text\">\n <span class=\"prefix\">Section&nbsp;</span>\n <span #currentSection class=\"section-number\">{{ section }}</span>\n </h3>\n }\n }\n </div>\n\n <!-- RIGHT SECTION BUTTON -->\n @if (rightSection) {\n <div\n class=\"neighbor-button-right neighbor-button\"\n [class.is-none]=\"rightSection === 'none'\"\n tabindex=\"1\"\n (click)=\"navigateToSection(rightSection!)\"\n (keypress.enter)=\"navigateToSection(rightSection!)\">\n <!-- ARROW ICON -->\n <div class=\"neighbor-button-icon\">\n <i>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"3\"\n stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\n </svg>\n </i>\n </div>\n\n <!-- SECTION LABEL -->\n <div class=\"neighbor-button-label\">\n <p>\n <span class=\"prefix\"> {{ modeLr() ? 'Right Section' : 'Section' }}&nbsp; </span>\n @if (rightSection !== 'none' && !modeLr()) {\n <span #rightSectionNumber class=\"section-number\">\n {{ rightSectionLabel() }}\n </span>\n }\n </p>\n </div>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;MAwBa,kBAAkB,CAAA;;AAGb,IAAA,kBAAkB;AAGlB,IAAA,cAAc;AAGd,IAAA,gBAAgB;AAGhB,IAAA,kBAAkB;AAGlB,IAAA,mBAAmB;;AAGlB,IAAA,GAAG,GAAsB,MAAM,CAAC,iBAAiB,CAAC;;AAGnD,IAAA,aAAa,GAAG,KAAK,CAAC,QAAQ,wDAAiB;AAC/C,IAAA,gBAAgB,GAAG,KAAK,CAAC,QAAQ,2DAAiB;AAClD,IAAA,iBAAiB,GAAG,KAAK,CAAC,QAAQ,4DAAwB;AAC1D,IAAA,iBAAiB,GAAG,KAAK,CAAC,QAAQ,4DAAwB;AAC1D,IAAA,eAAe,GAAG,KAAK,CAAC,QAAQ,0DAAqB;;AAGrD,IAAA,SAAS,GAAG,KAAK,CAAU,KAAK,qDAAC;AACjC,IAAA,WAAW,GAAG,KAAK,CAAU,IAAI,uDAAC;AAClC,IAAA,MAAM,GAAG,KAAK,CAAU,KAAK,kDAAC;AAC9B,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,qDAAC;AAC7B,IAAA,KAAK,GAAG,KAAK,CAAa,WAAW,iDAAC;;AAG5C,IAAA,aAAa,GAAG,IAAI,YAAY,EAAsB;AAEhE;;AAEG;AACgB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE;AAC/B,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE;AAClC,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,eAAe,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;aAChE,MAAM,CAAC,OAAO;aACd,IAAI,CAAC,GAAG,CAAC;AACd,IAAA,CAAC,yDAAC;;IAGe,aAAa,GAAmB,EAAE;;AAGhC,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,IAAI;YAC3B,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC;QACtD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,yDAAC;AAEiB,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;AAChD,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,IAAI;YAC3B,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC;QACtD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,0DAAC;AAEiB,IAAA,mBAAmB,GAAG,QAAQ,CAAC,MAAK;AACrD,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,SAAS;;YAGhC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,SAAS,CAAC;AACjD,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;;YAG7B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,SAAS;QAClB;AACF,IAAA,CAAC,+DAAC;AAEiB,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAK;AAClD,QAAA,IAAI;AACF,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE;AACnC,YAAA,IAAI,CAAC,MAAM;AAAE,gBAAA,OAAO,IAAI;;YAGxB,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC;AAC9C,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,MAAM,CAAC;;YAG1B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,4DAAC;AAEiB,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAK;AACnD,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE;AACrC,YAAA,IAAI,CAAC,OAAO;AAAE,gBAAA,OAAO,IAAI;;YAGzB,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC;AAC/C,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,OAAO,CAAC;;YAG3B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,6DAAC;;IAGM,qBAAqB,CAAC,IAAe,EAAE,mBAA2B,EAAA;AACxE,QAAA,IAAI;AACF,YAAA,MAAM,WAAW,GAAG,IAAI,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG;YAC/C,MAAM,eAAe,GAAG,MAAM;;YAG9B,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,mBAAmB,CAAC;AACzE,YAAA,IAAI,CAAC,mBAAmB;AAAE,gBAAA,OAAO,IAAI;;AAGrC,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE;AACtC,YAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,mBAAmB,CAAC;AAC/C,YAAA,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,eAAe;;AAG/E,YAAA,IAAI,oBAAoB,KAAK,eAAe,EAAE;AAC5C,gBAAA,OAAO,IAAI;YACb;;YAGA,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,oBAAoB,CAAC;AAC3E,YAAA,IAAI,CAAC,oBAAoB;AAAE,gBAAA,OAAO,IAAI;;YAGtC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC,oBAAoB,CAAC,EAAE;AAChD,gBAAA,OAAO,oBAAoB;YAC7B;;YAGA,OAAO,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,CAAC;QAC/D;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;IACF;;AAGA,IAAA,IAAW,WAAW,GAAA;AACpB,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE;IAC7B;AAEA,IAAA,IAAW,YAAY,GAAA;AACrB,QAAA,OAAO,IAAI,CAAC,cAAc,EAAE;IAC9B;AAEA,IAAA,IAAW,OAAO,GAAA;AAChB,QAAA,OAAO,IAAI,CAAC,mBAAmB,EAAE;IACnC;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAiB,KAAW,GAAG,CAAC,WAAW,EAAE,CAAC;IAC5E;;AAGO,IAAA,iBAAiB,CAAC,OAAe,EAAA;AACtC,QAAA,IAAI,OAAO,KAAK,MAAM,IAAI,CAAC,OAAO,EAAE;YAClC;QACF;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE;AACnC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE;AAErC,QAAA,IAAI,SAAoB;AACxB,QAAA,IAAI,OAAO,KAAK,MAAM,EAAE;YACtB,SAAS,GAAG,MAAM;QACpB;AAAO,aAAA,IAAI,OAAO,KAAK,OAAO,EAAE;YAC9B,SAAS,GAAG,MAAM;QACpB;aAAO;YACL;QACF;AAEA,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAC5D;uGAhMW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,mhECxB/B,y1FAyFA,EAAA,MAAA,EAAA,CAAA,q9IAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDjEa,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAP9B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,EAAA,UAAA,EACb,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,y1FAAA,EAAA,MAAA,EAAA,CAAA,q9IAAA,CAAA,EAAA;;sBAM9C,SAAS;uBAAC,oBAAoB;;sBAG9B,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,gBAAgB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAG7C,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,kBAAkB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAG/C,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,mBAAmB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAGhD,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,oBAAoB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAqBjD;;;AE3DH;;AAEG;;;;"}
1
+ {"version":3,"file":"3ddv-software-division-components-dvm-neighbors.mjs","sources":["../../dvm/neighbors/neighbors.component.ts","../../dvm/neighbors/neighbors.component.html","../../dvm/neighbors/3ddv-software-division-components-dvm-neighbors.ts"],"sourcesContent":["import { ThemeClass } from '@3ddv/software-division-components/shared';\r\nimport {\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n computed,\r\n ElementRef,\r\n EventEmitter,\r\n inject,\r\n input,\r\n OnDestroy,\r\n Output,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { Subscription } from 'rxjs';\r\nimport { Direction, HasAvailabilityFn, NeighborsData, SectionChangeEvent, TranslateSectionIdFn } from './types';\r\n\r\n@Component({\r\n selector: 'sdc-neighbors',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './neighbors.component.html',\r\n styleUrl: './neighbors.component.css',\r\n})\r\nexport class NeighborsComponent implements OnDestroy {\r\n // VIEWCHILD\r\n @ViewChild('neighborsContainer')\r\n public readonly neighborsContainer!: ElementRef<HTMLDivElement>;\r\n\r\n @ViewChild('currentSection', { static: false })\r\n public readonly currentSection!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('currentSection3d', { static: false })\r\n public readonly currentSection3d!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('leftSectionNumber', { static: false })\r\n public readonly leftSectionElement!: ElementRef<HTMLSpanElement>;\r\n\r\n @ViewChild('rightSectionNumber', { static: false })\r\n public readonly rightSectionElement!: ElementRef<HTMLSpanElement>;\r\n\r\n // SERVICES changes\r\n private readonly cdr: ChangeDetectorRef = inject(ChangeDetectorRef);\r\n\r\n // REQUIRED INPUTS\r\n public readonly neighborsData = input.required<NeighborsData>();\r\n public readonly currentSectionId = input.required<string | null>();\r\n public readonly translateTdcToMmc = input.required<TranslateSectionIdFn>();\r\n public readonly translateMmcToTdc = input.required<TranslateSectionIdFn>();\r\n public readonly hasAvailability = input.required<HasAvailabilityFn>();\r\n\r\n // OPTIONAL INPUTS\r\n public readonly isLoading = input<boolean>(false);\r\n public readonly showElement = input<boolean>(true);\r\n public readonly modeLr = input<boolean>(false);\r\n public readonly className = input<string>('');\r\n public readonly theme = input<ThemeClass>('theme-sdc');\r\n\r\n // OUTPUTS\r\n @Output() sectionChange = new EventEmitter<SectionChangeEvent>();\r\n\r\n /**\r\n * Computed class string that combines theme and user classes.\r\n */\r\n protected readonly computedClass = computed(() => {\r\n const themeClass = this.theme();\r\n const className = this.className();\r\n return Array.from(new Set(['sdc-neighbors', themeClass, className]))\r\n .filter(Boolean)\r\n .join(' ');\r\n });\r\n\r\n // COMPONENT STATE\r\n private readonly subscriptions: Subscription[] = [];\r\n\r\n // COMPUTED STATE\r\n protected readonly leftSectionId = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return null;\r\n return this.findAvailableNeighbor('prev', currentId);\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly rightSectionId = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return null;\r\n return this.findAvailableNeighbor('next', currentId);\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly currentSectionLabel = computed(() => {\r\n try {\r\n const currentId = this.currentSectionId();\r\n if (!currentId) return 'Loading';\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(currentId);\r\n if (!mmcId) return currentId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID (assuming format like \"S_31\" or similar)\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return 'Loading';\r\n }\r\n });\r\n\r\n protected readonly leftSectionLabel = computed(() => {\r\n try {\r\n const leftId = this.leftSectionId();\r\n if (!leftId) return null;\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(leftId);\r\n if (!mmcId) return leftId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n protected readonly rightSectionLabel = computed(() => {\r\n try {\r\n const rightId = this.rightSectionId();\r\n if (!rightId) return null;\r\n\r\n // Translate TDC ID to MMC ID for display\r\n const mmcId = this.translateTdcToMmc()(rightId);\r\n if (!mmcId) return rightId; // Fallback to TDC ID if translation fails\r\n\r\n // Extract section number from MMC ID\r\n return mmcId.replace(/^S_/, '');\r\n } catch (error) {\r\n return null;\r\n }\r\n });\r\n\r\n // NEIGHBOR RESOLUTION ALGORITHM\r\n private findAvailableNeighbor(type: Direction, currentSectionTdcId: string): string | null {\r\n try {\r\n const neighborKey = type === 'prev' ? 'l' : 'r';\r\n const noNeighborValue = 'none';\r\n\r\n // 1. Translate TDC → MMC\r\n const currentSectionMmcId = this.translateTdcToMmc()(currentSectionTdcId);\r\n if (!currentSectionMmcId) return null;\r\n\r\n // 2. Look up neighbor in data\r\n const neighbors = this.neighborsData();\r\n const neighbor = neighbors[currentSectionMmcId];\r\n const neighborSectionMmcId = neighbor ? neighbor[neighborKey] : noNeighborValue;\r\n\r\n // 3. Handle 'none' boundary\r\n if (neighborSectionMmcId === noNeighborValue) {\r\n return null;\r\n }\r\n\r\n // 4. Translate MMC → TDC\r\n const neighborSectionTdcId = this.translateMmcToTdc()(neighborSectionMmcId);\r\n if (!neighborSectionTdcId) return null;\r\n\r\n // 5. Check availability\r\n if (this.hasAvailability()(neighborSectionTdcId)) {\r\n return neighborSectionTdcId;\r\n }\r\n\r\n // 6. Recursively find next available (skip unavailable sections)\r\n return this.findAvailableNeighbor(type, neighborSectionTdcId);\r\n } catch (error) {\r\n return null;\r\n }\r\n }\r\n\r\n // GETTERS\r\n public get leftSection(): string | null {\r\n return this.leftSectionId();\r\n }\r\n\r\n public get rightSection(): string | null {\r\n return this.rightSectionId();\r\n }\r\n\r\n public get section(): string | null {\r\n return this.currentSectionLabel();\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.subscriptions.forEach((sub: Subscription): void => sub.unsubscribe());\r\n }\r\n\r\n // METHODS\r\n public navigateToSection(section: string): void {\r\n if (section === 'none' || !section) {\r\n return;\r\n }\r\n\r\n const leftId = this.leftSectionId();\r\n const rightId = this.rightSectionId();\r\n\r\n let direction: Direction;\r\n if (section === leftId) {\r\n direction = 'prev';\r\n } else if (section === rightId) {\r\n direction = 'next';\r\n } else {\r\n return;\r\n }\r\n\r\n this.sectionChange.emit({ direction, sectionId: section });\r\n }\r\n}\r\n","<div #neighborsContainer class=\"neighbors-container\" [class]=\"computedClass()\" [class.hidden]=\"!showElement()\">\n <!-- LEFT SECTION BUTTON -->\n @if (leftSection) {\n <div\n class=\"neighbor-button-left neighbor-button\"\n [class.is-none]=\"leftSection === 'none'\"\n tabindex=\"0\"\n (click)=\"navigateToSection(leftSection!)\"\n (keypress.enter)=\"navigateToSection(leftSection!)\">\n <!-- ARROW ICON -->\n <div class=\"neighbor-button-icon\">\n <i>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"3\"\n stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.75 19.5 8.25 12l7.5-7.5\" />\n </svg>\n </i>\n </div>\n\n <!-- SECTION LABEL -->\n <div class=\"neighbor-button-label\">\n <p>\n <span class=\"prefix\"> {{ modeLr() ? 'Left Section' : 'Section' }}&nbsp; </span>\n @if (leftSection !== 'none' && !modeLr()) {\n <span #leftSectionNumber class=\"section-number\">\n {{ leftSectionLabel() }}\n </span>\n }\n </p>\n </div>\n </div>\n }\n\n <!-- CURRENT SECTION TEXT -->\n <div class=\"current-section-wrapper\">\n @if (isLoading()) {\n <p class=\"current-section-loading\">\n <span>Loading</span>\n </p>\n } @else {\n @if (section) {\n <h3 class=\"current-section-text\">\n <span class=\"prefix\">Section&nbsp;</span>\n <span #currentSection class=\"section-number\">{{ section }}</span>\n </h3>\n }\n }\n </div>\n\n <!-- RIGHT SECTION BUTTON -->\n @if (rightSection) {\n <div\n class=\"neighbor-button-right neighbor-button\"\n [class.is-none]=\"rightSection === 'none'\"\n tabindex=\"1\"\n (click)=\"navigateToSection(rightSection!)\"\n (keypress.enter)=\"navigateToSection(rightSection!)\">\n <!-- ARROW ICON -->\n <div class=\"neighbor-button-icon\">\n <i>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"3\"\n stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m8.25 4.5 7.5 7.5-7.5 7.5\" />\n </svg>\n </i>\n </div>\n\n <!-- SECTION LABEL -->\n <div class=\"neighbor-button-label\">\n <p>\n <span class=\"prefix\"> {{ modeLr() ? 'Right Section' : 'Section' }}&nbsp; </span>\n @if (rightSection !== 'none' && !modeLr()) {\n <span #rightSectionNumber class=\"section-number\">\n {{ rightSectionLabel() }}\n </span>\n }\n </p>\n </div>\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;MAwBa,kBAAkB,CAAA;;AAGb,IAAA,kBAAkB;AAGlB,IAAA,cAAc;AAGd,IAAA,gBAAgB;AAGhB,IAAA,kBAAkB;AAGlB,IAAA,mBAAmB;;AAGlB,IAAA,GAAG,GAAsB,MAAM,CAAC,iBAAiB,CAAC;;AAGnD,IAAA,aAAa,GAAG,KAAK,CAAC,QAAQ,wDAAiB;AAC/C,IAAA,gBAAgB,GAAG,KAAK,CAAC,QAAQ,2DAAiB;AAClD,IAAA,iBAAiB,GAAG,KAAK,CAAC,QAAQ,4DAAwB;AAC1D,IAAA,iBAAiB,GAAG,KAAK,CAAC,QAAQ,4DAAwB;AAC1D,IAAA,eAAe,GAAG,KAAK,CAAC,QAAQ,0DAAqB;;AAGrD,IAAA,SAAS,GAAG,KAAK,CAAU,KAAK,qDAAC;AACjC,IAAA,WAAW,GAAG,KAAK,CAAU,IAAI,uDAAC;AAClC,IAAA,MAAM,GAAG,KAAK,CAAU,KAAK,kDAAC;AAC9B,IAAA,SAAS,GAAG,KAAK,CAAS,EAAE,qDAAC;AAC7B,IAAA,KAAK,GAAG,KAAK,CAAa,WAAW,iDAAC;;AAG5C,IAAA,aAAa,GAAG,IAAI,YAAY,EAAsB;AAEhE;;AAEG;AACgB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE;AAC/B,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE;AAClC,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,eAAe,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;aAChE,MAAM,CAAC,OAAO;aACd,IAAI,CAAC,GAAG,CAAC;AACd,IAAA,CAAC,yDAAC;;IAGe,aAAa,GAAmB,EAAE;;AAGhC,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,IAAI;YAC3B,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC;QACtD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,yDAAC;AAEiB,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;AAChD,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,IAAI;YAC3B,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC;QACtD;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,0DAAC;AAEiB,IAAA,mBAAmB,GAAG,QAAQ,CAAC,MAAK;AACrD,QAAA,IAAI;AACF,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,IAAI,CAAC,SAAS;AAAE,gBAAA,OAAO,SAAS;;YAGhC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,SAAS,CAAC;AACjD,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;;YAG7B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,SAAS;QAClB;AACF,IAAA,CAAC,+DAAC;AAEiB,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAK;AAClD,QAAA,IAAI;AACF,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE;AACnC,YAAA,IAAI,CAAC,MAAM;AAAE,gBAAA,OAAO,IAAI;;YAGxB,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC;AAC9C,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,MAAM,CAAC;;YAG1B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,4DAAC;AAEiB,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAK;AACnD,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE;AACrC,YAAA,IAAI,CAAC,OAAO;AAAE,gBAAA,OAAO,IAAI;;YAGzB,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC;AAC/C,YAAA,IAAI,CAAC,KAAK;gBAAE,OAAO,OAAO,CAAC;;YAG3B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACjC;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;AACF,IAAA,CAAC,6DAAC;;IAGM,qBAAqB,CAAC,IAAe,EAAE,mBAA2B,EAAA;AACxE,QAAA,IAAI;AACF,YAAA,MAAM,WAAW,GAAG,IAAI,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG;YAC/C,MAAM,eAAe,GAAG,MAAM;;YAG9B,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,mBAAmB,CAAC;AACzE,YAAA,IAAI,CAAC,mBAAmB;AAAE,gBAAA,OAAO,IAAI;;AAGrC,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE;AACtC,YAAA,MAAM,QAAQ,GAAG,SAAS,CAAC,mBAAmB,CAAC;AAC/C,YAAA,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,eAAe;;AAG/E,YAAA,IAAI,oBAAoB,KAAK,eAAe,EAAE;AAC5C,gBAAA,OAAO,IAAI;YACb;;YAGA,MAAM,oBAAoB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,oBAAoB,CAAC;AAC3E,YAAA,IAAI,CAAC,oBAAoB;AAAE,gBAAA,OAAO,IAAI;;YAGtC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC,oBAAoB,CAAC,EAAE;AAChD,gBAAA,OAAO,oBAAoB;YAC7B;;YAGA,OAAO,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,CAAC;QAC/D;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,IAAI;QACb;IACF;;AAGA,IAAA,IAAW,WAAW,GAAA;AACpB,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE;IAC7B;AAEA,IAAA,IAAW,YAAY,GAAA;AACrB,QAAA,OAAO,IAAI,CAAC,cAAc,EAAE;IAC9B;AAEA,IAAA,IAAW,OAAO,GAAA;AAChB,QAAA,OAAO,IAAI,CAAC,mBAAmB,EAAE;IACnC;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAiB,KAAW,GAAG,CAAC,WAAW,EAAE,CAAC;IAC5E;;AAGO,IAAA,iBAAiB,CAAC,OAAe,EAAA;AACtC,QAAA,IAAI,OAAO,KAAK,MAAM,IAAI,CAAC,OAAO,EAAE;YAClC;QACF;AAEA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE;AACnC,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE;AAErC,QAAA,IAAI,SAAoB;AACxB,QAAA,IAAI,OAAO,KAAK,MAAM,EAAE;YACtB,SAAS,GAAG,MAAM;QACpB;AAAO,aAAA,IAAI,OAAO,KAAK,OAAO,EAAE;YAC9B,SAAS,GAAG,MAAM;QACpB;aAAO;YACL;QACF;AAEA,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAC5D;uGAhMW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,mhECxB/B,y1FAyFA,EAAA,MAAA,EAAA,CAAA,q9IAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDjEa,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAP9B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,eAAe,EAAA,UAAA,EACb,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,y1FAAA,EAAA,MAAA,EAAA,CAAA,q9IAAA,CAAA,EAAA;;sBAM9C,SAAS;uBAAC,oBAAoB;;sBAG9B,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,gBAAgB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAG7C,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,kBAAkB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAG/C,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,mBAAmB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAGhD,SAAS;AAAC,gBAAA,IAAA,EAAA,CAAA,oBAAoB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;;sBAqBjD;;;AE3DH;;AAEG;;;;"}
@@ -1,4 +1,5 @@
1
- import { BraintreeComponent } from '@3ddv/software-division-components/generic/braintree';
1
+ import { BraintreeTrigger, BraintreeComponent } from '@3ddv/software-division-components/generic/braintree';
2
+ import { ButtonComponent } from '@3ddv/software-division-components/generic/button';
2
3
  import * as i0 from '@angular/core';
3
4
  import { input, output, inject, DestroyRef, ChangeDetectionStrategy, Component } from '@angular/core';
4
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -17,24 +18,31 @@ class AddDigitalWalletComponent {
17
18
  useDefaultCard = output();
18
19
  // STATE
19
20
  useDefaultCardCtrl = new FormControl(false);
21
+ trigger = new BraintreeTrigger();
20
22
  destroyRef = inject(DestroyRef);
21
23
  ngOnInit() {
22
24
  this.useDefaultCardCtrl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
23
25
  this.useDefaultCard.emit(!!value);
24
26
  });
25
27
  }
26
- onDigitalWalletSubmitted(data) {
27
- this.braintreePayload.emit(data);
28
+ async onSaveCard() {
29
+ try {
30
+ const payload = await this.trigger.requestPayment();
31
+ this.braintreePayload.emit(payload);
32
+ }
33
+ catch {
34
+ // braintree component logs errors internally
35
+ }
28
36
  }
29
37
  onCancel() {
30
38
  this.cancelClick.emit();
31
39
  }
32
40
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AddDigitalWalletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
33
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: AddDigitalWalletComponent, isStandalone: true, selector: "sdc-add-digital-wallet", inputs: { token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: true, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null }, showIsDefaultCardCheckbox: { classPropertyName: "showIsDefaultCardCheckbox", publicName: "showIsDefaultCardCheckbox", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { braintreePayload: "braintreePayload", cancelClick: "cancelClick", useDefaultCard: "useDefaultCard" }, ngImport: i0, template: "<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n (onSubmit)=\"onDigitalWalletSubmitted($event)\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() || 'Something bad happened' }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <button\n class=\"disabled:opacity-30 mr-3 md:mr-6 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__cancel btn-main-reverse\"\n id=\"braintree-cancel-button\"\n type=\"button\"\n (click)=\"onCancel()\">\n Cancel\n </button>\n }\n <button\n class=\"disabled:opacity-30 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__submit btn-main\"\n id=\"braintree-save-button\"\n type=\"button\">\n Save Card\n </button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n", styles: [".sdc-digital-wallet{--sdc-digital-wallet-max-width: 32rem;--sdc-digital-wallet-padding: var(--space-2) var(--space-0);--sdc-digital-wallet-title-size: var(--text-lg);--sdc-digital-wallet-title-weight: var(--font-bold);--sdc-digital-wallet-title-leading: var(--leading-tight);--sdc-digital-wallet-braintree-max-h: 24rem;--sdc-digital-wallet-braintree-overflow: auto;--sdc-digital-wallet-submit-radius: var(--radius-3xl);--sdc-digital-wallet-submit-text-size: var(--text-base);--sdc-digital-wallet-submit-text-weight: var(--font-bold);--sdc-digital-wallet-submit-text-color: var(--color-secondary-active);--sdc-digital-wallet-error-size: 1rem;--sdc-digital-wallet-error-color: var(--color-danger);position:relative;overflow:hidden;width:100%;max-width:var(--sdc-digital-wallet-max-width);padding:var(--sdc-digital-wallet-padding);margin:0 auto}.sdc-digital-wallet__inner{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;overflow-y:auto}.sdc-digital-wallet__title{font-size:var(--sdc-digital-wallet-title-size);font-weight:var(--sdc-digital-wallet-title-weight);line-height:var(--sdc-digital-wallet-title-leading)}.sdc-digital-wallet__braintree{max-height:var(--sdc-digital-wallet-braintree-max-h);overflow-y:var(--sdc-digital-wallet-braintree-overflow)}.sdc-digital-wallet__submit{border-radius:var(--sdc-digital-wallet-submit-radius);font-size:var(--sdc-digital-wallet-submit-text-size);font-weight:var(--sdc-digital-wallet-submit-text-weight);color:hsl(var(--sdc-digital-wallet-submit-text-color));white-space:nowrap;cursor:pointer;outline:0}.sdc-digital-wallet__default-card{display:flex;align-items:center;gap:.5rem;margin-top:1rem}.sdc-digital-wallet__error{font-size:var(--sdc-digital-wallet-error-size);color:hsl(var(--sdc-digital-wallet-error-color))}@media(min-width:1024px){.sdc-digital-wallet{--sdc-digital-wallet-padding: var(--space-6) var(--space-6);--sdc-digital-wallet-title-size: var(--text-xl);--sdc-digital-wallet-braintree-max-h: none}}\n"], dependencies: [{ kind: "component", type: BraintreeComponent, selector: "sdc-braintree", inputs: ["token", "isDigitalWalletPayment"], outputs: ["onSubmit"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
41
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: AddDigitalWalletComponent, isStandalone: true, selector: "sdc-add-digital-wallet", inputs: { token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: true, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null }, showIsDefaultCardCheckbox: { classPropertyName: "showIsDefaultCardCheckbox", publicName: "showIsDefaultCardCheckbox", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { braintreePayload: "braintreePayload", cancelClick: "cancelClick", useDefaultCard: "useDefaultCard" }, ngImport: i0, template: "<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n [trigger]=\"trigger\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" class=\"sdc-digital-wallet__checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <sdc-button\n className=\"sdc-digital-wallet__cancel\"\n id=\"braintree-cancel-button\"\n variant=\"outline\"\n (onClick)=\"onCancel()\">\n Cancel\n </sdc-button>\n }\n <sdc-button\n className=\"sdc-digital-wallet__submit\"\n id=\"braintree-save-button\"\n variant=\"accent\"\n (onClick)=\"onSaveCard()\">\n Save Card\n </sdc-button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n", styles: [".sdc-digital-wallet{--sdc-digital-wallet-max-width: 32rem;--sdc-digital-wallet-padding: var(--space-2) var(--space-0);--sdc-digital-wallet-title-size: var(--text-lg);--sdc-digital-wallet-title-weight: var(--font-bold);--sdc-digital-wallet-title-leading: var(--leading-tight);--sdc-digital-wallet-braintree-max-h: 24rem;--sdc-digital-wallet-braintree-overflow: auto;--sdc-digital-wallet-submit-radius: var(--radius-3xl);--sdc-digital-wallet-submit-text-size: var(--text-base);--sdc-digital-wallet-submit-text-weight: var(--font-bold);--sdc-digital-wallet-submit-text-color: var(--color-white);--sdc-digital-wallet-submit-bg-color: var(--color-accent);--sdc-digital-wallet-error-size: 1rem;--sdc-digital-wallet-error-color: var(--color-danger);position:relative;overflow:hidden;width:100%;max-width:var(--sdc-digital-wallet-max-width);padding:var(--sdc-digital-wallet-padding);margin:0 auto}.sdc-digital-wallet__inner{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;overflow-y:auto}.sdc-digital-wallet__title{font-size:var(--sdc-digital-wallet-title-size);font-weight:var(--sdc-digital-wallet-title-weight);line-height:var(--sdc-digital-wallet-title-leading)}.sdc-digital-wallet__braintree{max-height:var(--sdc-digital-wallet-braintree-max-h);overflow-y:var(--sdc-digital-wallet-braintree-overflow)}.sdc-digital-wallet__submit{border-radius:var(--sdc-digital-wallet-submit-radius);font-size:var(--sdc-digital-wallet-submit-text-size);font-weight:var(--sdc-digital-wallet-submit-text-weight);color:hsl(var(--sdc-digital-wallet-submit-text-color));background-color:hsl(var(--sdc-digital-wallet-submit-bg-color));white-space:nowrap;cursor:pointer;outline:0}.sdc-digital-wallet__checkbox{padding:.125rem;width:1rem;height:1rem;accent-color:var(--color-accent)}.sdc-digital-wallet__default-card{display:flex;align-items:center;gap:.5rem;margin-top:1rem}.sdc-digital-wallet__error{font-size:var(--sdc-digital-wallet-error-size);color:hsl(var(--sdc-digital-wallet-error-color))}@media(min-width:1024px){.sdc-digital-wallet{--sdc-digital-wallet-padding: var(--space-6) var(--space-6);--sdc-digital-wallet-title-size: var(--text-xl);--sdc-digital-wallet-braintree-max-h: none}}\n"], dependencies: [{ kind: "component", type: BraintreeComponent, selector: "sdc-braintree", inputs: ["token", "isDigitalWalletPayment", "trigger"], outputs: ["onSubmit"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "sdc-button", inputs: ["theme", "styleClass", "className", "variant", "size", "type", "disabled", "ripple", "debounce"], outputs: ["onClick", "onFocus", "onBlur", "isDebounced"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
34
42
  }
35
43
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: AddDigitalWalletComponent, decorators: [{
36
44
  type: Component,
37
- args: [{ standalone: true, selector: 'sdc-add-digital-wallet', changeDetection: ChangeDetectionStrategy.OnPush, imports: [BraintreeComponent, ReactiveFormsModule], template: "<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n (onSubmit)=\"onDigitalWalletSubmitted($event)\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() || 'Something bad happened' }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <button\n class=\"disabled:opacity-30 mr-3 md:mr-6 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__cancel btn-main-reverse\"\n id=\"braintree-cancel-button\"\n type=\"button\"\n (click)=\"onCancel()\">\n Cancel\n </button>\n }\n <button\n class=\"disabled:opacity-30 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__submit btn-main\"\n id=\"braintree-save-button\"\n type=\"button\">\n Save Card\n </button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n", styles: [".sdc-digital-wallet{--sdc-digital-wallet-max-width: 32rem;--sdc-digital-wallet-padding: var(--space-2) var(--space-0);--sdc-digital-wallet-title-size: var(--text-lg);--sdc-digital-wallet-title-weight: var(--font-bold);--sdc-digital-wallet-title-leading: var(--leading-tight);--sdc-digital-wallet-braintree-max-h: 24rem;--sdc-digital-wallet-braintree-overflow: auto;--sdc-digital-wallet-submit-radius: var(--radius-3xl);--sdc-digital-wallet-submit-text-size: var(--text-base);--sdc-digital-wallet-submit-text-weight: var(--font-bold);--sdc-digital-wallet-submit-text-color: var(--color-secondary-active);--sdc-digital-wallet-error-size: 1rem;--sdc-digital-wallet-error-color: var(--color-danger);position:relative;overflow:hidden;width:100%;max-width:var(--sdc-digital-wallet-max-width);padding:var(--sdc-digital-wallet-padding);margin:0 auto}.sdc-digital-wallet__inner{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;overflow-y:auto}.sdc-digital-wallet__title{font-size:var(--sdc-digital-wallet-title-size);font-weight:var(--sdc-digital-wallet-title-weight);line-height:var(--sdc-digital-wallet-title-leading)}.sdc-digital-wallet__braintree{max-height:var(--sdc-digital-wallet-braintree-max-h);overflow-y:var(--sdc-digital-wallet-braintree-overflow)}.sdc-digital-wallet__submit{border-radius:var(--sdc-digital-wallet-submit-radius);font-size:var(--sdc-digital-wallet-submit-text-size);font-weight:var(--sdc-digital-wallet-submit-text-weight);color:hsl(var(--sdc-digital-wallet-submit-text-color));white-space:nowrap;cursor:pointer;outline:0}.sdc-digital-wallet__default-card{display:flex;align-items:center;gap:.5rem;margin-top:1rem}.sdc-digital-wallet__error{font-size:var(--sdc-digital-wallet-error-size);color:hsl(var(--sdc-digital-wallet-error-color))}@media(min-width:1024px){.sdc-digital-wallet{--sdc-digital-wallet-padding: var(--space-6) var(--space-6);--sdc-digital-wallet-title-size: var(--text-xl);--sdc-digital-wallet-braintree-max-h: none}}\n"] }]
45
+ args: [{ standalone: true, selector: 'sdc-add-digital-wallet', changeDetection: ChangeDetectionStrategy.OnPush, imports: [BraintreeComponent, ReactiveFormsModule, ButtonComponent], template: "<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n [trigger]=\"trigger\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" class=\"sdc-digital-wallet__checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <sdc-button\n className=\"sdc-digital-wallet__cancel\"\n id=\"braintree-cancel-button\"\n variant=\"outline\"\n (onClick)=\"onCancel()\">\n Cancel\n </sdc-button>\n }\n <sdc-button\n className=\"sdc-digital-wallet__submit\"\n id=\"braintree-save-button\"\n variant=\"accent\"\n (onClick)=\"onSaveCard()\">\n Save Card\n </sdc-button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n", styles: [".sdc-digital-wallet{--sdc-digital-wallet-max-width: 32rem;--sdc-digital-wallet-padding: var(--space-2) var(--space-0);--sdc-digital-wallet-title-size: var(--text-lg);--sdc-digital-wallet-title-weight: var(--font-bold);--sdc-digital-wallet-title-leading: var(--leading-tight);--sdc-digital-wallet-braintree-max-h: 24rem;--sdc-digital-wallet-braintree-overflow: auto;--sdc-digital-wallet-submit-radius: var(--radius-3xl);--sdc-digital-wallet-submit-text-size: var(--text-base);--sdc-digital-wallet-submit-text-weight: var(--font-bold);--sdc-digital-wallet-submit-text-color: var(--color-white);--sdc-digital-wallet-submit-bg-color: var(--color-accent);--sdc-digital-wallet-error-size: 1rem;--sdc-digital-wallet-error-color: var(--color-danger);position:relative;overflow:hidden;width:100%;max-width:var(--sdc-digital-wallet-max-width);padding:var(--sdc-digital-wallet-padding);margin:0 auto}.sdc-digital-wallet__inner{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;overflow-y:auto}.sdc-digital-wallet__title{font-size:var(--sdc-digital-wallet-title-size);font-weight:var(--sdc-digital-wallet-title-weight);line-height:var(--sdc-digital-wallet-title-leading)}.sdc-digital-wallet__braintree{max-height:var(--sdc-digital-wallet-braintree-max-h);overflow-y:var(--sdc-digital-wallet-braintree-overflow)}.sdc-digital-wallet__submit{border-radius:var(--sdc-digital-wallet-submit-radius);font-size:var(--sdc-digital-wallet-submit-text-size);font-weight:var(--sdc-digital-wallet-submit-text-weight);color:hsl(var(--sdc-digital-wallet-submit-text-color));background-color:hsl(var(--sdc-digital-wallet-submit-bg-color));white-space:nowrap;cursor:pointer;outline:0}.sdc-digital-wallet__checkbox{padding:.125rem;width:1rem;height:1rem;accent-color:var(--color-accent)}.sdc-digital-wallet__default-card{display:flex;align-items:center;gap:.5rem;margin-top:1rem}.sdc-digital-wallet__error{font-size:var(--sdc-digital-wallet-error-size);color:hsl(var(--sdc-digital-wallet-error-color))}@media(min-width:1024px){.sdc-digital-wallet{--sdc-digital-wallet-padding: var(--space-6) var(--space-6);--sdc-digital-wallet-title-size: var(--text-xl);--sdc-digital-wallet-braintree-max-h: none}}\n"] }]
38
46
  }], propDecorators: { token: [{ type: i0.Input, args: [{ isSignal: true, alias: "token", required: true }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], showCancelButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCancelButton", required: false }] }], showIsDefaultCardCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIsDefaultCardCheckbox", required: false }] }], braintreePayload: [{ type: i0.Output, args: ["braintreePayload"] }], cancelClick: [{ type: i0.Output, args: ["cancelClick"] }], useDefaultCard: [{ type: i0.Output, args: ["useDefaultCard"] }] } });
39
47
 
40
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"3ddv-software-division-components-generic-add-digital-wallet.mjs","sources":["../../generic/add-digital-wallet/add-digital-wallet.component.ts","../../generic/add-digital-wallet/add-digital-wallet.component.html","../../generic/add-digital-wallet/3ddv-software-division-components-generic-add-digital-wallet.ts"],"sourcesContent":["import { BraintreeComponent, BraintreePayload } from '@3ddv/software-division-components/generic/braintree';\nimport { ChangeDetectionStrategy, Component, DestroyRef, OnInit, inject, input, output } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\n\n@Component({\n standalone: true,\n selector: 'sdc-add-digital-wallet',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BraintreeComponent, ReactiveFormsModule],\n templateUrl: './add-digital-wallet.component.html',\n styleUrls: ['./add-digital-wallet.component.css'],\n})\nexport class AddDigitalWalletComponent implements OnInit {\n // INPUTS\n public token = input.required<string>();\n public errorMessage = input<string | null>(null);\n public showCancelButton = input<boolean>(false);\n public showIsDefaultCardCheckbox = input<boolean>(false);\n\n // OUTPUTS\n public readonly braintreePayload = output<BraintreePayload>();\n public readonly cancelClick = output<void>();\n public readonly useDefaultCard = output<boolean>();\n\n // STATE\n public useDefaultCardCtrl = new FormControl(false);\n private readonly destroyRef = inject(DestroyRef);\n\n public ngOnInit(): void {\n this.useDefaultCardCtrl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {\n this.useDefaultCard.emit(!!value);\n });\n }\n\n public onDigitalWalletSubmitted(data: BraintreePayload): void {\n this.braintreePayload.emit(data);\n }\n\n public onCancel() {\n this.cancelClick.emit();\n }\n}\n","<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n (onSubmit)=\"onDigitalWalletSubmitted($event)\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() || 'Something bad happened' }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <button\n class=\"disabled:opacity-30 mr-3 md:mr-6 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__cancel btn-main-reverse\"\n id=\"braintree-cancel-button\"\n type=\"button\"\n (click)=\"onCancel()\">\n Cancel\n </button>\n }\n <button\n class=\"disabled:opacity-30 disabled:hover:transform-none hover:scale-95 transition-transform sdc-digital-wallet__submit btn-main\"\n id=\"braintree-save-button\"\n type=\"button\">\n Save Card\n </button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;MAaa,yBAAyB,CAAA;;AAE7B,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,gDAAU;AAChC,IAAA,YAAY,GAAG,KAAK,CAAgB,IAAI,wDAAC;AACzC,IAAA,gBAAgB,GAAG,KAAK,CAAU,KAAK,4DAAC;AACxC,IAAA,yBAAyB,GAAG,KAAK,CAAU,KAAK,qEAAC;;IAGxC,gBAAgB,GAAG,MAAM,EAAoB;IAC7C,WAAW,GAAG,MAAM,EAAQ;IAC5B,cAAc,GAAG,MAAM,EAAW;;AAG3C,IAAA,kBAAkB,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC;AACjC,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAEzC,QAAQ,GAAA;AACb,QAAA,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAG;YAC/F,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACnC,QAAA,CAAC,CAAC;IACJ;AAEO,IAAA,wBAAwB,CAAC,IAAsB,EAAA;AACpD,QAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;IAClC;IAEO,QAAQ,GAAA;AACb,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;IACzB;uGA5BW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,yBAAA,EAAA,EAAA,iBAAA,EAAA,2BAAA,EAAA,UAAA,EAAA,2BAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,gBAAA,EAAA,kBAAA,EAAA,WAAA,EAAA,aAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECbtC,4rDA8CA,EAAA,MAAA,EAAA,CAAA,+8DAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDrCY,kBAAkB,6HAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,4BAAA,EAAA,QAAA,EAAA,uGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,aAAA,EAAA,UAAA,EAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAItC,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBARrC,SAAS;iCACI,IAAI,EAAA,QAAA,EACN,wBAAwB,EAAA,eAAA,EACjB,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,4rDAAA,EAAA,MAAA,EAAA,CAAA,+8DAAA,CAAA,EAAA;;;AETpD;;AAEG;;;;"}
1
+ {"version":3,"file":"3ddv-software-division-components-generic-add-digital-wallet.mjs","sources":["../../generic/add-digital-wallet/add-digital-wallet.component.ts","../../generic/add-digital-wallet/add-digital-wallet.component.html","../../generic/add-digital-wallet/3ddv-software-division-components-generic-add-digital-wallet.ts"],"sourcesContent":["import { BraintreeComponent, BraintreePayload, BraintreeTrigger } from '@3ddv/software-division-components/generic/braintree';\nimport { ButtonComponent } from '@3ddv/software-division-components/generic/button';\nimport { ChangeDetectionStrategy, Component, DestroyRef, OnInit, inject, input, output } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\n\n@Component({\n standalone: true,\n selector: 'sdc-add-digital-wallet',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BraintreeComponent, ReactiveFormsModule, ButtonComponent],\n templateUrl: './add-digital-wallet.component.html',\n styleUrls: ['./add-digital-wallet.component.css'],\n})\nexport class AddDigitalWalletComponent implements OnInit {\n // INPUTS\n public token = input.required<string>();\n public errorMessage = input<string | null>(null);\n public showCancelButton = input<boolean>(false);\n public showIsDefaultCardCheckbox = input<boolean>(false);\n\n // OUTPUTS\n public readonly braintreePayload = output<BraintreePayload>();\n public readonly cancelClick = output<void>();\n public readonly useDefaultCard = output<boolean>();\n\n // STATE\n public useDefaultCardCtrl = new FormControl(false);\n public readonly trigger = new BraintreeTrigger();\n private readonly destroyRef = inject(DestroyRef);\n\n public ngOnInit(): void {\n this.useDefaultCardCtrl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {\n this.useDefaultCard.emit(!!value);\n });\n }\n\n public async onSaveCard(): Promise<void> {\n try {\n const payload = await this.trigger.requestPayment();\n this.braintreePayload.emit(payload);\n } catch {\n // braintree component logs errors internally\n }\n }\n\n public onCancel(): void {\n this.cancelClick.emit();\n }\n}\n","<div class=\"sdc-digital-wallet theme-sdc\" id=\"add-patron-card-modal\">\n <div class=\"sdc-digital-wallet__inner\">\n <h2 class=\"sdc-digital-wallet__title\">ADD YOUR CARD</h2>\n\n <sdc-braintree\n class=\"sdc-digital-wallet__braintree\"\n [isDigitalWalletPayment]=\"true\"\n [token]=\"token()\"\n [trigger]=\"trigger\">\n <div>\n @if (showIsDefaultCardCheckbox()) {\n <label\n id=\"braintree-use-default-card-checkbox\"\n class=\"flex justify-center py-4 sdc-digital-wallet__default-card\">\n <input type=\"checkbox\" class=\"sdc-digital-wallet__checkbox\" [formControl]=\"useDefaultCardCtrl\" />\n Make this my default card\n </label>\n }\n\n @if (errorMessage()) {\n <p class=\"sdc-digital-wallet__error\">\n {{ errorMessage() }}\n </p>\n }\n\n <div class=\"flex justify-center gap-4 pt-4\">\n @if (showCancelButton()) {\n <sdc-button\n className=\"sdc-digital-wallet__cancel\"\n id=\"braintree-cancel-button\"\n variant=\"outline\"\n (onClick)=\"onCancel()\">\n Cancel\n </sdc-button>\n }\n <sdc-button\n className=\"sdc-digital-wallet__submit\"\n id=\"braintree-save-button\"\n variant=\"accent\"\n (onClick)=\"onSaveCard()\">\n Save Card\n </sdc-button>\n </div>\n </div>\n </sdc-braintree>\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;MAca,yBAAyB,CAAA;;AAE7B,IAAA,KAAK,GAAG,KAAK,CAAC,QAAQ,gDAAU;AAChC,IAAA,YAAY,GAAG,KAAK,CAAgB,IAAI,wDAAC;AACzC,IAAA,gBAAgB,GAAG,KAAK,CAAU,KAAK,4DAAC;AACxC,IAAA,yBAAyB,GAAG,KAAK,CAAU,KAAK,qEAAC;;IAGxC,gBAAgB,GAAG,MAAM,EAAoB;IAC7C,WAAW,GAAG,MAAM,EAAQ;IAC5B,cAAc,GAAG,MAAM,EAAW;;AAG3C,IAAA,kBAAkB,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC;AAClC,IAAA,OAAO,GAAG,IAAI,gBAAgB,EAAE;AAC/B,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAEzC,QAAQ,GAAA;AACb,QAAA,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAG;YAC/F,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACnC,QAAA,CAAC,CAAC;IACJ;AAEO,IAAA,MAAM,UAAU,GAAA;AACrB,QAAA,IAAI;YACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE;AACnD,YAAA,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QACrC;AAAE,QAAA,MAAM;;QAER;IACF;IAEO,QAAQ,GAAA;AACb,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;IACzB;uGAlCW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,yBAAyB,gyBCdtC,miDA+CA,EAAA,MAAA,EAAA,CAAA,oqEAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDrCY,kBAAkB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,wBAAA,EAAA,SAAA,CAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,mBAAmB,2eAAE,eAAe,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,YAAA,EAAA,WAAA,EAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,UAAA,EAAA,QAAA,EAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,SAAA,EAAA,SAAA,EAAA,QAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAIvD,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBARrC,SAAS;AACI,YAAA,IAAA,EAAA,CAAA,EAAA,UAAA,EAAA,IAAI,EAAA,QAAA,EACN,wBAAwB,EAAA,eAAA,EACjB,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC,CAAC,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,CAAC,EAAA,QAAA,EAAA,miDAAA,EAAA,MAAA,EAAA,CAAA,oqEAAA,CAAA,EAAA;;;AEVrE;;AAEG;;;;"}
@@ -1,10 +1,39 @@
1
- import { CommonModule } from '@angular/common';
2
1
  import * as i0 from '@angular/core';
3
- import { viewChild, ElementRef, input, output, effect, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { signal, viewChild, ElementRef, input, output, effect, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+
5
+ /** Pass an instance to BraintreeComponent's [trigger] input. Call fire() to request payment. */
6
+ class BraintreeTrigger {
7
+ _count = signal(0, ...(ngDevMode ? [{ debugName: "_count" }] : []));
8
+ signal = this._count.asReadonly();
9
+ _resolve = null;
10
+ _reject = null;
11
+ /** Triggers the drop-in UI to request a payment method. Resolves with the payload on success, rejects on error. */
12
+ requestPayment() {
13
+ return new Promise((resolve, reject) => {
14
+ this._resolve = resolve;
15
+ this._reject = reject;
16
+ this._count.update(c => c + 1);
17
+ });
18
+ }
19
+ /** @internal Called by BraintreeComponent when payment succeeds */
20
+ settle(payload) {
21
+ this._resolve?.(payload);
22
+ this._resolve = null;
23
+ this._reject = null;
24
+ }
25
+ /** @internal Called by BraintreeComponent when payment fails */
26
+ fail(err) {
27
+ this._reject?.(err);
28
+ this._resolve = null;
29
+ this._reject = null;
30
+ }
31
+ }
4
32
 
5
33
  /**
6
34
  * Braintree Drop‑in component that auto‑wires clicks
7
35
  * on the first projected element and errors if more than one.
36
+ * It also accepts a BraintreeTrigger to get the nonce without a projected element.
8
37
  * Recreates the drop‑in UI whenever isDigitalWalletPayment changes,
9
38
  * ensuring the container is empty before initialization.
10
39
  */
@@ -16,21 +45,40 @@ class BraintreeComponent {
16
45
  token = input.required(...(ngDevMode ? [{ debugName: "token" }] : []));
17
46
  /** Toggle to require cardholder name */
18
47
  isDigitalWalletPayment = input(false, ...(ngDevMode ? [{ debugName: "isDigitalWalletPayment" }] : []));
48
+ /** BraintreeTrigger instance — call trigger.fire() from anywhere to request payment */
49
+ trigger = input(...(ngDevMode ? [undefined, { debugName: "trigger" }] : []));
19
50
  /** Emits when a payment payload is ready */
20
51
  onSubmit = output();
21
52
  /** Holds the Drop‑in instance */
22
53
  dropinInstance;
23
54
  constructor(elRef) {
24
55
  this.elRef = elRef;
56
+ // Call requestPayment whenever trigger.fire() increments the counter
57
+ effect(() => {
58
+ const t = this.trigger();
59
+ if (!t)
60
+ return;
61
+ const count = t.signal();
62
+ if (count > 0) {
63
+ this.requestPayment();
64
+ }
65
+ });
25
66
  // Re-initialize drop‑in UI on config change
26
67
  effect(() => {
27
68
  const requireName = this.isDigitalWalletPayment();
28
69
  this.teardownDropin();
29
70
  this.initializeDropin(requireName);
30
71
  });
31
- // Attach click handler to the single projected element
72
+ // Attach click handler to the single projected element (only when no trigger is provided)
32
73
  effect(onCleanup => {
74
+ if (this.trigger())
75
+ return;
33
76
  const projectedEls = Array.from(this.elRef.nativeElement.children).filter(el => el.id !== 'braintree-container');
77
+ // No projected elements, quit from trying to append event to button
78
+ if (projectedEls.length === 0) {
79
+ return;
80
+ }
81
+ // More than one button projected, problematic scenario, quit with an error.
34
82
  if (projectedEls.length > 1) {
35
83
  throw new Error(`sdc-braintree: only one projected element allowed, found ${projectedEls.length}`);
36
84
  }
@@ -83,10 +131,22 @@ class BraintreeComponent {
83
131
  this.dropinInstance.requestPaymentMethod((err, payload) => {
84
132
  if (err) {
85
133
  console.error('sdc-braintree payment error:', err);
134
+ this.trigger()?.fail(err);
135
+ this.focusContainer();
86
136
  return;
87
137
  }
88
138
  this.onSubmit.emit(payload);
139
+ this.trigger()?.settle(payload);
140
+ });
141
+ }
142
+ /** Scrolls the drop-in container into view and focuses it — called on payment error to redirect user attention. */
143
+ focusContainer() {
144
+ const container = this.braintreeContainer()?.nativeElement;
145
+ container?.scrollIntoView({
146
+ behavior: 'smooth', // animated
147
+ block: 'center', // keeps form fully visible mid-viewport
89
148
  });
149
+ container?.focus({ preventScroll: true /* avoid jump-fighting the smooth scroll above */ });
90
150
  }
91
151
  /** Tears down the drop-in instance if present */
92
152
  teardownDropin() {
@@ -96,7 +156,7 @@ class BraintreeComponent {
96
156
  this.dropinInstance = null;
97
157
  }
98
158
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: BraintreeComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
99
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: BraintreeComponent, isStandalone: true, selector: "sdc-braintree", inputs: { token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: true, transformFunction: null }, isDigitalWalletPayment: { classPropertyName: "isDigitalWalletPayment", publicName: "isDigitalWalletPayment", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSubmit: "onSubmit" }, viewQueries: [{ propertyName: "braintreeContainer", first: true, predicate: ["braintreeContainer"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
159
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.4", type: BraintreeComponent, isStandalone: true, selector: "sdc-braintree", inputs: { token: { classPropertyName: "token", publicName: "token", isSignal: true, isRequired: true, transformFunction: null }, isDigitalWalletPayment: { classPropertyName: "isDigitalWalletPayment", publicName: "isDigitalWalletPayment", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSubmit: "onSubmit" }, viewQueries: [{ propertyName: "braintreeContainer", first: true, predicate: ["braintreeContainer"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
100
160
  <div #braintreeContainer id="braintree-container"></div>
101
161
  <ng-content></ng-content>
102
162
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -113,7 +173,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImpor
113
173
  <ng-content></ng-content>
114
174
  `,
115
175
  }]
116
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { braintreeContainer: [{ type: i0.ViewChild, args: ['braintreeContainer', { ...{ read: (ElementRef) }, isSignal: true }] }], token: [{ type: i0.Input, args: [{ isSignal: true, alias: "token", required: true }] }], isDigitalWalletPayment: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDigitalWalletPayment", required: false }] }], onSubmit: [{ type: i0.Output, args: ["onSubmit"] }] } });
176
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { braintreeContainer: [{ type: i0.ViewChild, args: ['braintreeContainer', { ...{ read: (ElementRef) }, isSignal: true }] }], token: [{ type: i0.Input, args: [{ isSignal: true, alias: "token", required: true }] }], isDigitalWalletPayment: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDigitalWalletPayment", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], onSubmit: [{ type: i0.Output, args: ["onSubmit"] }] } });
117
177
 
118
178
  /*
119
179
  * Public API Surface of software-division-components
@@ -123,5 +183,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImpor
123
183
  * Generated bundle index. Do not edit.
124
184
  */
125
185
 
126
- export { BraintreeComponent };
186
+ export { BraintreeComponent, BraintreeTrigger };
127
187
  //# sourceMappingURL=3ddv-software-division-components-generic-braintree.mjs.map