valtech-components 2.0.288 → 2.0.290

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.
@@ -1007,15 +1007,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1007
1007
  /**
1008
1008
  * LinkProcessorService - Service for processing text content to convert URLs and internal routes into clickable links.
1009
1009
  *
1010
- * This service automatically detects external URLs (http/https) and internal routes (starting with /)
1011
- * and converts them into HTML anchor elements with appropriate attributes.
1010
+ * This service automatically detects external URLs (http/https), internal routes (starting with /),
1011
+ * and Markdown-style links [text](url) and converts them into HTML anchor elements with appropriate attributes.
1012
1012
  *
1013
1013
  * @example Basic usage:
1014
1014
  * ```typescript
1015
1015
  * constructor(private linkProcessor: LinkProcessorService) {}
1016
1016
  *
1017
1017
  * processText() {
1018
- * const text = 'Visit https://example.com or go to /profile';
1018
+ * const text = 'Visit https://example.com, go to /profile, or [check docs](https://docs.example.com)';
1019
1019
  * const processed = this.linkProcessor.processLinks(text);
1020
1020
  * // Returns SafeHtml with clickable links
1021
1021
  * }
@@ -1024,14 +1024,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1024
1024
  class LinkProcessorService {
1025
1025
  constructor(sanitizer) {
1026
1026
  this.sanitizer = sanitizer;
1027
- // Regex para detectar URLs completas (http/https)
1028
- this.urlRegex = /(https?:\/\/[^\s]+)/g;
1029
- // Regex para detectar rutas internas (empiezan con / pero no son URLs completas)
1030
- this.internalRouteRegex = /(\s|^)(\/[^\s]*)/g;
1027
+ // Regex para detectar URLs completas (http/https) - permite caracteres válidos pero excluye puntuación al final
1028
+ this.urlRegex = /(https?:\/\/[^\s]+?)(?=[.,;!?()\s]|$)/g;
1029
+ // Regex para detectar rutas internas (empiezan con / pero no son URLs completas) - excluye puntuación al final
1030
+ this.internalRouteRegex = /(\s|^)(\/[^\s]*?)(?=[.,;!?()\s]|$)/g;
1031
+ // Regex para detectar enlaces estilo Markdown [texto](url)
1032
+ this.markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
1031
1033
  }
1032
1034
  /**
1033
1035
  * Procesa texto para convertir enlaces en elementos <a> clickeables.
1034
- * Detecta automáticamente URLs externas e internas y las convierte en enlaces.
1036
+ * Detecta automáticamente URLs externas, rutas internas y enlaces estilo Markdown.
1035
1037
  *
1036
1038
  * @param text - Texto a procesar
1037
1039
  * @param config - Configuración del procesamiento
@@ -1040,10 +1042,11 @@ class LinkProcessorService {
1040
1042
  * @example
1041
1043
  * ```typescript
1042
1044
  * const result = this.linkProcessor.processLinks(
1043
- * 'Visit https://example.com or /profile',
1045
+ * 'Visit https://example.com, go to /profile, or [check docs](https://docs.example.com)',
1044
1046
  * {
1045
1047
  * openExternalInNewTab: true,
1046
1048
  * openInternalInNewTab: false,
1049
+ * processMarkdownLinks: true,
1047
1050
  * linkClass: 'custom-link'
1048
1051
  * }
1049
1052
  * );
@@ -1052,33 +1055,61 @@ class LinkProcessorService {
1052
1055
  processLinks(text, config = {}) {
1053
1056
  if (!text)
1054
1057
  return '';
1055
- const { openExternalInNewTab = true, openInternalInNewTab = false, linkClass = 'processed-link', externalLinkClass = 'external-link', internalLinkClass = 'internal-link', } = config;
1058
+ const { openExternalInNewTab = true, openInternalInNewTab = false, linkClass = 'processed-link', externalLinkClass = 'external-link', internalLinkClass = 'internal-link', processMarkdownLinks = true, } = config;
1056
1059
  let hasLinks = false;
1057
1060
  let processedText = text;
1058
- // Procesar URLs externas primero
1059
- if (this.urlRegex.test(text)) {
1060
- hasLinks = true;
1061
- this.urlRegex.lastIndex = 0; // Reset regex
1062
- processedText = processedText.replace(this.urlRegex, url => {
1063
- const target = openExternalInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
1064
- const classes = `${linkClass} ${externalLinkClass}`.trim();
1065
- return `<a href="${url}"${target} class="${classes}">${url}</a>`;
1061
+ // 1. Procesar enlaces estilo Markdown [texto](url) primero
1062
+ if (processMarkdownLinks) {
1063
+ this.markdownLinkRegex.lastIndex = 0; // Reset regex
1064
+ processedText = processedText.replace(this.markdownLinkRegex, (match, linkText, url) => {
1065
+ hasLinks = true;
1066
+ const isExternal = /^https?:\/\//.test(url);
1067
+ const target = (isExternal ? openExternalInNewTab : openInternalInNewTab)
1068
+ ? isExternal
1069
+ ? ' target="_blank" rel="noopener noreferrer"'
1070
+ : ' target="_blank"'
1071
+ : '';
1072
+ const typeClass = isExternal ? externalLinkClass : internalLinkClass;
1073
+ const classes = `${linkClass} ${typeClass}`.trim();
1074
+ return `<a href="${url}"${target} class="${classes}">${linkText}</a>`;
1066
1075
  });
1067
1076
  }
1068
- // Procesar rutas internas después
1069
- if (this.internalRouteRegex.test(processedText)) {
1077
+ // 2. Procesar URLs externas directas (solo si no están ya en un enlace HTML)
1078
+ this.urlRegex.lastIndex = 0; // Reset regex
1079
+ processedText = processedText.replace(this.urlRegex, (fullMatch, url) => {
1080
+ // Verificar que no esté ya dentro de un enlace HTML existente
1081
+ const urlPosition = processedText.indexOf(fullMatch);
1082
+ const textBefore = processedText.substring(0, urlPosition);
1083
+ // Buscar la última apertura y cierre de enlace antes de esta posición
1084
+ const lastOpenTag = textBefore.lastIndexOf('<a ');
1085
+ const lastCloseTag = textBefore.lastIndexOf('</a>');
1086
+ // Si hay un tag <a abierto sin cerrar, no procesamos
1087
+ if (lastOpenTag > lastCloseTag) {
1088
+ return fullMatch; // Mantener original
1089
+ }
1070
1090
  hasLinks = true;
1071
- this.internalRouteRegex.lastIndex = 0; // Reset regex
1072
- processedText = processedText.replace(this.internalRouteRegex, (match, prefix, route) => {
1073
- // Solo procesar si no está ya dentro de un enlace
1074
- if (processedText.indexOf(`href="${route}"`) === -1) {
1075
- const target = openInternalInNewTab ? ' target="_blank"' : '';
1076
- const classes = `${linkClass} ${internalLinkClass}`.trim();
1077
- return `${prefix}<a href="${route}"${target} class="${classes}">${route}</a>`;
1078
- }
1079
- return match;
1080
- });
1081
- }
1091
+ const target = openExternalInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
1092
+ const classes = `${linkClass} ${externalLinkClass}`.trim();
1093
+ return `<a href="${url}"${target} class="${classes}">${url}</a>`;
1094
+ });
1095
+ // 3. Procesar rutas internas (solo si no están ya en un enlace HTML)
1096
+ this.internalRouteRegex.lastIndex = 0; // Reset regex
1097
+ processedText = processedText.replace(this.internalRouteRegex, (match, prefix, route) => {
1098
+ // Verificar que no esté ya dentro de un enlace HTML existente
1099
+ const matchPosition = processedText.indexOf(match);
1100
+ const textBefore = processedText.substring(0, matchPosition);
1101
+ // Buscar la última apertura y cierre de enlace antes de esta posición
1102
+ const lastOpenTag = textBefore.lastIndexOf('<a ');
1103
+ const lastCloseTag = textBefore.lastIndexOf('</a>');
1104
+ // Si hay un tag <a abierto sin cerrar, no procesamos
1105
+ if (lastOpenTag > lastCloseTag) {
1106
+ return match; // Mantener original
1107
+ }
1108
+ hasLinks = true;
1109
+ const target = openInternalInNewTab ? ' target="_blank"' : '';
1110
+ const classes = `${linkClass} ${internalLinkClass}`.trim();
1111
+ return `${prefix}<a href="${route}"${target} class="${classes}">${route}</a>`;
1112
+ });
1082
1113
  // Si hay enlaces, sanitizar el HTML
1083
1114
  if (hasLinks) {
1084
1115
  return this.sanitizer.bypassSecurityTrustHtml(processedText);
@@ -1086,14 +1117,14 @@ class LinkProcessorService {
1086
1117
  return text;
1087
1118
  }
1088
1119
  /**
1089
- * Detecta si un texto contiene enlaces (URLs o rutas internas).
1120
+ * Detecta si un texto contiene enlaces (URLs, rutas internas o enlaces Markdown).
1090
1121
  *
1091
1122
  * @param text - Texto a analizar
1092
1123
  * @returns true si contiene enlaces
1093
1124
  *
1094
1125
  * @example
1095
1126
  * ```typescript
1096
- * const hasLinks = this.linkProcessor.hasLinks('Visit https://example.com');
1127
+ * const hasLinks = this.linkProcessor.hasLinks('Visit https://example.com or [docs](https://docs.com)');
1097
1128
  * // Returns: true
1098
1129
  * ```
1099
1130
  */
