valtech-components 2.0.595 → 2.0.596
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/components/molecules/content-reaction/content-reaction.component.mjs +191 -0
- package/esm2022/lib/components/molecules/content-reaction/types.mjs +2 -0
- package/esm2022/lib/components/molecules/docs-section/docs-section.component.mjs +85 -0
- package/esm2022/lib/components/molecules/docs-section/types.mjs +2 -0
- package/esm2022/lib/components/molecules/feedback-form/feedback-form.component.mjs +354 -0
- package/esm2022/lib/components/molecules/feedback-form/types.mjs +2 -0
- package/esm2022/lib/components/templates/docs-page/docs-page.component.mjs +188 -0
- package/esm2022/lib/components/templates/docs-page/types.mjs +2 -0
- package/esm2022/lib/services/ads/types.mjs +1 -1
- package/esm2022/lib/services/feedback/config.mjs +49 -0
- package/esm2022/lib/services/feedback/feedback.service.mjs +228 -0
- package/esm2022/lib/services/feedback/index.mjs +44 -0
- package/esm2022/lib/services/feedback/types.mjs +30 -0
- package/esm2022/public-api.mjs +10 -5
- package/fesm2022/valtech-components.mjs +1135 -4
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/content-reaction/content-reaction.component.d.ts +57 -0
- package/lib/components/molecules/content-reaction/types.d.ts +61 -0
- package/lib/components/molecules/docs-section/docs-section.component.d.ts +29 -0
- package/lib/components/molecules/docs-section/types.d.ts +27 -0
- package/lib/components/molecules/feedback-form/feedback-form.component.d.ts +58 -0
- package/lib/components/molecules/feedback-form/types.d.ts +54 -0
- package/lib/components/templates/docs-page/docs-page.component.d.ts +54 -0
- package/lib/components/templates/docs-page/types.d.ts +69 -0
- package/lib/services/ads/types.d.ts +10 -0
- package/lib/services/feedback/config.d.ts +35 -0
- package/lib/services/feedback/feedback.service.d.ts +110 -0
- package/lib/services/feedback/index.d.ts +40 -0
- package/lib/services/feedback/types.d.ts +159 -0
- package/package.json +1 -1
- package/public-api.d.ts +9 -0
|
@@ -5,14 +5,14 @@ import { IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal,
|
|
|
5
5
|
import * as i1 from '@angular/common';
|
|
6
6
|
import { CommonModule, NgStyle, NgFor, NgClass, isPlatformBrowser } from '@angular/common';
|
|
7
7
|
import { addIcons } from 'ionicons';
|
|
8
|
-
import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, settings, home, search, person, helpCircle, informationCircle, documentText, notifications, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, shuffleOutline, logoWhatsapp, paperPlaneOutline, mailOutline, trophyOutline, ticketOutline, giftOutline, personOutline, ellipsisVertical, closeCircle, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, documentOutline, menuOutline, searchOutline } from 'ionicons/icons';
|
|
8
|
+
import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, settings, home, search, person, helpCircle, informationCircle, documentText, notifications, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, shuffleOutline, logoWhatsapp, paperPlaneOutline, mailOutline, trophyOutline, ticketOutline, giftOutline, personOutline, ellipsisVertical, closeCircle, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, documentOutline, bugOutline, bulbOutline, closeCircleOutline, menuOutline, searchOutline } from 'ionicons/icons';
|
|
9
9
|
import * as i1$1 from '@angular/router';
|
|
10
10
|
import { RouterLink, Router, NavigationEnd, RouterOutlet } from '@angular/router';
|
|
11
11
|
import { Browser } from '@capacitor/browser';
|
|
12
12
|
import * as i1$2 from '@angular/platform-browser';
|
|
13
13
|
import QRCodeStyling from 'qr-code-styling';
|
|
14
14
|
import * as i1$3 from '@angular/forms';
|
|
15
|
-
import { ReactiveFormsModule, FormsModule, FormControl, Validators } from '@angular/forms';
|
|
15
|
+
import { ReactiveFormsModule, FormsModule, FormControl, Validators, FormBuilder } from '@angular/forms';
|
|
16
16
|
import { BehaviorSubject, isObservable, firstValueFrom, Subject, map, distinctUntilChanged, of, Observable, throwError, from, filter as filter$1 } from 'rxjs';
|
|
17
17
|
import * as i1$4 from 'ng-otp-input';
|
|
18
18
|
import { NgOtpInputComponent, NgOtpInputModule } from 'ng-otp-input';
|
|
@@ -28,7 +28,7 @@ import 'prismjs/components/prism-bash';
|
|
|
28
28
|
import Swiper from 'swiper';
|
|
29
29
|
import { Navigation, Pagination, EffectFade, EffectCube, EffectCoverflow, EffectFlip, Autoplay } from 'swiper/modules';
|
|
30
30
|
import * as i1$8 from '@angular/common/http';
|
|
31
|
-
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
31
|
+
import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
|
|
32
32
|
import { filter, map as map$1, catchError, tap, switchMap, finalize, take, debounceTime, takeUntil } from 'rxjs/operators';
|
|
33
33
|
import { Analytics, logEvent, setUserId, setUserProperties, provideAnalytics, getAnalytics } from '@angular/fire/analytics';
|
|
34
34
|
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
|
|
@@ -34761,6 +34761,872 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
34761
34761
|
type: Input
|
|
34762
34762
|
}] } });
|
|
34763
34763
|
|
|
34764
|
+
/**
|
|
34765
|
+
* Token de inyección para la configuración de Feedback.
|
|
34766
|
+
*/
|
|
34767
|
+
const VALTECH_FEEDBACK_CONFIG = new InjectionToken('ValtechFeedbackConfig');
|
|
34768
|
+
/**
|
|
34769
|
+
* Configuración por defecto.
|
|
34770
|
+
*/
|
|
34771
|
+
const DEFAULT_FEEDBACK_CONFIG = {
|
|
34772
|
+
feedbackPrefix: '/v1/feedback',
|
|
34773
|
+
maxAttachments: 5,
|
|
34774
|
+
maxFileSize: 10 * 1024 * 1024, // 10MB
|
|
34775
|
+
allowedFileTypes: ['image/*', 'video/*', 'application/pdf'],
|
|
34776
|
+
storagePath: 'feedback',
|
|
34777
|
+
};
|
|
34778
|
+
/**
|
|
34779
|
+
* Provee el servicio de feedback a la aplicación Angular.
|
|
34780
|
+
*
|
|
34781
|
+
* @param config - Configuración de feedback
|
|
34782
|
+
* @returns EnvironmentProviders para usar en bootstrapApplication
|
|
34783
|
+
*
|
|
34784
|
+
* @example
|
|
34785
|
+
* ```typescript
|
|
34786
|
+
* // main.ts
|
|
34787
|
+
* import { bootstrapApplication } from '@angular/platform-browser';
|
|
34788
|
+
* import { provideValtechFeedback } from 'valtech-components';
|
|
34789
|
+
* import { environment } from './environments/environment';
|
|
34790
|
+
*
|
|
34791
|
+
* bootstrapApplication(AppComponent, {
|
|
34792
|
+
* providers: [
|
|
34793
|
+
* provideValtechAuth({ apiUrl: environment.apiUrl }),
|
|
34794
|
+
* provideValtechFeedback({
|
|
34795
|
+
* apiUrl: environment.apiUrl,
|
|
34796
|
+
* appId: 'my-app-name',
|
|
34797
|
+
* }),
|
|
34798
|
+
* ],
|
|
34799
|
+
* });
|
|
34800
|
+
* ```
|
|
34801
|
+
*/
|
|
34802
|
+
function provideValtechFeedback(config) {
|
|
34803
|
+
const mergedConfig = {
|
|
34804
|
+
...DEFAULT_FEEDBACK_CONFIG,
|
|
34805
|
+
...config,
|
|
34806
|
+
};
|
|
34807
|
+
return makeEnvironmentProviders([
|
|
34808
|
+
{ provide: VALTECH_FEEDBACK_CONFIG, useValue: mergedConfig },
|
|
34809
|
+
]);
|
|
34810
|
+
}
|
|
34811
|
+
|
|
34812
|
+
/**
|
|
34813
|
+
* Servicio para gestionar feedback de usuarios.
|
|
34814
|
+
*
|
|
34815
|
+
* @example
|
|
34816
|
+
* ```typescript
|
|
34817
|
+
* @Component({...})
|
|
34818
|
+
* export class MyComponent {
|
|
34819
|
+
* private feedbackService = inject(FeedbackService);
|
|
34820
|
+
*
|
|
34821
|
+
* async submitFeedback() {
|
|
34822
|
+
* const response = await this.feedbackService.createAsync(
|
|
34823
|
+
* 'feedback',
|
|
34824
|
+
* 'Mi comentario',
|
|
34825
|
+
* 'Descripción detallada...'
|
|
34826
|
+
* );
|
|
34827
|
+
* console.log('Feedback enviado:', response.feedbackId);
|
|
34828
|
+
* }
|
|
34829
|
+
* }
|
|
34830
|
+
* ```
|
|
34831
|
+
*/
|
|
34832
|
+
class FeedbackService {
|
|
34833
|
+
constructor() {
|
|
34834
|
+
this.config = inject(VALTECH_FEEDBACK_CONFIG);
|
|
34835
|
+
this.http = inject(HttpClient);
|
|
34836
|
+
}
|
|
34837
|
+
/**
|
|
34838
|
+
* URL base para endpoints de feedback.
|
|
34839
|
+
*/
|
|
34840
|
+
get baseUrl() {
|
|
34841
|
+
return `${this.config.apiUrl}${this.config.feedbackPrefix}`;
|
|
34842
|
+
}
|
|
34843
|
+
/**
|
|
34844
|
+
* Captura el contexto del dispositivo automáticamente.
|
|
34845
|
+
*/
|
|
34846
|
+
captureDeviceContext() {
|
|
34847
|
+
const ua = navigator.userAgent;
|
|
34848
|
+
return {
|
|
34849
|
+
browser: this.detectBrowser(ua),
|
|
34850
|
+
os: this.detectOS(ua),
|
|
34851
|
+
viewport: `${window.innerWidth}x${window.innerHeight}`,
|
|
34852
|
+
language: navigator.language,
|
|
34853
|
+
userAgent: ua,
|
|
34854
|
+
pageUrl: window.location.href,
|
|
34855
|
+
};
|
|
34856
|
+
}
|
|
34857
|
+
/**
|
|
34858
|
+
* Crea un nuevo feedback.
|
|
34859
|
+
*
|
|
34860
|
+
* @param type - Tipo de feedback
|
|
34861
|
+
* @param title - Título del feedback
|
|
34862
|
+
* @param description - Descripción detallada
|
|
34863
|
+
* @param attachments - URLs de archivos adjuntos (opcional)
|
|
34864
|
+
* @param contentRef - Referencia a contenido específico (opcional)
|
|
34865
|
+
* @returns Observable con la respuesta
|
|
34866
|
+
*/
|
|
34867
|
+
create(type, title, description, attachments = [], contentRef) {
|
|
34868
|
+
const request = {
|
|
34869
|
+
type,
|
|
34870
|
+
title,
|
|
34871
|
+
description,
|
|
34872
|
+
attachments,
|
|
34873
|
+
contentRef,
|
|
34874
|
+
deviceContext: this.captureDeviceContext(),
|
|
34875
|
+
appId: this.config.appId,
|
|
34876
|
+
};
|
|
34877
|
+
return this.http.post(this.baseUrl, request);
|
|
34878
|
+
}
|
|
34879
|
+
/**
|
|
34880
|
+
* Crea un nuevo feedback (versión async/await).
|
|
34881
|
+
*/
|
|
34882
|
+
async createAsync(type, title, description, attachments = [], contentRef) {
|
|
34883
|
+
return firstValueFrom(this.create(type, title, description, attachments, contentRef));
|
|
34884
|
+
}
|
|
34885
|
+
/**
|
|
34886
|
+
* Obtiene un feedback por ID (solo el propietario).
|
|
34887
|
+
*
|
|
34888
|
+
* @param feedbackId - ID del feedback
|
|
34889
|
+
* @returns Observable con la respuesta
|
|
34890
|
+
*/
|
|
34891
|
+
getById(feedbackId) {
|
|
34892
|
+
return this.http.get(`${this.baseUrl}/${feedbackId}`);
|
|
34893
|
+
}
|
|
34894
|
+
/**
|
|
34895
|
+
* Obtiene un feedback por ID (versión async/await).
|
|
34896
|
+
*/
|
|
34897
|
+
async getByIdAsync(feedbackId) {
|
|
34898
|
+
return firstValueFrom(this.getById(feedbackId));
|
|
34899
|
+
}
|
|
34900
|
+
/**
|
|
34901
|
+
* Valida si un archivo cumple con las restricciones.
|
|
34902
|
+
*/
|
|
34903
|
+
validateFile(file) {
|
|
34904
|
+
// Verificar tamaño
|
|
34905
|
+
if (file.size > this.config.maxFileSize) {
|
|
34906
|
+
const maxSizeMB = Math.round(this.config.maxFileSize / (1024 * 1024));
|
|
34907
|
+
return {
|
|
34908
|
+
valid: false,
|
|
34909
|
+
error: `El archivo excede el tamaño máximo de ${maxSizeMB}MB`,
|
|
34910
|
+
};
|
|
34911
|
+
}
|
|
34912
|
+
// Verificar tipo
|
|
34913
|
+
const allowedTypes = this.config.allowedFileTypes || [];
|
|
34914
|
+
const isAllowed = allowedTypes.some((pattern) => {
|
|
34915
|
+
if (pattern.endsWith('/*')) {
|
|
34916
|
+
const baseType = pattern.replace('/*', '');
|
|
34917
|
+
return file.type.startsWith(baseType);
|
|
34918
|
+
}
|
|
34919
|
+
return file.type === pattern;
|
|
34920
|
+
});
|
|
34921
|
+
if (!isAllowed) {
|
|
34922
|
+
return {
|
|
34923
|
+
valid: false,
|
|
34924
|
+
error: 'Tipo de archivo no permitido',
|
|
34925
|
+
};
|
|
34926
|
+
}
|
|
34927
|
+
return { valid: true };
|
|
34928
|
+
}
|
|
34929
|
+
/**
|
|
34930
|
+
* Obtiene la configuración actual del servicio.
|
|
34931
|
+
*/
|
|
34932
|
+
getConfig() {
|
|
34933
|
+
return this.config;
|
|
34934
|
+
}
|
|
34935
|
+
// =========================================================================
|
|
34936
|
+
// Reaction Methods (Content feedback with emojis)
|
|
34937
|
+
// =========================================================================
|
|
34938
|
+
/**
|
|
34939
|
+
* Verifica si el usuario ya dio feedback para una entidad específica.
|
|
34940
|
+
*
|
|
34941
|
+
* @param entityType - Tipo de entidad (article, docs, feature, etc.)
|
|
34942
|
+
* @param entityId - ID de la entidad
|
|
34943
|
+
* @returns Promise con la respuesta de verificación
|
|
34944
|
+
*
|
|
34945
|
+
* @example
|
|
34946
|
+
* ```typescript
|
|
34947
|
+
* const check = await this.feedbackService.checkFeedback('article', 'art-123');
|
|
34948
|
+
* if (check.hasFeedback) {
|
|
34949
|
+
* console.log('Ya dio feedback:', check.reactionValue);
|
|
34950
|
+
* }
|
|
34951
|
+
* ```
|
|
34952
|
+
*/
|
|
34953
|
+
async checkFeedback(entityType, entityId) {
|
|
34954
|
+
const params = new URLSearchParams({
|
|
34955
|
+
appId: this.config.appId,
|
|
34956
|
+
entityType,
|
|
34957
|
+
entityId,
|
|
34958
|
+
});
|
|
34959
|
+
return firstValueFrom(this.http.get(`${this.baseUrl}/check?${params}`));
|
|
34960
|
+
}
|
|
34961
|
+
/**
|
|
34962
|
+
* Crea o actualiza una reacción (feedback con emoji).
|
|
34963
|
+
*
|
|
34964
|
+
* @param entityRef - Referencia a la entidad
|
|
34965
|
+
* @param value - Valor de la reacción (negative, neutral, positive)
|
|
34966
|
+
* @param comment - Comentario opcional (máx 500 caracteres)
|
|
34967
|
+
* @returns Promise con la respuesta
|
|
34968
|
+
*
|
|
34969
|
+
* @example
|
|
34970
|
+
* ```typescript
|
|
34971
|
+
* const response = await this.feedbackService.createReaction(
|
|
34972
|
+
* { entityType: 'article', entityId: 'art-123' },
|
|
34973
|
+
* 'positive',
|
|
34974
|
+
* 'Muy útil!'
|
|
34975
|
+
* );
|
|
34976
|
+
* ```
|
|
34977
|
+
*/
|
|
34978
|
+
async createReaction(entityRef, value, comment) {
|
|
34979
|
+
const request = {
|
|
34980
|
+
type: 'reaction',
|
|
34981
|
+
entityRef,
|
|
34982
|
+
reactionValue: value,
|
|
34983
|
+
description: comment || '',
|
|
34984
|
+
deviceContext: this.captureDeviceContext(),
|
|
34985
|
+
appId: this.config.appId,
|
|
34986
|
+
};
|
|
34987
|
+
return firstValueFrom(this.http.post(this.baseUrl, request));
|
|
34988
|
+
}
|
|
34989
|
+
// =========================================================================
|
|
34990
|
+
// Helpers privados para detección de browser/OS
|
|
34991
|
+
// =========================================================================
|
|
34992
|
+
detectBrowser(ua) {
|
|
34993
|
+
if (ua.includes('Edg/'))
|
|
34994
|
+
return 'Edge';
|
|
34995
|
+
if (ua.includes('Chrome/'))
|
|
34996
|
+
return 'Chrome';
|
|
34997
|
+
if (ua.includes('Firefox/'))
|
|
34998
|
+
return 'Firefox';
|
|
34999
|
+
if (ua.includes('Safari/') && !ua.includes('Chrome'))
|
|
35000
|
+
return 'Safari';
|
|
35001
|
+
if (ua.includes('Opera') || ua.includes('OPR/'))
|
|
35002
|
+
return 'Opera';
|
|
35003
|
+
return 'Unknown';
|
|
35004
|
+
}
|
|
35005
|
+
detectOS(ua) {
|
|
35006
|
+
if (ua.includes('Windows NT 10'))
|
|
35007
|
+
return 'Windows 10';
|
|
35008
|
+
if (ua.includes('Windows NT 11'))
|
|
35009
|
+
return 'Windows 11';
|
|
35010
|
+
if (ua.includes('Windows'))
|
|
35011
|
+
return 'Windows';
|
|
35012
|
+
if (ua.includes('Mac OS X')) {
|
|
35013
|
+
const match = ua.match(/Mac OS X (\d+[._]\d+)/);
|
|
35014
|
+
if (match) {
|
|
35015
|
+
return `macOS ${match[1].replace('_', '.')}`;
|
|
35016
|
+
}
|
|
35017
|
+
return 'macOS';
|
|
35018
|
+
}
|
|
35019
|
+
if (ua.includes('Android'))
|
|
35020
|
+
return 'Android';
|
|
35021
|
+
if (ua.includes('iPhone') || ua.includes('iPad'))
|
|
35022
|
+
return 'iOS';
|
|
35023
|
+
if (ua.includes('Linux'))
|
|
35024
|
+
return 'Linux';
|
|
35025
|
+
return 'Unknown';
|
|
35026
|
+
}
|
|
35027
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeedbackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
35028
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeedbackService, providedIn: 'root' }); }
|
|
35029
|
+
}
|
|
35030
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeedbackService, decorators: [{
|
|
35031
|
+
type: Injectable,
|
|
35032
|
+
args: [{ providedIn: 'root' }]
|
|
35033
|
+
}] });
|
|
35034
|
+
|
|
35035
|
+
/**
|
|
35036
|
+
* Configuración por defecto de tipos de feedback.
|
|
35037
|
+
*/
|
|
35038
|
+
const DEFAULT_FEEDBACK_TYPE_OPTIONS = [
|
|
35039
|
+
{
|
|
35040
|
+
value: 'issue',
|
|
35041
|
+
label: 'Reportar problema',
|
|
35042
|
+
description: 'Algo no funciona correctamente',
|
|
35043
|
+
icon: 'bug-outline',
|
|
35044
|
+
},
|
|
35045
|
+
{
|
|
35046
|
+
value: 'poor-content',
|
|
35047
|
+
label: 'Contenido incorrecto',
|
|
35048
|
+
description: 'Información incorrecta o desactualizada',
|
|
35049
|
+
icon: 'document-text-outline',
|
|
35050
|
+
},
|
|
35051
|
+
{
|
|
35052
|
+
value: 'feedback',
|
|
35053
|
+
label: 'Comentario general',
|
|
35054
|
+
description: 'Tu opinión o experiencia',
|
|
35055
|
+
icon: 'chatbubble-outline',
|
|
35056
|
+
},
|
|
35057
|
+
{
|
|
35058
|
+
value: 'suggestion',
|
|
35059
|
+
label: 'Sugerencia',
|
|
35060
|
+
description: 'Propuesta de mejora o nueva funcionalidad',
|
|
35061
|
+
icon: 'bulb-outline',
|
|
35062
|
+
},
|
|
35063
|
+
];
|
|
35064
|
+
|
|
35065
|
+
/**
|
|
35066
|
+
* Valtech Feedback Service
|
|
35067
|
+
*
|
|
35068
|
+
* Servicio para gestionar feedback de usuarios a nivel de plataforma.
|
|
35069
|
+
*
|
|
35070
|
+
* @example
|
|
35071
|
+
* ```typescript
|
|
35072
|
+
* // main.ts - Configuración
|
|
35073
|
+
* import { provideValtechFeedback } from 'valtech-components';
|
|
35074
|
+
*
|
|
35075
|
+
* bootstrapApplication(AppComponent, {
|
|
35076
|
+
* providers: [
|
|
35077
|
+
* provideValtechAuth({ apiUrl: environment.apiUrl }),
|
|
35078
|
+
* provideValtechFeedback({
|
|
35079
|
+
* apiUrl: environment.apiUrl,
|
|
35080
|
+
* appId: 'my-app-name',
|
|
35081
|
+
* }),
|
|
35082
|
+
* ],
|
|
35083
|
+
* });
|
|
35084
|
+
*
|
|
35085
|
+
* // component.ts - Uso
|
|
35086
|
+
* import { FeedbackService } from 'valtech-components';
|
|
35087
|
+
*
|
|
35088
|
+
* @Component({...})
|
|
35089
|
+
* export class MyComponent {
|
|
35090
|
+
* private feedbackService = inject(FeedbackService);
|
|
35091
|
+
*
|
|
35092
|
+
* async submitFeedback() {
|
|
35093
|
+
* const response = await this.feedbackService.createAsync(
|
|
35094
|
+
* 'feedback',
|
|
35095
|
+
* 'Título',
|
|
35096
|
+
* 'Descripción...'
|
|
35097
|
+
* );
|
|
35098
|
+
* }
|
|
35099
|
+
* }
|
|
35100
|
+
* ```
|
|
35101
|
+
*/
|
|
35102
|
+
// Configuration
|
|
35103
|
+
|
|
35104
|
+
/**
|
|
35105
|
+
* val-feedback-form
|
|
35106
|
+
*
|
|
35107
|
+
* Formulario reutilizable para enviar feedback desde cualquier parte de la aplicación.
|
|
35108
|
+
*
|
|
35109
|
+
* @example
|
|
35110
|
+
* ```html
|
|
35111
|
+
* <!-- Feedback general -->
|
|
35112
|
+
* <val-feedback-form
|
|
35113
|
+
* [props]="{ defaultType: 'feedback', showTypeSelector: true }"
|
|
35114
|
+
* (onSubmit)="handleSuccess($event)"
|
|
35115
|
+
* (onCancel)="closeModal()"
|
|
35116
|
+
* />
|
|
35117
|
+
*
|
|
35118
|
+
* <!-- Reportar contenido incorrecto -->
|
|
35119
|
+
* <val-feedback-form
|
|
35120
|
+
* [props]="{
|
|
35121
|
+
* defaultType: 'poor-content',
|
|
35122
|
+
* showTypeSelector: false,
|
|
35123
|
+
* contentRef: { contentId: article.id, contentType: 'article' },
|
|
35124
|
+
* submitButtonText: 'Reportar contenido'
|
|
35125
|
+
* }"
|
|
35126
|
+
* />
|
|
35127
|
+
* ```
|
|
35128
|
+
*/
|
|
35129
|
+
class FeedbackFormComponent {
|
|
35130
|
+
constructor() {
|
|
35131
|
+
/**
|
|
35132
|
+
* Configuración del formulario.
|
|
35133
|
+
*/
|
|
35134
|
+
this.props = {};
|
|
35135
|
+
/**
|
|
35136
|
+
* Evento emitido cuando el feedback se envía exitosamente.
|
|
35137
|
+
*/
|
|
35138
|
+
this.onSubmit = new EventEmitter();
|
|
35139
|
+
/**
|
|
35140
|
+
* Evento emitido cuando el usuario cancela.
|
|
35141
|
+
*/
|
|
35142
|
+
this.onCancel = new EventEmitter();
|
|
35143
|
+
this.fb = inject(FormBuilder);
|
|
35144
|
+
this.feedbackService = inject(FeedbackService);
|
|
35145
|
+
this.i18n = inject(I18nService);
|
|
35146
|
+
this.typeOptions = DEFAULT_FEEDBACK_TYPE_OPTIONS;
|
|
35147
|
+
this.isSubmitting = signal(false);
|
|
35148
|
+
this.isSuccess = signal(false);
|
|
35149
|
+
this.error = signal(null);
|
|
35150
|
+
addIcons({
|
|
35151
|
+
bugOutline,
|
|
35152
|
+
bulbOutline,
|
|
35153
|
+
chatbubbleOutline,
|
|
35154
|
+
checkmarkCircleOutline,
|
|
35155
|
+
closeCircleOutline,
|
|
35156
|
+
documentTextOutline,
|
|
35157
|
+
});
|
|
35158
|
+
}
|
|
35159
|
+
ngOnInit() {
|
|
35160
|
+
// Filtrar tipos habilitados si se especifica
|
|
35161
|
+
if (this.props.enabledTypes?.length) {
|
|
35162
|
+
this.typeOptions = this.typeOptions.filter((opt) => this.props.enabledTypes.includes(opt.value));
|
|
35163
|
+
}
|
|
35164
|
+
// Usar opciones personalizadas si se proporcionan
|
|
35165
|
+
if (this.props.typeOptions?.length) {
|
|
35166
|
+
this.typeOptions = this.props.typeOptions;
|
|
35167
|
+
}
|
|
35168
|
+
// Inicializar formulario
|
|
35169
|
+
this.form = this.fb.group({
|
|
35170
|
+
type: [this.props.defaultType || 'feedback', Validators.required],
|
|
35171
|
+
title: [
|
|
35172
|
+
'',
|
|
35173
|
+
[Validators.required, Validators.minLength(5), Validators.maxLength(200)],
|
|
35174
|
+
],
|
|
35175
|
+
description: [
|
|
35176
|
+
'',
|
|
35177
|
+
[Validators.required, Validators.minLength(10), Validators.maxLength(5000)],
|
|
35178
|
+
],
|
|
35179
|
+
});
|
|
35180
|
+
}
|
|
35181
|
+
async handleSubmit() {
|
|
35182
|
+
if (this.form.invalid || this.isSubmitting())
|
|
35183
|
+
return;
|
|
35184
|
+
this.isSubmitting.set(true);
|
|
35185
|
+
this.error.set(null);
|
|
35186
|
+
this.isSuccess.set(false);
|
|
35187
|
+
try {
|
|
35188
|
+
const { type, title, description } = this.form.value;
|
|
35189
|
+
const response = await this.feedbackService.createAsync(type, title, description, [], // attachments (por ahora vacío)
|
|
35190
|
+
this.props.contentRef);
|
|
35191
|
+
this.isSuccess.set(true);
|
|
35192
|
+
this.form.reset({ type: this.props.defaultType || 'feedback' });
|
|
35193
|
+
this.onSubmit.emit({
|
|
35194
|
+
response,
|
|
35195
|
+
type: type,
|
|
35196
|
+
title,
|
|
35197
|
+
});
|
|
35198
|
+
}
|
|
35199
|
+
catch (err) {
|
|
35200
|
+
this.error.set(err.error?.message || err.message || this.i18n.t('feedbackError'));
|
|
35201
|
+
}
|
|
35202
|
+
finally {
|
|
35203
|
+
this.isSubmitting.set(false);
|
|
35204
|
+
}
|
|
35205
|
+
}
|
|
35206
|
+
onCancelClick() {
|
|
35207
|
+
this.onCancel.emit();
|
|
35208
|
+
}
|
|
35209
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeedbackFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
35210
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FeedbackFormComponent, isStandalone: true, selector: "val-feedback-form", inputs: { props: "props" }, outputs: { onSubmit: "onSubmit", onCancel: "onCancel" }, ngImport: i0, template: `
|
|
35211
|
+
<form
|
|
35212
|
+
[formGroup]="form"
|
|
35213
|
+
(ngSubmit)="handleSubmit()"
|
|
35214
|
+
class="feedback-form"
|
|
35215
|
+
[class.compact]="props.compact"
|
|
35216
|
+
[ngClass]="props.cssClass"
|
|
35217
|
+
>
|
|
35218
|
+
<!-- Type selector -->
|
|
35219
|
+
@if (props.showTypeSelector !== false) {
|
|
35220
|
+
<ion-item>
|
|
35221
|
+
<ion-select
|
|
35222
|
+
formControlName="type"
|
|
35223
|
+
[label]="i18n.t('feedbackType')"
|
|
35224
|
+
labelPlacement="floating"
|
|
35225
|
+
interface="popover"
|
|
35226
|
+
>
|
|
35227
|
+
@for (option of typeOptions; track option.value) {
|
|
35228
|
+
<ion-select-option [value]="option.value">
|
|
35229
|
+
{{ option.label }}
|
|
35230
|
+
</ion-select-option>
|
|
35231
|
+
}
|
|
35232
|
+
</ion-select>
|
|
35233
|
+
</ion-item>
|
|
35234
|
+
}
|
|
35235
|
+
|
|
35236
|
+
<!-- Title -->
|
|
35237
|
+
<ion-item>
|
|
35238
|
+
<ion-textarea
|
|
35239
|
+
formControlName="title"
|
|
35240
|
+
[label]="props.titleLabel || i18n.t('title')"
|
|
35241
|
+
labelPlacement="floating"
|
|
35242
|
+
[placeholder]="props.titlePlaceholder || i18n.t('titlePlaceholder')"
|
|
35243
|
+
[maxlength]="200"
|
|
35244
|
+
[counter]="true"
|
|
35245
|
+
[autoGrow]="false"
|
|
35246
|
+
rows="1"
|
|
35247
|
+
></ion-textarea>
|
|
35248
|
+
</ion-item>
|
|
35249
|
+
@if (form.get('title')?.invalid && form.get('title')?.touched) {
|
|
35250
|
+
<ion-note color="danger" class="ion-padding-start">
|
|
35251
|
+
{{ i18n.t('titleValidation') }}
|
|
35252
|
+
</ion-note>
|
|
35253
|
+
}
|
|
35254
|
+
|
|
35255
|
+
<!-- Description -->
|
|
35256
|
+
<ion-item>
|
|
35257
|
+
<ion-textarea
|
|
35258
|
+
formControlName="description"
|
|
35259
|
+
[label]="props.descriptionLabel || i18n.t('description')"
|
|
35260
|
+
labelPlacement="floating"
|
|
35261
|
+
[placeholder]="props.descriptionPlaceholder || i18n.t('descriptionPlaceholder')"
|
|
35262
|
+
[maxlength]="5000"
|
|
35263
|
+
[counter]="true"
|
|
35264
|
+
[autoGrow]="true"
|
|
35265
|
+
rows="4"
|
|
35266
|
+
></ion-textarea>
|
|
35267
|
+
</ion-item>
|
|
35268
|
+
@if (form.get('description')?.invalid && form.get('description')?.touched) {
|
|
35269
|
+
<ion-note color="danger" class="ion-padding-start">
|
|
35270
|
+
{{ i18n.t('descriptionValidation') }}
|
|
35271
|
+
</ion-note>
|
|
35272
|
+
}
|
|
35273
|
+
|
|
35274
|
+
<!-- Error message -->
|
|
35275
|
+
@if (error()) {
|
|
35276
|
+
<div class="feedback-alert error">
|
|
35277
|
+
<ion-icon name="close-circle-outline"></ion-icon>
|
|
35278
|
+
<span>{{ error() }}</span>
|
|
35279
|
+
</div>
|
|
35280
|
+
}
|
|
35281
|
+
|
|
35282
|
+
<!-- Success message -->
|
|
35283
|
+
@if (isSuccess()) {
|
|
35284
|
+
<div class="feedback-alert success">
|
|
35285
|
+
<ion-icon name="checkmark-circle-outline"></ion-icon>
|
|
35286
|
+
<span>{{ props.successMessage || i18n.t('feedbackSuccess') }}</span>
|
|
35287
|
+
</div>
|
|
35288
|
+
}
|
|
35289
|
+
|
|
35290
|
+
<!-- Actions -->
|
|
35291
|
+
<div class="form-actions">
|
|
35292
|
+
@if (props.cancelButtonText) {
|
|
35293
|
+
<ion-button
|
|
35294
|
+
fill="outline"
|
|
35295
|
+
color="medium"
|
|
35296
|
+
type="button"
|
|
35297
|
+
(click)="onCancelClick()"
|
|
35298
|
+
>
|
|
35299
|
+
{{ props.cancelButtonText }}
|
|
35300
|
+
</ion-button>
|
|
35301
|
+
}
|
|
35302
|
+
<ion-button
|
|
35303
|
+
type="submit"
|
|
35304
|
+
[disabled]="form.invalid || isSubmitting()"
|
|
35305
|
+
expand="block"
|
|
35306
|
+
>
|
|
35307
|
+
@if (isSubmitting()) {
|
|
35308
|
+
<ion-spinner name="crescent"></ion-spinner>
|
|
35309
|
+
} @else {
|
|
35310
|
+
{{ props.submitButtonText || i18n.t('submit') }}
|
|
35311
|
+
}
|
|
35312
|
+
</ion-button>
|
|
35313
|
+
</div>
|
|
35314
|
+
</form>
|
|
35315
|
+
`, isInline: true, styles: [".feedback-form{display:flex;flex-direction:column;gap:8px;&.compact{gap:4px}}.form-actions{display:flex;gap:8px;margin-top:16px;justify-content:flex-end;ion-button{flex:1}}.feedback-alert{display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:8px;margin-top:8px;&.error{background-color:var(--ion-color-danger-tint);color:var(--ion-color-danger-shade)}&.success{background-color:var(--ion-color-success-tint);color:var(--ion-color-success-shade)}ion-icon{font-size:20px}}ion-note{font-size:12px;margin-top:-4px;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
|
|
35316
|
+
}
|
|
35317
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FeedbackFormComponent, decorators: [{
|
|
35318
|
+
type: Component,
|
|
35319
|
+
args: [{ selector: 'val-feedback-form', standalone: true, imports: [
|
|
35320
|
+
CommonModule,
|
|
35321
|
+
ReactiveFormsModule,
|
|
35322
|
+
IonButton,
|
|
35323
|
+
IonIcon,
|
|
35324
|
+
IonItem,
|
|
35325
|
+
IonLabel,
|
|
35326
|
+
IonList,
|
|
35327
|
+
IonNote,
|
|
35328
|
+
IonSelect,
|
|
35329
|
+
IonSelectOption,
|
|
35330
|
+
IonSpinner,
|
|
35331
|
+
IonText,
|
|
35332
|
+
IonTextarea,
|
|
35333
|
+
], template: `
|
|
35334
|
+
<form
|
|
35335
|
+
[formGroup]="form"
|
|
35336
|
+
(ngSubmit)="handleSubmit()"
|
|
35337
|
+
class="feedback-form"
|
|
35338
|
+
[class.compact]="props.compact"
|
|
35339
|
+
[ngClass]="props.cssClass"
|
|
35340
|
+
>
|
|
35341
|
+
<!-- Type selector -->
|
|
35342
|
+
@if (props.showTypeSelector !== false) {
|
|
35343
|
+
<ion-item>
|
|
35344
|
+
<ion-select
|
|
35345
|
+
formControlName="type"
|
|
35346
|
+
[label]="i18n.t('feedbackType')"
|
|
35347
|
+
labelPlacement="floating"
|
|
35348
|
+
interface="popover"
|
|
35349
|
+
>
|
|
35350
|
+
@for (option of typeOptions; track option.value) {
|
|
35351
|
+
<ion-select-option [value]="option.value">
|
|
35352
|
+
{{ option.label }}
|
|
35353
|
+
</ion-select-option>
|
|
35354
|
+
}
|
|
35355
|
+
</ion-select>
|
|
35356
|
+
</ion-item>
|
|
35357
|
+
}
|
|
35358
|
+
|
|
35359
|
+
<!-- Title -->
|
|
35360
|
+
<ion-item>
|
|
35361
|
+
<ion-textarea
|
|
35362
|
+
formControlName="title"
|
|
35363
|
+
[label]="props.titleLabel || i18n.t('title')"
|
|
35364
|
+
labelPlacement="floating"
|
|
35365
|
+
[placeholder]="props.titlePlaceholder || i18n.t('titlePlaceholder')"
|
|
35366
|
+
[maxlength]="200"
|
|
35367
|
+
[counter]="true"
|
|
35368
|
+
[autoGrow]="false"
|
|
35369
|
+
rows="1"
|
|
35370
|
+
></ion-textarea>
|
|
35371
|
+
</ion-item>
|
|
35372
|
+
@if (form.get('title')?.invalid && form.get('title')?.touched) {
|
|
35373
|
+
<ion-note color="danger" class="ion-padding-start">
|
|
35374
|
+
{{ i18n.t('titleValidation') }}
|
|
35375
|
+
</ion-note>
|
|
35376
|
+
}
|
|
35377
|
+
|
|
35378
|
+
<!-- Description -->
|
|
35379
|
+
<ion-item>
|
|
35380
|
+
<ion-textarea
|
|
35381
|
+
formControlName="description"
|
|
35382
|
+
[label]="props.descriptionLabel || i18n.t('description')"
|
|
35383
|
+
labelPlacement="floating"
|
|
35384
|
+
[placeholder]="props.descriptionPlaceholder || i18n.t('descriptionPlaceholder')"
|
|
35385
|
+
[maxlength]="5000"
|
|
35386
|
+
[counter]="true"
|
|
35387
|
+
[autoGrow]="true"
|
|
35388
|
+
rows="4"
|
|
35389
|
+
></ion-textarea>
|
|
35390
|
+
</ion-item>
|
|
35391
|
+
@if (form.get('description')?.invalid && form.get('description')?.touched) {
|
|
35392
|
+
<ion-note color="danger" class="ion-padding-start">
|
|
35393
|
+
{{ i18n.t('descriptionValidation') }}
|
|
35394
|
+
</ion-note>
|
|
35395
|
+
}
|
|
35396
|
+
|
|
35397
|
+
<!-- Error message -->
|
|
35398
|
+
@if (error()) {
|
|
35399
|
+
<div class="feedback-alert error">
|
|
35400
|
+
<ion-icon name="close-circle-outline"></ion-icon>
|
|
35401
|
+
<span>{{ error() }}</span>
|
|
35402
|
+
</div>
|
|
35403
|
+
}
|
|
35404
|
+
|
|
35405
|
+
<!-- Success message -->
|
|
35406
|
+
@if (isSuccess()) {
|
|
35407
|
+
<div class="feedback-alert success">
|
|
35408
|
+
<ion-icon name="checkmark-circle-outline"></ion-icon>
|
|
35409
|
+
<span>{{ props.successMessage || i18n.t('feedbackSuccess') }}</span>
|
|
35410
|
+
</div>
|
|
35411
|
+
}
|
|
35412
|
+
|
|
35413
|
+
<!-- Actions -->
|
|
35414
|
+
<div class="form-actions">
|
|
35415
|
+
@if (props.cancelButtonText) {
|
|
35416
|
+
<ion-button
|
|
35417
|
+
fill="outline"
|
|
35418
|
+
color="medium"
|
|
35419
|
+
type="button"
|
|
35420
|
+
(click)="onCancelClick()"
|
|
35421
|
+
>
|
|
35422
|
+
{{ props.cancelButtonText }}
|
|
35423
|
+
</ion-button>
|
|
35424
|
+
}
|
|
35425
|
+
<ion-button
|
|
35426
|
+
type="submit"
|
|
35427
|
+
[disabled]="form.invalid || isSubmitting()"
|
|
35428
|
+
expand="block"
|
|
35429
|
+
>
|
|
35430
|
+
@if (isSubmitting()) {
|
|
35431
|
+
<ion-spinner name="crescent"></ion-spinner>
|
|
35432
|
+
} @else {
|
|
35433
|
+
{{ props.submitButtonText || i18n.t('submit') }}
|
|
35434
|
+
}
|
|
35435
|
+
</ion-button>
|
|
35436
|
+
</div>
|
|
35437
|
+
</form>
|
|
35438
|
+
`, styles: [".feedback-form{display:flex;flex-direction:column;gap:8px;&.compact{gap:4px}}.form-actions{display:flex;gap:8px;margin-top:16px;justify-content:flex-end;ion-button{flex:1}}.feedback-alert{display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:8px;margin-top:8px;&.error{background-color:var(--ion-color-danger-tint);color:var(--ion-color-danger-shade)}&.success{background-color:var(--ion-color-success-tint);color:var(--ion-color-success-shade)}ion-icon{font-size:20px}}ion-note{font-size:12px;margin-top:-4px;margin-bottom:8px}\n"] }]
|
|
35439
|
+
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
35440
|
+
type: Input
|
|
35441
|
+
}], onSubmit: [{
|
|
35442
|
+
type: Output
|
|
35443
|
+
}], onCancel: [{
|
|
35444
|
+
type: Output
|
|
35445
|
+
}] } });
|
|
35446
|
+
|
|
35447
|
+
/**
|
|
35448
|
+
* Componente para reacciones de contenido con emojis.
|
|
35449
|
+
*
|
|
35450
|
+
* @example
|
|
35451
|
+
* ```html
|
|
35452
|
+
* <val-content-reaction
|
|
35453
|
+
* [props]="{
|
|
35454
|
+
* entityRef: { entityType: 'article', entityId: 'art-123' },
|
|
35455
|
+
* question: '¿Te fue útil este artículo?'
|
|
35456
|
+
* }"
|
|
35457
|
+
* (reactionSubmit)="onReactionSubmit($event)"
|
|
35458
|
+
* />
|
|
35459
|
+
* ```
|
|
35460
|
+
*/
|
|
35461
|
+
class ContentReactionComponent {
|
|
35462
|
+
constructor() {
|
|
35463
|
+
this.feedbackService = inject(FeedbackService);
|
|
35464
|
+
this.toast = inject(ToastService);
|
|
35465
|
+
this.i18n = inject(I18nService);
|
|
35466
|
+
this.props = {};
|
|
35467
|
+
this.reactionSubmit = new EventEmitter();
|
|
35468
|
+
this.reactionChange = new EventEmitter();
|
|
35469
|
+
// Estado reactivo
|
|
35470
|
+
this.state = signal({
|
|
35471
|
+
selectedValue: null,
|
|
35472
|
+
comment: '',
|
|
35473
|
+
isLoading: false,
|
|
35474
|
+
isSubmitted: false,
|
|
35475
|
+
hadPreviousReaction: false,
|
|
35476
|
+
error: null,
|
|
35477
|
+
});
|
|
35478
|
+
// Valores por defecto
|
|
35479
|
+
this.defaultEmojis = ['😞', '😐', '😊'];
|
|
35480
|
+
this.defaultLabels = [
|
|
35481
|
+
'No me ayudó',
|
|
35482
|
+
'Regular',
|
|
35483
|
+
'Muy útil',
|
|
35484
|
+
];
|
|
35485
|
+
this.reactionValues = ['negative', 'neutral', 'positive'];
|
|
35486
|
+
// Computed properties
|
|
35487
|
+
this.resolvedProps = computed(() => ({
|
|
35488
|
+
entityRef: this.props.entityRef,
|
|
35489
|
+
question: this.props.question || this.t('question'),
|
|
35490
|
+
showComment: this.props.showComment ?? true,
|
|
35491
|
+
commentPlaceholder: this.props.commentPlaceholder || this.t('commentPlaceholder'),
|
|
35492
|
+
maxCommentLength: this.props.maxCommentLength ?? 500,
|
|
35493
|
+
emojis: this.props.emojis || this.defaultEmojis,
|
|
35494
|
+
emojiLabels: this.props.emojiLabels || this.defaultLabels,
|
|
35495
|
+
showThankYou: this.props.showThankYou ?? true,
|
|
35496
|
+
thankYouMessage: this.props.thankYouMessage || this.t('thankYou'),
|
|
35497
|
+
disabled: this.props.disabled ?? false,
|
|
35498
|
+
readonly: this.props.readonly ?? false,
|
|
35499
|
+
}));
|
|
35500
|
+
this.showCommentField = computed(() => this.state().selectedValue !== null && this.resolvedProps().showComment);
|
|
35501
|
+
this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
|
|
35502
|
+
}
|
|
35503
|
+
ngOnInit() {
|
|
35504
|
+
this.loadPreviousReaction();
|
|
35505
|
+
}
|
|
35506
|
+
ngOnChanges(changes) {
|
|
35507
|
+
if (changes['props'] && !changes['props'].firstChange) {
|
|
35508
|
+
this.loadPreviousReaction();
|
|
35509
|
+
}
|
|
35510
|
+
}
|
|
35511
|
+
async loadPreviousReaction() {
|
|
35512
|
+
if (!this.props.entityRef)
|
|
35513
|
+
return;
|
|
35514
|
+
this.state.update((s) => ({ ...s, isLoading: true, error: null }));
|
|
35515
|
+
try {
|
|
35516
|
+
const check = await this.feedbackService.checkFeedback(this.props.entityRef.entityType, this.props.entityRef.entityId);
|
|
35517
|
+
if (check.hasFeedback && check.reactionValue) {
|
|
35518
|
+
this.state.update((s) => ({
|
|
35519
|
+
...s,
|
|
35520
|
+
selectedValue: check.reactionValue,
|
|
35521
|
+
hadPreviousReaction: true,
|
|
35522
|
+
isLoading: false,
|
|
35523
|
+
isSubmitted: true,
|
|
35524
|
+
}));
|
|
35525
|
+
}
|
|
35526
|
+
else {
|
|
35527
|
+
this.state.update((s) => ({ ...s, isLoading: false }));
|
|
35528
|
+
}
|
|
35529
|
+
}
|
|
35530
|
+
catch (error) {
|
|
35531
|
+
console.error('Error loading previous reaction:', error);
|
|
35532
|
+
this.state.update((s) => ({ ...s, isLoading: false }));
|
|
35533
|
+
}
|
|
35534
|
+
}
|
|
35535
|
+
selectReaction(value) {
|
|
35536
|
+
if (this.resolvedProps().disabled || this.resolvedProps().readonly)
|
|
35537
|
+
return;
|
|
35538
|
+
const previousValue = this.state().selectedValue;
|
|
35539
|
+
this.state.update((s) => ({
|
|
35540
|
+
...s,
|
|
35541
|
+
selectedValue: value,
|
|
35542
|
+
isSubmitted: false,
|
|
35543
|
+
error: null,
|
|
35544
|
+
}));
|
|
35545
|
+
this.reactionChange.emit({ value, previousValue });
|
|
35546
|
+
}
|
|
35547
|
+
async submitReaction() {
|
|
35548
|
+
const currentState = this.state();
|
|
35549
|
+
const props = this.resolvedProps();
|
|
35550
|
+
if (!currentState.selectedValue || props.disabled)
|
|
35551
|
+
return;
|
|
35552
|
+
this.state.update((s) => ({ ...s, isLoading: true, error: null }));
|
|
35553
|
+
try {
|
|
35554
|
+
await this.feedbackService.createReaction(props.entityRef, currentState.selectedValue, currentState.comment || undefined);
|
|
35555
|
+
this.state.update((s) => ({
|
|
35556
|
+
...s,
|
|
35557
|
+
isLoading: false,
|
|
35558
|
+
isSubmitted: true,
|
|
35559
|
+
hadPreviousReaction: true,
|
|
35560
|
+
}));
|
|
35561
|
+
this.reactionSubmit.emit({
|
|
35562
|
+
value: currentState.selectedValue,
|
|
35563
|
+
comment: currentState.comment || undefined,
|
|
35564
|
+
entityRef: props.entityRef,
|
|
35565
|
+
isUpdate: currentState.hadPreviousReaction,
|
|
35566
|
+
});
|
|
35567
|
+
if (props.showThankYou) {
|
|
35568
|
+
this.toast.show({
|
|
35569
|
+
message: props.thankYouMessage,
|
|
35570
|
+
duration: 2000,
|
|
35571
|
+
position: 'bottom',
|
|
35572
|
+
color: 'success',
|
|
35573
|
+
});
|
|
35574
|
+
}
|
|
35575
|
+
}
|
|
35576
|
+
catch (error) {
|
|
35577
|
+
console.error('Error submitting reaction:', error);
|
|
35578
|
+
this.state.update((s) => ({
|
|
35579
|
+
...s,
|
|
35580
|
+
isLoading: false,
|
|
35581
|
+
error: this.t('errorSubmitting'),
|
|
35582
|
+
}));
|
|
35583
|
+
this.toast.show({
|
|
35584
|
+
message: this.t('errorSubmitting'),
|
|
35585
|
+
duration: 3000,
|
|
35586
|
+
position: 'bottom',
|
|
35587
|
+
color: 'danger',
|
|
35588
|
+
});
|
|
35589
|
+
}
|
|
35590
|
+
}
|
|
35591
|
+
updateComment(event) {
|
|
35592
|
+
const value = event.detail.value || '';
|
|
35593
|
+
this.state.update((s) => ({ ...s, comment: value }));
|
|
35594
|
+
}
|
|
35595
|
+
getEmoji(index) {
|
|
35596
|
+
return this.resolvedProps().emojis[index];
|
|
35597
|
+
}
|
|
35598
|
+
getEmojiLabel(index) {
|
|
35599
|
+
return this.resolvedProps().emojiLabels[index];
|
|
35600
|
+
}
|
|
35601
|
+
isSelected(value) {
|
|
35602
|
+
return this.state().selectedValue === value;
|
|
35603
|
+
}
|
|
35604
|
+
t(key) {
|
|
35605
|
+
const translations = {
|
|
35606
|
+
question: '¿Te resultó útil este contenido?',
|
|
35607
|
+
commentPlaceholder: 'Cuéntanos más (opcional)...',
|
|
35608
|
+
submit: 'Enviar opinión',
|
|
35609
|
+
update: 'Actualizar opinión',
|
|
35610
|
+
thankYou: '¡Gracias por tu opinión!',
|
|
35611
|
+
submitted: 'Tu opinión ha sido registrada',
|
|
35612
|
+
errorSubmitting: 'Error al enviar. Intenta de nuevo.',
|
|
35613
|
+
};
|
|
35614
|
+
return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
|
|
35615
|
+
}
|
|
35616
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
35617
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"content-reaction\"\n [class.disabled]=\"resolvedProps().disabled\"\n [class.readonly]=\"resolvedProps().readonly\"\n>\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">{{ resolvedProps().question }}</p>\n\n <!-- Emojis -->\n <div class=\"emoji-container\">\n @for (value of reactionValues; track value; let i = $index) {\n <button\n type=\"button\"\n class=\"emoji-button\"\n [class.selected]=\"isSelected(value)\"\n [class.negative]=\"value === 'negative' && isSelected(value)\"\n [class.neutral]=\"value === 'neutral' && isSelected(value)\"\n [class.positive]=\"value === 'positive' && isSelected(value)\"\n [attr.aria-label]=\"getEmojiLabel(i)\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji\">{{ getEmoji(i) }}</span>\n </button>\n }\n </div>\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\">\n {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }}\n </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button\n expand=\"block\"\n [disabled]=\"!canSubmit()\"\n (click)=\"submitReaction()\"\n class=\"submit-button\"\n >\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else {\n {{ state().hadPreviousReaction ? t('update') : t('submit') }}\n }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">\n {{ t('submitted') }}\n </p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n }\n }\n</div>\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:16px;font-weight:500;color:var(--ion-color-dark);margin:0 0 16px}.emoji-container{display:flex;justify-content:center;gap:24px;margin-bottom:20px}.emoji-button{background:transparent;border:2px solid var(--ion-color-light-shade);border-radius:50%;width:64px;height:64px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;justify-content:center;padding:0}.emoji-button .emoji{font-size:32px;transition:transform .3s cubic-bezier(.4,0,.2,1);line-height:1}.emoji-button:hover:not(:disabled){transform:scale(1.1);border-color:var(--ion-color-medium)}.emoji-button:focus{outline:2px solid var(--ion-color-primary);outline-offset:2px}.emoji-button.selected{transform:scale(1.2)}.emoji-button.selected .emoji{transform:scale(1.1)}.emoji-button.selected.negative{border-color:var(--ion-color-danger);background:var(--ion-color-danger-tint);box-shadow:0 4px 12px rgba(var(--ion-color-danger-rgb),.3)}.emoji-button.selected.neutral{border-color:var(--ion-color-warning);background:var(--ion-color-warning-tint);box-shadow:0 4px 12px rgba(var(--ion-color-warning-rgb),.3)}.emoji-button.selected.positive{border-color:var(--ion-color-success);background:var(--ion-color-success-tint);box-shadow:0 4px 12px rgba(var(--ion-color-success-rgb),.3)}.emoji-button:disabled{cursor:not-allowed;opacity:.5}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: var(--ion-color-light);--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:12px;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px;--border-radius: 8px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:14px}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
|
|
35618
|
+
}
|
|
35619
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
|
|
35620
|
+
type: Component,
|
|
35621
|
+
args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "<div\n class=\"content-reaction\"\n [class.disabled]=\"resolvedProps().disabled\"\n [class.readonly]=\"resolvedProps().readonly\"\n>\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">{{ resolvedProps().question }}</p>\n\n <!-- Emojis -->\n <div class=\"emoji-container\">\n @for (value of reactionValues; track value; let i = $index) {\n <button\n type=\"button\"\n class=\"emoji-button\"\n [class.selected]=\"isSelected(value)\"\n [class.negative]=\"value === 'negative' && isSelected(value)\"\n [class.neutral]=\"value === 'neutral' && isSelected(value)\"\n [class.positive]=\"value === 'positive' && isSelected(value)\"\n [attr.aria-label]=\"getEmojiLabel(i)\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji\">{{ getEmoji(i) }}</span>\n </button>\n }\n </div>\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\">\n {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }}\n </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button\n expand=\"block\"\n [disabled]=\"!canSubmit()\"\n (click)=\"submitReaction()\"\n class=\"submit-button\"\n >\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else {\n {{ state().hadPreviousReaction ? t('update') : t('submit') }}\n }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">\n {{ t('submitted') }}\n </p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n }\n }\n</div>\n", styles: [":host{display:block}.content-reaction{padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.question{font-size:16px;font-weight:500;color:var(--ion-color-dark);margin:0 0 16px}.emoji-container{display:flex;justify-content:center;gap:24px;margin-bottom:20px}.emoji-button{background:transparent;border:2px solid var(--ion-color-light-shade);border-radius:50%;width:64px;height:64px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:flex;align-items:center;justify-content:center;padding:0}.emoji-button .emoji{font-size:32px;transition:transform .3s cubic-bezier(.4,0,.2,1);line-height:1}.emoji-button:hover:not(:disabled){transform:scale(1.1);border-color:var(--ion-color-medium)}.emoji-button:focus{outline:2px solid var(--ion-color-primary);outline-offset:2px}.emoji-button.selected{transform:scale(1.2)}.emoji-button.selected .emoji{transform:scale(1.1)}.emoji-button.selected.negative{border-color:var(--ion-color-danger);background:var(--ion-color-danger-tint);box-shadow:0 4px 12px rgba(var(--ion-color-danger-rgb),.3)}.emoji-button.selected.neutral{border-color:var(--ion-color-warning);background:var(--ion-color-warning-tint);box-shadow:0 4px 12px rgba(var(--ion-color-warning-rgb),.3)}.emoji-button.selected.positive{border-color:var(--ion-color-success);background:var(--ion-color-success-tint);box-shadow:0 4px 12px rgba(var(--ion-color-success-rgb),.3)}.emoji-button:disabled{cursor:not-allowed;opacity:.5}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: var(--ion-color-light);--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:12px;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px;--border-radius: 8px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:14px}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
|
|
35622
|
+
}], propDecorators: { props: [{
|
|
35623
|
+
type: Input
|
|
35624
|
+
}], reactionSubmit: [{
|
|
35625
|
+
type: Output
|
|
35626
|
+
}], reactionChange: [{
|
|
35627
|
+
type: Output
|
|
35628
|
+
}] } });
|
|
35629
|
+
|
|
34764
35630
|
addIcons({ menuOutline, closeOutline });
|
|
34765
35631
|
/**
|
|
34766
35632
|
* val-docs-layout
|
|
@@ -36718,6 +37584,271 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
36718
37584
|
`, styles: [".docs-callout{--callout-bg: #fffbeb;--callout-border: #f59e0b;--callout-title: #92400e;--callout-text: #78350f;margin:1.5rem 0;padding:1rem 1.25rem;border-left:4px solid var(--callout-border);border-radius:0 8px 8px 0;background:var(--callout-bg)}.docs-callout__title{font-size:.9375rem;font-weight:700;color:var(--callout-title);margin-bottom:.5rem}.docs-callout__content{font-size:.9375rem;line-height:1.7;color:var(--callout-text)}.docs-callout__content p{margin:0}.docs-callout__content p+p{margin-top:.75rem}.docs-callout__content code{font-family:SF Mono,Fira Code,Monaco,Consolas,monospace;font-size:.875rem;background:#0000000f;padding:.125rem .375rem;border-radius:4px}.docs-callout__content a{color:var(--callout-title);text-decoration:underline;text-underline-offset:2px}.docs-callout__content a:hover{text-decoration:none}.docs-callout--note{--callout-bg: #fffbeb;--callout-border: #f59e0b;--callout-title: #92400e;--callout-text: #78350f}.docs-callout--warning{--callout-bg: #fff7ed;--callout-border: #f97316;--callout-title: #c2410c;--callout-text: #9a3412}.docs-callout--tip{--callout-bg: #ecfdf5;--callout-border: #10b981;--callout-title: #065f46;--callout-text: #047857}.docs-callout--danger{--callout-bg: #fef2f2;--callout-border: #ef4444;--callout-title: #991b1b;--callout-text: #b91c1c}.docs-callout--info{--callout-bg: #eff6ff;--callout-border: #3b82f6;--callout-title: #1e40af;--callout-text: #1d4ed8}:host-context(.dark) .docs-callout--note,:host-context([color-scheme=dark]) .docs-callout--note{--callout-bg: rgba(245, 158, 11, .1);--callout-border: #f59e0b;--callout-title: #fbbf24;--callout-text: #fcd34d}:host-context(.dark) .docs-callout--warning,:host-context([color-scheme=dark]) .docs-callout--warning{--callout-bg: rgba(249, 115, 22, .1);--callout-border: #f97316;--callout-title: #fb923c;--callout-text: #fdba74}:host-context(.dark) .docs-callout--tip,:host-context([color-scheme=dark]) .docs-callout--tip{--callout-bg: rgba(16, 185, 129, .1);--callout-border: #10b981;--callout-title: #34d399;--callout-text: #6ee7b7}:host-context(.dark) .docs-callout--danger,:host-context([color-scheme=dark]) .docs-callout--danger{--callout-bg: rgba(239, 68, 68, .1);--callout-border: #ef4444;--callout-title: #f87171;--callout-text: #fca5a5}:host-context(.dark) .docs-callout--info,:host-context([color-scheme=dark]) .docs-callout--info{--callout-bg: rgba(59, 130, 246, .1);--callout-border: #3b82f6;--callout-title: #60a5fa;--callout-text: #93c5fd}:host-context(.dark) .docs-callout__content code,:host-context([color-scheme=dark]) .docs-callout__content code{background:#ffffff1a}\n"] }]
|
|
36719
37585
|
}] });
|
|
36720
37586
|
|
|
37587
|
+
/**
|
|
37588
|
+
* val-docs-section
|
|
37589
|
+
*
|
|
37590
|
+
* A semantic section wrapper for documentation pages.
|
|
37591
|
+
* Automatically creates headings with IDs for TOC linking.
|
|
37592
|
+
*
|
|
37593
|
+
* @example Basic usage
|
|
37594
|
+
* ```html
|
|
37595
|
+
* <val-docs-section [props]="{ id: 'installation', title: 'Installation' }">
|
|
37596
|
+
* <p>Install the package using npm...</p>
|
|
37597
|
+
* <val-docs-code-example [props]="codeExample"></val-docs-code-example>
|
|
37598
|
+
* </val-docs-section>
|
|
37599
|
+
* ```
|
|
37600
|
+
*
|
|
37601
|
+
* @example With level 3 heading
|
|
37602
|
+
* ```html
|
|
37603
|
+
* <val-docs-section [props]="{ id: 'npm', title: 'Using npm', level: 3 }">
|
|
37604
|
+
* <val-docs-code-example [props]="npmExample"></val-docs-code-example>
|
|
37605
|
+
* </val-docs-section>
|
|
37606
|
+
* ```
|
|
37607
|
+
*/
|
|
37608
|
+
class DocsSectionComponent {
|
|
37609
|
+
constructor() {
|
|
37610
|
+
this.props = { id: '', title: '' };
|
|
37611
|
+
}
|
|
37612
|
+
get level() {
|
|
37613
|
+
return this.props.level ?? 2;
|
|
37614
|
+
}
|
|
37615
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocsSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
37616
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DocsSectionComponent, isStandalone: true, selector: "val-docs-section", inputs: { props: "props" }, ngImport: i0, template: `
|
|
37617
|
+
<section
|
|
37618
|
+
[id]="props.id"
|
|
37619
|
+
class="docs-section"
|
|
37620
|
+
[class]="props.cssClass"
|
|
37621
|
+
[class.docs-section--level-2]="level === 2"
|
|
37622
|
+
[class.docs-section--level-3]="level === 3"
|
|
37623
|
+
>
|
|
37624
|
+
@if (level === 2) {
|
|
37625
|
+
<h2 class="docs-section__title">{{ props.title }}</h2>
|
|
37626
|
+
} @else {
|
|
37627
|
+
<h3 class="docs-section__title">{{ props.title }}</h3>
|
|
37628
|
+
}
|
|
37629
|
+
|
|
37630
|
+
@if (props.description) {
|
|
37631
|
+
<p class="docs-section__description">{{ props.description }}</p>
|
|
37632
|
+
}
|
|
37633
|
+
|
|
37634
|
+
<div class="docs-section__content">
|
|
37635
|
+
<ng-content></ng-content>
|
|
37636
|
+
</div>
|
|
37637
|
+
</section>
|
|
37638
|
+
`, isInline: true, styles: [".docs-section{margin-bottom:2.5rem}.docs-section__title{margin:0 0 1rem;font-weight:600;color:var(--ion-text-color);scroll-margin-top:80px}.docs-section--level-2 .docs-section__title{font-size:1.5rem;padding-bottom:.5rem;border-bottom:1px solid var(--ion-border-color, #e0e0e0)}.docs-section--level-3 .docs-section__title{font-size:1.25rem}.docs-section__description{margin:0 0 1.25rem;color:var(--ion-color-medium);font-size:1rem;line-height:1.6}.docs-section__content>*:last-child{margin-bottom:0}:host-context(.dark) .docs-section--level-2 .docs-section__title,:host-context([color-scheme=\"dark\"]) .docs-section--level-2 .docs-section__title{border-bottom-color:var(--ion-border-color, #333)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
|
|
37639
|
+
}
|
|
37640
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocsSectionComponent, decorators: [{
|
|
37641
|
+
type: Component,
|
|
37642
|
+
args: [{ selector: 'val-docs-section', standalone: true, imports: [CommonModule], template: `
|
|
37643
|
+
<section
|
|
37644
|
+
[id]="props.id"
|
|
37645
|
+
class="docs-section"
|
|
37646
|
+
[class]="props.cssClass"
|
|
37647
|
+
[class.docs-section--level-2]="level === 2"
|
|
37648
|
+
[class.docs-section--level-3]="level === 3"
|
|
37649
|
+
>
|
|
37650
|
+
@if (level === 2) {
|
|
37651
|
+
<h2 class="docs-section__title">{{ props.title }}</h2>
|
|
37652
|
+
} @else {
|
|
37653
|
+
<h3 class="docs-section__title">{{ props.title }}</h3>
|
|
37654
|
+
}
|
|
37655
|
+
|
|
37656
|
+
@if (props.description) {
|
|
37657
|
+
<p class="docs-section__description">{{ props.description }}</p>
|
|
37658
|
+
}
|
|
37659
|
+
|
|
37660
|
+
<div class="docs-section__content">
|
|
37661
|
+
<ng-content></ng-content>
|
|
37662
|
+
</div>
|
|
37663
|
+
</section>
|
|
37664
|
+
`, styles: [".docs-section{margin-bottom:2.5rem}.docs-section__title{margin:0 0 1rem;font-weight:600;color:var(--ion-text-color);scroll-margin-top:80px}.docs-section--level-2 .docs-section__title{font-size:1.5rem;padding-bottom:.5rem;border-bottom:1px solid var(--ion-border-color, #e0e0e0)}.docs-section--level-3 .docs-section__title{font-size:1.25rem}.docs-section__description{margin:0 0 1.25rem;color:var(--ion-color-medium);font-size:1rem;line-height:1.6}.docs-section__content>*:last-child{margin-bottom:0}:host-context(.dark) .docs-section--level-2 .docs-section__title,:host-context([color-scheme=\"dark\"]) .docs-section--level-2 .docs-section__title{border-bottom-color:var(--ion-border-color, #333)}\n"] }]
|
|
37665
|
+
}], propDecorators: { props: [{
|
|
37666
|
+
type: Input
|
|
37667
|
+
}] } });
|
|
37668
|
+
|
|
37669
|
+
/**
|
|
37670
|
+
* val-docs-page
|
|
37671
|
+
*
|
|
37672
|
+
* A complete documentation page template that eliminates boilerplate.
|
|
37673
|
+
* Provides automatic TOC generation, navigation links, and consistent styling.
|
|
37674
|
+
*
|
|
37675
|
+
* @example Basic usage
|
|
37676
|
+
* ```html
|
|
37677
|
+
* <val-docs-page [props]="{
|
|
37678
|
+
* title: 'Button',
|
|
37679
|
+
* lead: 'A clickable element for user interactions.',
|
|
37680
|
+
* previousPage: { title: 'Quick Start', route: ['/docs', 'quick-start'] },
|
|
37681
|
+
* nextPage: { title: 'Card', route: ['/docs', 'components', 'card'] }
|
|
37682
|
+
* }">
|
|
37683
|
+
* <val-docs-section [props]="{ id: 'basic-usage', title: 'Basic Usage' }">
|
|
37684
|
+
* <p>Content here...</p>
|
|
37685
|
+
* </val-docs-section>
|
|
37686
|
+
*
|
|
37687
|
+
* <val-docs-section [props]="{ id: 'variants', title: 'Variants' }">
|
|
37688
|
+
* <p>More content...</p>
|
|
37689
|
+
* </val-docs-section>
|
|
37690
|
+
* </val-docs-page>
|
|
37691
|
+
* ```
|
|
37692
|
+
*
|
|
37693
|
+
* @example With badge
|
|
37694
|
+
* ```html
|
|
37695
|
+
* <val-docs-page [props]="{
|
|
37696
|
+
* title: 'New Component',
|
|
37697
|
+
* badge: 'New',
|
|
37698
|
+
* badgeColor: 'success'
|
|
37699
|
+
* }">
|
|
37700
|
+
* ...
|
|
37701
|
+
* </val-docs-page>
|
|
37702
|
+
* ```
|
|
37703
|
+
*/
|
|
37704
|
+
class DocsPageComponent {
|
|
37705
|
+
constructor() {
|
|
37706
|
+
this.elementRef = inject(ElementRef);
|
|
37707
|
+
this.props = { title: '' };
|
|
37708
|
+
this.tocItems = signal([]);
|
|
37709
|
+
this.observer = null;
|
|
37710
|
+
this.tocProps = computed(() => ({
|
|
37711
|
+
title: this.props.toc?.title ?? 'On this page',
|
|
37712
|
+
items: this.tocItems(),
|
|
37713
|
+
}));
|
|
37714
|
+
this.navLinksProps = computed(() => ({
|
|
37715
|
+
previous: this.props.previousPage
|
|
37716
|
+
? {
|
|
37717
|
+
label: this.props.navLabels?.previous ?? 'Previous',
|
|
37718
|
+
title: this.props.previousPage.title,
|
|
37719
|
+
route: this.props.previousPage.route,
|
|
37720
|
+
}
|
|
37721
|
+
: undefined,
|
|
37722
|
+
next: this.props.nextPage
|
|
37723
|
+
? {
|
|
37724
|
+
label: this.props.navLabels?.next ?? 'Next',
|
|
37725
|
+
title: this.props.nextPage.title,
|
|
37726
|
+
route: this.props.nextPage.route,
|
|
37727
|
+
}
|
|
37728
|
+
: undefined,
|
|
37729
|
+
}));
|
|
37730
|
+
this.showNavLinks = computed(() => {
|
|
37731
|
+
return !!this.props.previousPage || !!this.props.nextPage;
|
|
37732
|
+
});
|
|
37733
|
+
}
|
|
37734
|
+
ngAfterViewInit() {
|
|
37735
|
+
// Initial scan
|
|
37736
|
+
this.scanForSections();
|
|
37737
|
+
// Watch for dynamic content changes
|
|
37738
|
+
this.observer = new MutationObserver(() => {
|
|
37739
|
+
this.scanForSections();
|
|
37740
|
+
});
|
|
37741
|
+
const contentEl = this.elementRef.nativeElement.querySelector('.docs-page__sections');
|
|
37742
|
+
if (contentEl) {
|
|
37743
|
+
this.observer.observe(contentEl, { childList: true, subtree: true });
|
|
37744
|
+
}
|
|
37745
|
+
}
|
|
37746
|
+
ngOnDestroy() {
|
|
37747
|
+
this.observer?.disconnect();
|
|
37748
|
+
}
|
|
37749
|
+
scanForSections() {
|
|
37750
|
+
const contentEl = this.elementRef.nativeElement.querySelector('.docs-page__sections');
|
|
37751
|
+
if (!contentEl)
|
|
37752
|
+
return;
|
|
37753
|
+
const headings = contentEl.querySelectorAll('h2[id], h3[id], section[id] > h2, section[id] > h3');
|
|
37754
|
+
const items = [];
|
|
37755
|
+
headings.forEach((heading) => {
|
|
37756
|
+
// Get ID from heading or parent section
|
|
37757
|
+
let id = heading.id;
|
|
37758
|
+
if (!id && heading.parentElement?.tagName === 'SECTION') {
|
|
37759
|
+
id = heading.parentElement.id;
|
|
37760
|
+
}
|
|
37761
|
+
if (id) {
|
|
37762
|
+
const level = heading.tagName === 'H2' ? 2 : 3;
|
|
37763
|
+
items.push({ id, label: heading.textContent?.trim() || '', level });
|
|
37764
|
+
}
|
|
37765
|
+
});
|
|
37766
|
+
this.tocItems.set(items);
|
|
37767
|
+
}
|
|
37768
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
37769
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DocsPageComponent, isStandalone: true, selector: "val-docs-page", inputs: { props: "props" }, ngImport: i0, template: `
|
|
37770
|
+
<div class="docs-page" [class]="props.cssClass">
|
|
37771
|
+
<div class="docs-page__content" #content>
|
|
37772
|
+
<header class="docs-page__header">
|
|
37773
|
+
<div class="docs-page__title-row">
|
|
37774
|
+
<h1 class="docs-page__title">{{ props.title }}</h1>
|
|
37775
|
+
@if (props.badge) {
|
|
37776
|
+
<span
|
|
37777
|
+
class="docs-page__badge"
|
|
37778
|
+
[class.docs-page__badge--success]="props.badgeColor === 'success'"
|
|
37779
|
+
[class.docs-page__badge--warning]="props.badgeColor === 'warning'"
|
|
37780
|
+
[class.docs-page__badge--danger]="props.badgeColor === 'danger'"
|
|
37781
|
+
>
|
|
37782
|
+
{{ props.badge }}
|
|
37783
|
+
</span>
|
|
37784
|
+
}
|
|
37785
|
+
</div>
|
|
37786
|
+
@if (props.lead) {
|
|
37787
|
+
<p class="docs-page__lead">{{ props.lead }}</p>
|
|
37788
|
+
}
|
|
37789
|
+
</header>
|
|
37790
|
+
|
|
37791
|
+
<div class="docs-page__sections">
|
|
37792
|
+
<ng-content></ng-content>
|
|
37793
|
+
</div>
|
|
37794
|
+
|
|
37795
|
+
@if (showNavLinks()) {
|
|
37796
|
+
<val-docs-nav-links [props]="navLinksProps()"></val-docs-nav-links>
|
|
37797
|
+
}
|
|
37798
|
+
</div>
|
|
37799
|
+
|
|
37800
|
+
@if (!props.toc?.hide) {
|
|
37801
|
+
<aside class="docs-page__toc">
|
|
37802
|
+
<val-docs-toc [props]="tocProps()"></val-docs-toc>
|
|
37803
|
+
</aside>
|
|
37804
|
+
}
|
|
37805
|
+
</div>
|
|
37806
|
+
`, isInline: true, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem;@media (min-width: 1200px){grid-template-columns:1fr 220px;padding:2rem}&__content{min-width:0;max-width:900px}&__header{margin-bottom:2rem}&__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}&__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color);line-height:1.2;@media (min-width: 768px){font-size:2.5rem}}&__badge{display:inline-flex;align-items:center;padding:.25rem .625rem;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.025em;border-radius:4px;background:#00000014;color:var(--ion-color-medium-shade);&--success{background:var(--ion-color-success-tint, #e8f5e9);color:var(--ion-color-success-shade, #2e7d32)}&--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}&--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}}&__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium)}&__sections{>*:last-child{margin-bottom:0}}&__toc{display:none;@media (min-width: 1200px){display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}}// Dark mode :host-context(.dark),:host-context([color-scheme=\"dark\"]){.docs-page{&__badge{background:#ffffff1a;color:var(--ion-color-medium);&--success{background:#2e7d3233;color:#81c784}&--warning{background:#e6510033;color:#ffb74d}&--danger{background:#c6282833;color:#e57373}}}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DocsNavLinksComponent, selector: "val-docs-nav-links", inputs: ["props"], outputs: ["navigate"] }, { kind: "component", type: DocsTocComponent, selector: "val-docs-toc", inputs: ["props"], outputs: ["sectionChange"] }] }); }
|
|
37807
|
+
}
|
|
37808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DocsPageComponent, decorators: [{
|
|
37809
|
+
type: Component,
|
|
37810
|
+
args: [{ selector: 'val-docs-page', standalone: true, imports: [CommonModule, DocsNavLinksComponent, DocsTocComponent], template: `
|
|
37811
|
+
<div class="docs-page" [class]="props.cssClass">
|
|
37812
|
+
<div class="docs-page__content" #content>
|
|
37813
|
+
<header class="docs-page__header">
|
|
37814
|
+
<div class="docs-page__title-row">
|
|
37815
|
+
<h1 class="docs-page__title">{{ props.title }}</h1>
|
|
37816
|
+
@if (props.badge) {
|
|
37817
|
+
<span
|
|
37818
|
+
class="docs-page__badge"
|
|
37819
|
+
[class.docs-page__badge--success]="props.badgeColor === 'success'"
|
|
37820
|
+
[class.docs-page__badge--warning]="props.badgeColor === 'warning'"
|
|
37821
|
+
[class.docs-page__badge--danger]="props.badgeColor === 'danger'"
|
|
37822
|
+
>
|
|
37823
|
+
{{ props.badge }}
|
|
37824
|
+
</span>
|
|
37825
|
+
}
|
|
37826
|
+
</div>
|
|
37827
|
+
@if (props.lead) {
|
|
37828
|
+
<p class="docs-page__lead">{{ props.lead }}</p>
|
|
37829
|
+
}
|
|
37830
|
+
</header>
|
|
37831
|
+
|
|
37832
|
+
<div class="docs-page__sections">
|
|
37833
|
+
<ng-content></ng-content>
|
|
37834
|
+
</div>
|
|
37835
|
+
|
|
37836
|
+
@if (showNavLinks()) {
|
|
37837
|
+
<val-docs-nav-links [props]="navLinksProps()"></val-docs-nav-links>
|
|
37838
|
+
}
|
|
37839
|
+
</div>
|
|
37840
|
+
|
|
37841
|
+
@if (!props.toc?.hide) {
|
|
37842
|
+
<aside class="docs-page__toc">
|
|
37843
|
+
<val-docs-toc [props]="tocProps()"></val-docs-toc>
|
|
37844
|
+
</aside>
|
|
37845
|
+
}
|
|
37846
|
+
</div>
|
|
37847
|
+
`, styles: [".docs-page{display:grid;grid-template-columns:1fr;gap:2rem;max-width:1400px;margin:0 auto;padding:2rem 1.5rem;@media (min-width: 1200px){grid-template-columns:1fr 220px;padding:2rem}&__content{min-width:0;max-width:900px}&__header{margin-bottom:2rem}&__title-row{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}&__title{margin:0;font-size:2rem;font-weight:700;color:var(--ion-text-color);line-height:1.2;@media (min-width: 768px){font-size:2.5rem}}&__badge{display:inline-flex;align-items:center;padding:.25rem .625rem;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.025em;border-radius:4px;background:#00000014;color:var(--ion-color-medium-shade);&--success{background:var(--ion-color-success-tint, #e8f5e9);color:var(--ion-color-success-shade, #2e7d32)}&--warning{background:var(--ion-color-warning-tint, #fff3e0);color:var(--ion-color-warning-shade, #e65100)}&--danger{background:var(--ion-color-danger-tint, #ffebee);color:var(--ion-color-danger-shade, #c62828)}}&__lead{margin:1rem 0 0;font-size:1.125rem;line-height:1.7;color:var(--ion-color-medium)}&__sections{>*:last-child{margin-bottom:0}}&__toc{display:none;@media (min-width: 1200px){display:block;position:sticky;top:2rem;height:fit-content;max-height:calc(100vh - 4rem);overflow-y:auto}}}// Dark mode :host-context(.dark),:host-context([color-scheme=\"dark\"]){.docs-page{&__badge{background:#ffffff1a;color:var(--ion-color-medium);&--success{background:#2e7d3233;color:#81c784}&--warning{background:#e6510033;color:#ffb74d}&--danger{background:#c6282833;color:#e57373}}}}\n"] }]
|
|
37848
|
+
}], propDecorators: { props: [{
|
|
37849
|
+
type: Input
|
|
37850
|
+
}] } });
|
|
37851
|
+
|
|
36721
37852
|
/*
|
|
36722
37853
|
* Public API Surface of valtech-components
|
|
36723
37854
|
*/
|
|
@@ -36726,5 +37857,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
36726
37857
|
* Generated bundle index. Do not edit.
|
|
36727
37858
|
*/
|
|
36728
37859
|
|
|
36729
|
-
export { AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsSearchComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_ADS_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_DEFAULT_CONTENT, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
|
|
37860
|
+
export { AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, ContentReactionComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_ADS_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
|
|
36730
37861
|
//# sourceMappingURL=valtech-components.mjs.map
|