@@ -1103,20 +1134,24 @@ class LinkProcessorService {
1103
1134
  // Reset regex indices
1104
1135
  this.urlRegex.lastIndex = 0;
1105
1136
  this.internalRouteRegex.lastIndex = 0;
1106
- return this.urlRegex.test(text) || this.internalRouteRegex.test(text);
1137
+ this.markdownLinkRegex.lastIndex = 0;
1138
+ return (this.urlRegex.test(text) ||
1139
+ this.internalRouteRegex.test(text) ||
1140
+ this.markdownLinkRegex.test(text));
1107
1141
  }
1108
1142
  /**
1109
1143
  * Extrae todos los enlaces de un texto.
1110
1144
  *
1111
1145
  * @param text - Texto a analizar
1112
- * @returns Array de enlaces encontrados con su tipo
1146
+ * @returns Array de enlaces encontrados con su tipo y texto (si es Markdown)
1113
1147
  *
1114
1148
  * @example
1115
1149
  * ```typescript
1116
- * const links = this.linkProcessor.extractLinks('Visit https://example.com or /profile');
1150
+ * const links = this.linkProcessor.extractLinks('Visit https://example.com, /profile, or [docs](https://docs.com)');
1117
1151
  * // Returns: [
1118
- * // { url: 'https://example.com', type: 'external' },
1119
- * // { url: '/profile', type: 'internal' }
1152
+ * // { url: 'https://example.com', type: 'external', text: 'https://example.com' },
1153
+ * // { url: '/profile', type: 'internal', text: '/profile' },
1154
+ * // { url: 'https://docs.com', type: 'external', text: 'docs' }
1120
1155
  * // ]
1121
1156
  * ```
1122
1157
  */
@@ -1127,14 +1162,30 @@ class LinkProcessorService {
1127
1162
  // Reset regex indices
1128
1163
  this.urlRegex.lastIndex = 0;
1129
1164
  this.internalRouteRegex.lastIndex = 0;
1130
- // Extraer URLs externas
1165
+ this.markdownLinkRegex.lastIndex = 0;
1166
+ // Extraer enlaces Markdown primero
1131
1167
  let match;
1168
+ while ((match = this.markdownLinkRegex.exec(text)) !== null) {
1169
+ const url = match[2];
1170
+ const linkText = match[1];
1171
+ const type = /^https?:\/\//.test(url) ? 'external' : 'internal';
1172
+ links.push({ url, type, text: linkText });
1173
+ }
1174
+ // Extraer URLs externas directas
1132
1175
  while ((match = this.urlRegex.exec(text)) !== null) {
1133
- links.push({ url: match[1], type: 'external' });
1176
+ const url = match[1];
1177
+ // Verificar que no esté ya capturado como Markdown link
1178
+ if (!links.some(link => link.url === url)) {
1179
+ links.push({ url, type: 'external', text: url });
1180
+ }
1134
1181
  }
1135
- // Extraer rutas internas
1182
+ // Extraer rutas internas directas
1136
1183
  while ((match = this.internalRouteRegex.exec(text)) !== null) {
1137
- links.push({ url: match[2], type: 'internal' });
1184
+ const url = match[2];
1185
+ // Verificar que no esté ya capturado como Markdown link
1186
+ if (!links.some(link => link.url === url)) {
1187
+ links.push({ url, type: 'internal', text: url });
1188
+ }
1138
1189
  }
1139
1190
  return links;
1140
1191
  }
@@ -2053,7 +2104,7 @@ class TextComponent {
2053
2104
  <p [class]="props.size" [class.bold]="props.bold">{{ displayContent$ | async }}</p>
2054
2105
  }
2055
2106
  </ion-text>
2056
- `, isInline: true, styles: ["@charset \"UTF-8\";:root{--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8}@media (prefers-color-scheme: dark){:root{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143,73,248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255,255,255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.small{font-size:.75rem;line-height:1.25rem;font-weight:400}.small.bold{font-size:.75rem;line-height:1.25rem;font-weight:700}.medium{font-size:.875rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.medium{font-size:1rem;line-height:1.5rem}}.medium.bold{font-size:.875rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.medium.bold{font-size:1rem;line-height:1.5rem}}.large{font-size:1rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.large{font-size:1.125rem;line-height:1.5rem}}.large.bold{font-size:1rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.large.bold{font-size:1.125rem;line-height:1.5rem}}.xlarge{font-size:1.125rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.xlarge{font-size:1.5rem;line-height:2rem}}.xlarge.bold{font-size:1.125rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.xlarge.bold{font-size:1.5rem;line-height:2rem}}:host ::ng-deep .processed-link{color:var(--ion-color-primary, #3880ff);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;transition:color .3s ease}:host ::ng-deep .processed-link:hover{color:var(--ion-color-primary-shade, #3171e0);text-decoration-thickness:2px}:host ::ng-deep .processed-link:active{color:var(--ion-color-primary-tint, #4c8dff)}:host ::ng-deep .external-link:after{content:\" \\2197\";font-size:.8em;opacity:.7}:host ::ng-deep .internal-link{color:var(--ion-color-secondary, #0cd1e8)}:host ::ng-deep .internal-link:hover{color:var(--ion-color-secondary-shade, #0bb8cc)}\n"], dependencies: [{ kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: ProcessLinksPipe, name: "processLinks" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2107
+ `, isInline: true, styles: [":root{--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8}@media (prefers-color-scheme: dark){:root{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143,73,248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255,255,255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.small{font-size:.75rem;line-height:1.25rem;font-weight:400}.small.bold{font-size:.75rem;line-height:1.25rem;font-weight:700}.medium{font-size:.875rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.medium{font-size:1rem;line-height:1.5rem}}.medium.bold{font-size:.875rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.medium.bold{font-size:1rem;line-height:1.5rem}}.large{font-size:1rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.large{font-size:1.125rem;line-height:1.5rem}}.large.bold{font-size:1rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.large.bold{font-size:1.125rem;line-height:1.5rem}}.xlarge{font-size:1.125rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.xlarge{font-size:1.5rem;line-height:2rem}}.xlarge.bold{font-size:1.125rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.xlarge.bold{font-size:1.5rem;line-height:2rem}}\n"], dependencies: [{ kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: ProcessLinksPipe, name: "processLinks" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2057
2108
  }
2058
2109
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextComponent, decorators: [{
2059
2110
  type: Component,
@@ -2069,7 +2120,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2069
2120
  <p [class]="props.size" [class.bold]="props.bold">{{ displayContent$ | async }}</p>
2070
2121
  }
2071
2122
  </ion-text>
2072
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["@charset \"UTF-8\";:root{--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8}@media (prefers-color-scheme: dark){:root{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143,73,248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255,255,255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.small{font-size:.75rem;line-height:1.25rem;font-weight:400}.small.bold{font-size:.75rem;line-height:1.25rem;font-weight:700}.medium{font-size:.875rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.medium{font-size:1rem;line-height:1.5rem}}.medium.bold{font-size:.875rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.medium.bold{font-size:1rem;line-height:1.5rem}}.large{font-size:1rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.large{font-size:1.125rem;line-height:1.5rem}}.large.bold{font-size:1rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.large.bold{font-size:1.125rem;line-height:1.5rem}}.xlarge{font-size:1.125rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.xlarge{font-size:1.5rem;line-height:2rem}}.xlarge.bold{font-size:1.125rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.xlarge.bold{font-size:1.5rem;line-height:2rem}}:host ::ng-deep .processed-link{color:var(--ion-color-primary, #3880ff);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;transition:color .3s ease}:host ::ng-deep .processed-link:hover{color:var(--ion-color-primary-shade, #3171e0);text-decoration-thickness:2px}:host ::ng-deep .processed-link:active{color:var(--ion-color-primary-tint, #4c8dff)}:host ::ng-deep .external-link:after{content:\" \\2197\";font-size:.8em;opacity:.7}:host ::ng-deep .internal-link{color:var(--ion-color-secondary, #0cd1e8)}:host ::ng-deep .internal-link:hover{color:var(--ion-color-secondary-shade, #0bb8cc)}\n"] }]
2123
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":root{--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8}@media (prefers-color-scheme: dark){:root{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143,73,248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255,255,255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.small{font-size:.75rem;line-height:1.25rem;font-weight:400}.small.bold{font-size:.75rem;line-height:1.25rem;font-weight:700}.medium{font-size:.875rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.medium{font-size:1rem;line-height:1.5rem}}.medium.bold{font-size:.875rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.medium.bold{font-size:1rem;line-height:1.5rem}}.large{font-size:1rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.large{font-size:1.125rem;line-height:1.5rem}}.large.bold{font-size:1rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.large.bold{font-size:1.125rem;line-height:1.5rem}}.xlarge{font-size:1.125rem;line-height:1.5rem;font-weight:400}@media (min-width: 768px){.xlarge{font-size:1.5rem;line-height:2rem}}.xlarge.bold{font-size:1.125rem;line-height:1.5rem;font-weight:700}@media (min-width: 768px){.xlarge.bold{font-size:1.5rem;line-height:2rem}}\n"] }]
2073
2124
  }], ctorParameters: () => [{ type: ContentService }, { type: LinkProcessorService }], propDecorators: { props: [{
2074
2125
  type: Input
2075
2126
  }] } });
@@ -6572,6 +6623,48 @@ class LinkProcessingExampleComponent {
6572
6623
  internalLinkClass: 'internal-same-tab',
6573
6624
  },
6574
6625
  };
6626
+ this.markdownLinksProps = {
6627
+ content: 'Consulta [la documentación de Angular](https://angular.io/docs) y ve a [configuración del perfil](/profile/settings) para más opciones.',
6628
+ size: 'medium',
6629
+ color: 'dark',
6630
+ bold: false,
6631
+ processLinks: true,
6632
+ linkConfig: {
6633
+ openExternalInNewTab: true,
6634
+ openInternalInNewTab: false,
6635
+ processMarkdownLinks: true,
6636
+ linkClass: 'markdown-link',
6637
+ externalLinkClass: 'markdown-external',
6638
+ internalLinkClass: 'markdown-internal',
6639
+ },
6640
+ };
6641
+ this.mixedFormatsProps = {
6642
+ content: 'Aquí hay [documentación oficial](https://angular.io/docs), un enlace directo https://github.com/angular/angular, y una ruta interna /dashboard/analytics. ¡Todos funcionan!',
6643
+ size: 'medium',
6644
+ color: 'dark',
6645
+ bold: false,
6646
+ processLinks: true,
6647
+ linkConfig: {
6648
+ openExternalInNewTab: true,
6649
+ openInternalInNewTab: false,
6650
+ processMarkdownLinks: true,
6651
+ linkClass: 'mixed-link',
6652
+ externalLinkClass: 'mixed-external',
6653
+ internalLinkClass: 'mixed-internal',
6654
+ },
6655
+ };
6656
+ this.punctuationTestProps = {
6657
+ content: 'URLs con puntuación: https://ionicframework.com/docs, revisa https://angular.io! También https://github.com/angular? Y finalmente https://typescript.org. ¡Todos deben funcionar correctamente!',
6658
+ size: 'medium',
6659
+ color: 'dark',
6660
+ bold: false,
6661
+ processLinks: true,
6662
+ linkConfig: {
6663
+ openExternalInNewTab: true,
6664
+ linkClass: 'punctuation-test',
6665
+ externalLinkClass: 'external-punct',
6666
+ },
6667
+ };
6575
6668
  }
6576
6669
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LinkProcessingExampleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6577
6670
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LinkProcessingExampleComponent, isStandalone: true, selector: "val-link-processing-example", ngImport: i0, template: `
@@ -6602,6 +6695,21 @@ class LinkProcessingExampleComponent {
6602
6695
  <h3>Enlaces sin abrir en nueva pestaña:</h3>
6603
6696
  <val-text [props]="sameTabLinksProps"></val-text>
6604
6697
  </div>
6698
+
6699
+ <div class="example-section">
6700
+ <h3>Enlaces estilo Markdown [texto](url):</h3>
6701
+ <val-text [props]="markdownLinksProps"></val-text>
6702
+ </div>
6703
+
6704
+ <div class="example-section">
6705
+ <h3>Mezcla de enlaces directos y Markdown:</h3>
6706
+ <val-text [props]="mixedFormatsProps"></val-text>
6707
+ </div>
6708
+
6709
+ <div class="example-section">
6710
+ <h3>Corrección de puntuación en URLs:</h3>
6711
+ <val-text [props]="punctuationTestProps"></val-text>
6712
+ </div>
6605
6713
  </div>
6606
6714
  `, isInline: true, styles: [".link-examples{padding:20px;max-width:800px}.example-section{margin-bottom:24px;padding:16px;border:1px solid var(--ion-color-light, #f4f5f8);border-radius:8px;background:var(--ion-color-light-tint, #f5f6f9)}h2{color:var(--ion-color-primary, #3880ff);margin-bottom:20px}h3{color:var(--ion-color-dark, #222428);margin-bottom:10px;font-size:16px}\n"], dependencies: [{ kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }] }); }
6607
6715
  }
@@ -6635,6 +6743,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
6635
6743
  <h3>Enlaces sin abrir en nueva pestaña:</h3>
6636
6744
  <val-text [props]="sameTabLinksProps"></val-text>
6637
6745
  </div>
6746
+
6747
+ <div class="example-section">
6748
+ <h3>Enlaces estilo Markdown [texto](url):</h3>
6749
+ <val-text [props]="markdownLinksProps"></val-text>
6750
+ </div>
6751
+
6752
+ <div class="example-section">
6753
+ <h3>Mezcla de enlaces directos y Markdown:</h3>
6754
+ <val-text [props]="mixedFormatsProps"></val-text>
6755
+ </div>
6756
+
6757
+ <div class="example-section">
6758
+ <h3>Corrección de puntuación en URLs:</h3>
6759
+ <val-text [props]="punctuationTestProps"></val-text>
6760
+ </div>
6638
6761
  </div>
6639
6762
  `, styles: [".link-examples{padding:20px;max-width:800px}.example-section{margin-bottom:24px;padding:16px;border:1px solid var(--ion-color-light, #f4f5f8);border-radius:8px;background:var(--ion-color-light-tint, #f5f6f9)}h2{color:var(--ion-color-primary, #3880ff);margin-bottom:20px}h3{color:var(--ion-color-dark, #222428);margin-bottom:10px;font-size:16px}\n"] }]
6640
6763
  }] });