valtech-components 2.0.326 → 2.0.328

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.
Files changed (28) hide show
  1. package/esm2022/lib/components/atoms/button/button.component.mjs +18 -18
  2. package/esm2022/lib/components/atoms/display/display.component.mjs +13 -16
  3. package/esm2022/lib/components/atoms/text/text.component.mjs +19 -19
  4. package/esm2022/lib/components/atoms/title/title.component.mjs +7 -7
  5. package/esm2022/lib/components/atoms/title/types.mjs +6 -2
  6. package/esm2022/lib/components/molecules/alert-box/alert-box.component.mjs +6 -6
  7. package/esm2022/lib/components/molecules/language-selector/language-selector.component.mjs +6 -17
  8. package/esm2022/lib/components/organisms/form/form.component.mjs +1 -1
  9. package/esm2022/lib/services/lang-provider/lang-provider.service.mjs +66 -1
  10. package/esm2022/lib/shared/utils/simple-content.mjs +119 -0
  11. package/esm2022/public-api.mjs +3 -3
  12. package/fesm2022/valtech-components.mjs +413 -680
  13. package/fesm2022/valtech-components.mjs.map +1 -1
  14. package/lib/components/atoms/button/button.component.d.ts +3 -3
  15. package/lib/components/atoms/display/display.component.d.ts +2 -3
  16. package/lib/components/atoms/text/text.component.d.ts +3 -3
  17. package/lib/components/atoms/title/title.component.d.ts +1 -2
  18. package/lib/components/atoms/title/types.d.ts +10 -4
  19. package/lib/components/molecules/alert-box/alert-box.component.d.ts +3 -3
  20. package/lib/components/molecules/language-selector/language-selector.component.d.ts +1 -3
  21. package/lib/services/lang-provider/lang-provider.service.d.ts +50 -0
  22. package/lib/shared/utils/simple-content.d.ts +120 -0
  23. package/package.json +1 -1
  24. package/public-api.d.ts +1 -2
  25. package/esm2022/lib/services/content.service.mjs +0 -327
  26. package/esm2022/lib/shared/utils/reactive-content.mjs +0 -117
  27. package/lib/services/content.service.d.ts +0 -296
  28. package/lib/shared/utils/reactive-content.d.ts +0 -109
@@ -5,7 +5,7 @@ import * as i1 from '@angular/common';
5
5
  import { CommonModule, NgStyle, AsyncPipe, NgFor, NgClass } from '@angular/common';
6
6
  import { addIcons } from 'ionicons';
7
7
  import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, 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, createOutline, trashOutline, playOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, chevronDown, language, globe, chevronBackOutline } from 'ionicons/icons';
8
- import { map, BehaviorSubject, distinctUntilChanged, shareReplay, Subscription, of, combineLatest } from 'rxjs';
8
+ import { BehaviorSubject, distinctUntilChanged, shareReplay, map, Subscription, of, combineLatest } from 'rxjs';
9
9
  import { Router, RouterLink } from '@angular/router';
10
10
  import { Browser } from '@capacitor/browser';
11
11
  import * as i1$1 from '@angular/platform-browser';
@@ -219,6 +219,125 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
219
219
  type: Output
220
220
  }] } });
221
221
 
222
+ /**
223
+ * Simplified content utilities for the new LangService-only system.
224
+ * This replaces the old reactive-content.ts with a much simpler approach.
225
+ */
226
+ /**
227
+ * Helper function to determine if component should use reactive content.
228
+ * @param metadata Component metadata
229
+ * @returns True if component should use reactive content
230
+ */
231
+ function shouldUseReactiveContent(metadata) {
232
+ return !metadata.content && !!(metadata.contentKey && metadata.contentClass);
233
+ }
234
+ /**
235
+ * Extract content configuration from metadata.
236
+ * @param metadata Component metadata
237
+ * @returns Content configuration for LangService
238
+ */
239
+ function extractContentConfig(metadata) {
240
+ return {
241
+ className: metadata.contentClass || '',
242
+ key: metadata.contentKey || '',
243
+ fallback: metadata.contentFallback,
244
+ };
245
+ }
246
+ /**
247
+ * Create reactive content metadata with defaults.
248
+ * @param config Partial content configuration
249
+ * @returns Complete reactive content metadata
250
+ */
251
+ function createReactiveContentMetadata(config) {
252
+ return {
253
+ content: config.content,
254
+ contentKey: config.contentKey,
255
+ contentClass: config.contentClass,
256
+ contentFallback: config.contentFallback,
257
+ };
258
+ }
259
+ /**
260
+ * Interpolate content string with values.
261
+ * Replaces placeholders like {{key}} or {key} with actual values.
262
+ *
263
+ * @param content - Content string with placeholders
264
+ * @param values - Values to interpolate
265
+ * @returns Interpolated string
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * interpolateContent('Hello {{name}}!', { name: 'World' })
270
+ * // Returns: 'Hello World!'
271
+ * ```
272
+ */
273
+ function interpolateContent$1(content, values) {
274
+ if (!values || !content) {
275
+ return content;
276
+ }
277
+ return content.replace(/\{\{?(\w+)\}?\}/g, (match, key) => {
278
+ const value = values[key];
279
+ return value !== undefined ? String(value) : match;
280
+ });
281
+ }
282
+ /**
283
+ * Check if component should use reactive content with interpolation.
284
+ * @param metadata Component metadata with interpolation
285
+ * @returns True if component should use reactive content with interpolation
286
+ */
287
+ function shouldUseReactiveContentWithInterpolation(metadata) {
288
+ return shouldUseReactiveContent(metadata) && !!metadata.contentInterpolation;
289
+ }
290
+ /**
291
+ * Extract content configuration with interpolation from metadata.
292
+ * @param metadata Component metadata with interpolation
293
+ * @returns Content configuration for LangService with interpolation data
294
+ */
295
+ function extractContentConfigWithInterpolation(metadata) {
296
+ return {
297
+ className: metadata.contentClass || '',
298
+ key: metadata.contentKey || '',
299
+ fallback: metadata.contentFallback,
300
+ interpolation: metadata.contentInterpolation,
301
+ };
302
+ }
303
+ /**
304
+ * Helper function to get reactive content with interpolation using LangService.
305
+ * This provides a unified way to get reactive, interpolated content.
306
+ *
307
+ * @param langService - The LangService instance
308
+ * @param metadata - Component metadata with interpolation
309
+ * @returns Observable that emits interpolated content
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * const content$ = fromContentWithInterpolation(this.langService, {
314
+ * contentClass: 'MyComponent',
315
+ * contentKey: 'greeting',
316
+ * contentInterpolation: { name: 'World' }
317
+ * });
318
+ * ```
319
+ */
320
+ function fromContentWithInterpolation$1(langService, // LangService type would cause circular dependency
321
+ metadata) {
322
+ // Observable<string> but avoiding import
323
+ const config = extractContentConfigWithInterpolation(metadata);
324
+ if (!config.className || !config.key) {
325
+ throw new Error('fromContentWithInterpolation requires both contentClass and contentKey');
326
+ }
327
+ return langService.getContentWithInterpolation(config.className, config.key, config.interpolation, config.fallback);
328
+ }
329
+ /**
330
+ * Helper function to get static content with interpolation.
331
+ * This provides interpolation for static content strings.
332
+ *
333
+ * @param content - Static content string
334
+ * @param interpolationData - Values to interpolate
335
+ * @returns Interpolated string
336
+ */
337
+ function interpolateStaticContent(content, interpolationData) {
338
+ return interpolateContent$1(content, interpolationData);
339
+ }
340
+
222
341
  const ENABLED = 'ENABLED';
223
342
  const DISABLED = 'DISABLED';
224
343
  const WORKING = 'WORKING';
@@ -434,191 +553,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
434
553
  }]
435
554
  }], ctorParameters: () => [] });
436
555
 
437
- /**
438
- * Create a reactive content observable from a content key.
439
- * This is the primary utility for the `fromContent` pattern with unified support
440
- * for both simple content and content with interpolation.
441
- *
442
- * @param langService - The language service instance
443
- * @param config - Content configuration with optional interpolation
444
- * @returns Observable that emits the content string and updates on language change
445
- *
446
- * @example Simple content:
447
- * ```typescript
448
- * // Component-specific content
449
- * this.title$ = fromContent(this.langService, {
450
- * className: 'HeaderComponent',
451
- * key: 'title',
452
- * fallback: 'Default Title'
453
- * });
454
- *
455
- * // Global content (no className needed)
456
- * this.saveButton$ = fromContent(this.langService, {
457
- * key: 'save'
458
- * });
459
- * ```
460
- *
461
- * @example Content with interpolation:
462
- * ```typescript
463
- * // Content: "Hello {name}, you have {count} messages"
464
- * this.greeting$ = fromContent(this.langService, {
465
- * className: 'WelcomeComponent',
466
- * key: 'greeting',
467
- * interpolation: { name: 'John', count: 5 }
468
- * });
469
- * // Results in: "Hello John, you have 5 messages"
470
- * ```
471
- */
472
- function fromContent(langService, config) {
473
- // Use _global as default className if not provided
474
- const finalClassName = config.className || '_global';
475
- const contentObservable = langService.getContent(finalClassName, config.key, config.fallback);
476
- // If interpolation is provided, apply it
477
- if (config.interpolation) {
478
- return contentObservable.pipe(map(content => interpolateContent(content, config.interpolation)));
479
- }
480
- return contentObservable;
481
- }
482
- /**
483
- * Create a reactive content observable with interpolation support.
484
- *
485
- * @deprecated Use fromContent() with interpolation property instead.
486
- * This method is kept for backward compatibility.
487
- *
488
- * @param langService - The language service instance
489
- * @param config - Interpolated content configuration
490
- * @returns Observable that emits the interpolated content string
491
- *
492
- * @example Migration:
493
- * ```typescript
494
- * // OLD (deprecated):
495
- * this.greeting$ = fromContentWithInterpolation(this.langService, {
496
- * className: 'WelcomeComponent',
497
- * key: 'greeting',
498
- * interpolation: { name: 'John', count: 5 }
499
- * });
500
- *
501
- * // NEW (recommended):
502
- * this.greeting$ = fromContent(this.langService, {
503
- * className: 'WelcomeComponent',
504
- * key: 'greeting',
505
- * interpolation: { name: 'John', count: 5 }
506
- * });
507
- * ```
508
- */
509
- function fromContentWithInterpolation(langService, config) {
510
- // Delegate to the unified fromContent method
511
- return fromContent(langService, config);
512
- }
513
- /**
514
- * Create multiple reactive content observables at once.
515
- * Useful when a component needs several content strings.
516
- *
517
- * @param langService - The language service instance
518
- * @param className - The component class name
519
- * @param keys - Array of content keys to retrieve
520
- * @returns Observable that emits an object with all requested content
521
- *
522
- * @example
523
- * ```typescript
524
- * this.content$ = fromMultipleContent(this.langService, 'FormComponent', [
525
- * 'title', 'submitButton', 'cancelButton'
526
- * ]);
527
- *
528
- * // In template
529
- * <ng-container *ngIf="content$ | async as content">
530
- * <h2>{{ content.title }}</h2>
531
- * <button>{{ content.submitButton }}</button>
532
- * <button>{{ content.cancelButton }}</button>
533
- * </ng-container>
534
- * ```
535
- */
536
- function fromMultipleContent(langService, className, keys) {
537
- return langService.getMultipleContent(className, keys);
538
- }
539
- /**
540
- * Helper function to interpolate values into a content string.
541
- * Replaces placeholders in the format {key} with corresponding values.
542
- *
543
- * @param content - The content string with placeholders
544
- * @param values - Object with values to interpolate
545
- * @returns The interpolated string
546
- *
547
- * @example
548
- * ```typescript
549
- * const result = interpolateContent("Hello {name}!", { name: "World" });
550
- * // Returns: "Hello World!"
551
- * ```
552
- */
553
- function interpolateContent(content, values) {
554
- if (!values)
555
- return content;
556
- return Object.entries(values).reduce((result, [key, value]) => {
557
- return result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
558
- }, content);
559
- }
560
- /**
561
- * Factory function to create a content helper bound to a specific component class.
562
- * This creates a more convenient API for components that need multiple content strings.
563
- *
564
- * @param langService - The language service instance
565
- * @param className - The component class name
566
- * @returns Object with convenient methods for content retrieval
567
- *
568
- * @example
569
- * ```typescript
570
- * export class MyComponent {
571
- * private content = createContentHelper(this.langService, 'MyComponent');
572
- *
573
- * constructor(private langService: LangService) {}
574
- *
575
- * ngOnInit() {
576
- * this.title$ = this.content.get('title');
577
- * this.allContent$ = this.content.getMultiple(['title', 'description']);
578
- * }
579
- * }
580
- * ```
581
- */
582
- function createContentHelper(langService, className) {
583
- return {
584
- /**
585
- * Get a single content string reactively.
586
- */
587
- get(key, fallback) {
588
- return fromContent(langService, { className, key, fallback });
589
- },
590
- /**
591
- * Get multiple content strings reactively.
592
- */
593
- getMultiple(keys) {
594
- return fromMultipleContent(langService, className, keys);
595
- },
596
- /**
597
- * Get content with interpolation.
598
- */
599
- getWithInterpolation(key, interpolation, fallback) {
600
- return fromContentWithInterpolation(langService, {
601
- className,
602
- key,
603
- interpolation,
604
- fallback,
605
- });
606
- },
607
- /**
608
- * Get a single content string synchronously (current language only).
609
- */
610
- getText(key, fallback) {
611
- return langService.getText(className, key, fallback);
612
- },
613
- /**
614
- * Check if a content key exists.
615
- */
616
- hasContent(key) {
617
- return langService.hasContent(className, key);
618
- },
619
- };
620
- }
621
-
622
556
  const LANG = 'LANG';
623
557
  const THEME = 'THEME';
624
558
 
@@ -1122,348 +1056,90 @@ class LangService {
1122
1056
  this.warnedMissingLanguages.clear();
1123
1057
  console.log('LangService: All content cleared');
1124
1058
  }
1125
- // Legacy getters/setters for backward compatibility
1126
- get Lang() {
1127
- return this.currentLang;
1128
- }
1129
- set Lang(lang) {
1130
- this.setLang(lang);
1131
- }
1132
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, deps: [{ token: ValtechConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1133
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, providedIn: 'root' }); }
1134
- }
1135
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, decorators: [{
1136
- type: Injectable,
1137
- args: [{
1138
- providedIn: 'root',
1139
- }]
1140
- }], ctorParameters: () => [{ type: undefined, decorators: [{
1141
- type: Inject,
1142
- args: [ValtechConfigService]
1143
- }] }] });
1144
-
1145
- /**
1146
- * ContentService - High-level reactive content management service.
1147
- *
1148
- * This service provides a clean, convenient API for reactive content management
1149
- * without requiring direct interaction with LangService. It acts as a facade
1150
- * that simplifies the most common content operations.
1151
- *
1152
- * @example Basic usage:
1153
- * ```typescript
1154
- * @Component({})
1155
- * export class MyComponent {
1156
- * content = inject(ContentService);
1157
- *
1158
- * title$ = this.content.fromContent({
1159
- * className: 'MyComponent',
1160
- * key: 'title'
1161
- * });
1162
- *
1163
- * constructor() {}
1164
- * }
1165
- * ```
1166
- */
1167
- class ContentService {
1168
- constructor(langService) {
1169
- this.langService = langService;
1170
- }
1171
- /**
1172
- * Get current language as an observable stream.
1173
- * Emits whenever the language changes.
1174
- */
1175
- get currentLang$() {
1176
- return this.langService.currentLang$;
1177
- }
1178
- /**
1179
- * Get current language synchronously.
1180
- */
1181
- get currentLang() {
1182
- return this.langService.currentLang;
1183
- }
1184
- /**
1185
- * Set the current language.
1186
- * This will trigger updates in all reactive content.
1187
- *
1188
- * @param lang - The language to set
1189
- */
1190
- setLang(lang) {
1191
- this.langService.setLang(lang);
1192
- }
1193
1059
  /**
1194
- * Create a reactive content observable from a content key.
1195
- * This is the primary method for reactive content with unified support
1196
- * for both simple content and content with interpolation.
1197
- *
1198
- * @param config - Content configuration with optional interpolation. If className is omitted, searches in global content.
1199
- * @returns Observable that emits the content string and updates on language change
1200
- *
1201
- * @example Simple global content:
1202
- * ```typescript
1203
- * // Searches in global content (no className needed)
1204
- * okButton$ = this.content.fromContent({
1205
- * key: 'ok',
1206
- * fallback: 'OK'
1207
- * });
1208
- * ```
1209
- *
1210
- * @example Component-specific content:
1211
- * ```typescript
1212
- * title$ = this.content.fromContent({
1213
- * className: 'HeaderComponent',
1214
- * key: 'title',
1215
- * fallback: 'Default Title'
1216
- * });
1217
- * ```
1218
- *
1219
- * @example Content with interpolation:
1220
- * ```typescript
1221
- * // Global content with interpolation
1222
- * confirmation$ = this.content.fromContent({
1223
- * key: 'deleteConfirmation',
1224
- * interpolation: { itemName: 'archivo' }
1225
- * });
1226
- *
1227
- * // Component content with interpolation
1228
- * greeting$ = this.content.fromContent({
1229
- * className: 'WelcomeComponent',
1230
- * key: 'greeting',
1231
- * interpolation: { name: 'John', count: 5 }
1232
- * });
1233
- * ```
1234
- */
1235
- fromContent(config) {
1236
- return fromContent(this.langService, config);
1237
- }
1238
- /**
1239
- * Create a reactive content observable with interpolation support.
1240
- *
1241
- * @deprecated Use fromContent() with interpolation property instead.
1242
- * This method is kept for backward compatibility.
1243
- *
1244
- * @param config - Interpolated content configuration. If className is omitted, searches in global content.
1245
- * @returns Observable that emits the interpolated content string
1246
- *
1247
- * @example Migration:
1248
- * ```typescript
1249
- * // OLD (deprecated):
1250
- * greeting$ = this.content.fromContentWithInterpolation({
1251
- * className: 'WelcomeComponent',
1252
- * key: 'greeting',
1253
- * interpolation: { name: 'John', count: 5 }
1254
- * });
1255
- *
1256
- * // NEW (recommended):
1257
- * greeting$ = this.content.fromContent({
1258
- * className: 'WelcomeComponent',
1259
- * key: 'greeting',
1260
- * interpolation: { name: 'John', count: 5 }
1261
- * });
1262
- * ```
1263
- */
1264
- fromContentWithInterpolation(config) {
1265
- // Delegate to the unified fromContent method
1266
- return this.fromContent(config);
1267
- }
1268
- /**
1269
- * Create multiple reactive content observables at once.
1270
- * Useful when a component needs several content strings.
1060
+ * Get content with interpolation support.
1061
+ * Retrieves content and replaces placeholders with provided values.
1271
1062
  *
1272
1063
  * @param className - The component class name
1273
- * @param keys - Array of content keys to retrieve
1274
- * @returns Observable that emits an object with all requested content
1275
- *
1276
- * @example
1277
- * ```typescript
1278
- * content$ = this.content.fromMultipleContent('FormComponent', [
1279
- * 'title', 'submitButton', 'cancelButton'
1280
- * ]);
1281
- *
1282
- * // In template
1283
- * <ng-container *ngIf="content$ | async as content">
1284
- * <h2>{{ content['title'] }}</h2>
1285
- * <button>{{ content['submitButton'] }}</button>
1286
- * <button>{{ content['cancelButton'] }}</button>
1287
- * </ng-container>
1288
- * ```
1289
- */
1290
- fromMultipleContent(className, keys) {
1291
- return fromMultipleContent(this.langService, className, keys);
1292
- }
1293
- getText(classNameOrKey, keyOrFallback, fallback) {
1294
- // Handle overloaded signatures
1295
- if (keyOrFallback === undefined ||
1296
- (typeof keyOrFallback === 'string' && fallback === undefined)) {
1297
- // Single parameter or two parameters (key, fallback) - treat as global content
1298
- return this.langService.getText('_global', classNameOrKey, keyOrFallback);
1299
- }
1300
- else {
1301
- // Three parameters (className, key, fallback) - treat as component content
1302
- return this.langService.getText(classNameOrKey, keyOrFallback, fallback);
1303
- }
1304
- }
1305
- /**
1306
- * Get a single global content string synchronously.
1307
- * This is a convenience method that's equivalent to getText(key, fallback).
1308
- *
1309
1064
  * @param key - The text key
1065
+ * @param interpolationData - Object with values to interpolate
1310
1066
  * @param fallback - Optional fallback text if key is not found
1311
- * @returns The text string or fallback
1312
- *
1313
- * @example
1314
- * ```typescript
1315
- * const okText = this.content.getGlobalText('ok');
1316
- * const cancelText = this.content.getGlobalText('cancel', 'Cancel');
1317
- * ```
1067
+ * @returns Text with interpolated values
1318
1068
  */
1319
- getGlobalText(key, fallback) {
1320
- return this.langService.getText('_global', key, fallback);
1069
+ getTextWithInterpolation(className, key, interpolationData, fallback) {
1070
+ const content = this.getText(className, key, fallback);
1071
+ return this.interpolateString(content, interpolationData);
1321
1072
  }
1322
1073
  /**
1323
- * Check if a content key exists for a component.
1074
+ * Get reactive content with interpolation support.
1075
+ * Returns an Observable that emits interpolated content when language changes.
1324
1076
  *
1325
1077
  * @param className - The component class name
1326
1078
  * @param key - The text key
1327
- * @returns True if the key exists in any language
1328
- *
1329
- * @example
1330
- * ```typescript
1331
- * if (this.content.hasContent('MyComponent', 'optionalText')) {
1332
- * // Show optional content
1333
- * }
1334
- * ```
1079
+ * @param interpolationData - Object with values to interpolate
1080
+ * @param fallback - Optional fallback text if key is not found
1081
+ * @returns Observable that emits interpolated text
1335
1082
  */
1336
- hasContent(className, key) {
1337
- return this.langService.hasContent(className, key);
1083
+ getContentWithInterpolation(className, key, interpolationData, fallback) {
1084
+ return this.getContent(className, key, fallback).pipe(map(content => this.interpolateString(content, interpolationData)));
1338
1085
  }
1339
1086
  /**
1340
- * Helper function to interpolate values into a content string.
1341
- * Useful for processing content strings manually.
1087
+ * Interpolate a string with provided values.
1088
+ * Replaces placeholders like {{key}} or {key} with actual values.
1342
1089
  *
1343
- * @param content - The content string with placeholders
1344
- * @param values - Object with values to interpolate
1345
- * @returns The interpolated string
1090
+ * @param content - Content string with placeholders
1091
+ * @param values - Values to interpolate
1092
+ * @returns Interpolated string
1346
1093
  *
1347
1094
  * @example
1348
1095
  * ```typescript
1349
- * const result = this.content.interpolate("Hello {name}!", { name: "World" });
1350
- * // Returns: "Hello World!"
1096
+ * interpolateString('Hello {{name}}!', { name: 'World' })
1097
+ * // Returns: 'Hello World!'
1351
1098
  * ```
1352
1099
  */
1353
- interpolate(content, values) {
1354
- return interpolateContent(content, values);
1100
+ interpolateString(content, values) {
1101
+ if (!values || !content) {
1102
+ return content;
1103
+ }
1104
+ return content.replace(/\{\{?(\w+)\}?\}/g, (match, key) => {
1105
+ const value = values[key];
1106
+ return value !== undefined ? String(value) : match;
1107
+ });
1355
1108
  }
1356
1109
  /**
1357
- * Create a content helper bound to a specific component class.
1358
- * This provides a scoped API for components that need multiple content strings.
1110
+ * Legacy function equivalent to the old fromContentWithInterpolation.
1111
+ * Provides reactive content with interpolation support for backward compatibility.
1359
1112
  *
1360
1113
  * @param className - The component class name
1361
- * @returns Content helper with methods scoped to the class
1362
- *
1363
- * @example
1364
- * ```typescript
1365
- * export class MyComponent {
1366
- * private contentHelper = this.content.createHelper('MyComponent');
1367
- *
1368
- * ngOnInit() {
1369
- * this.title$ = this.contentHelper.get('title');
1370
- * this.allContent$ = this.contentHelper.getMultiple(['title', 'description']);
1371
- * }
1114
+ * @param key - The text key
1115
+ * @param interpolationData - Object with values to interpolate
1116
+ * @param fallback - Optional fallback text if key is not found
1117
+ * @returns Observable that emits interpolated text
1372
1118
  *
1373
- * constructor(private content: ContentService) {}
1374
- * }
1375
- * ```
1119
+ * @deprecated Use getContentWithInterpolation instead
1376
1120
  */
1377
- createHelper(className) {
1378
- return createContentHelper(this.langService, className);
1121
+ fromContentWithInterpolation(className, key, interpolationData, fallback) {
1122
+ return this.getContentWithInterpolation(className, key, interpolationData, fallback);
1379
1123
  }
1380
- /**
1381
- * Create a scoped content service for a specific component class.
1382
- * This provides an even more convenient API for components with many content needs.
1383
- *
1384
- * @param className - The component class name
1385
- * @returns Scoped content service
1386
- *
1387
- * @example
1388
- * ```typescript
1389
- * export class MyComponent {
1390
- * private content = inject(ContentService).forComponent('MyComponent');
1391
- *
1392
- * // Simple content
1393
- * title$ = this.content.get('title');
1394
- *
1395
- * // Content with interpolation
1396
- * greeting$ = this.content.get('greeting', {
1397
- * interpolation: { name: 'John' }
1398
- * });
1399
- *
1400
- * // Content with fallback
1401
- * subtitle$ = this.content.get('subtitle', {
1402
- * fallback: 'Default Subtitle'
1403
- * });
1404
- *
1405
- * // Multiple texts
1406
- * allTexts$ = this.content.getMultiple(['title', 'subtitle', 'description']);
1407
- *
1408
- * constructor() {}
1409
- * }
1410
- * ```
1411
- */
1412
- forComponent(className) {
1413
- return {
1414
- /**
1415
- * Get a single content string reactively for this component.
1416
- * Supports optional interpolation.
1417
- */
1418
- get: (key, options) => {
1419
- return this.fromContent({
1420
- className,
1421
- key,
1422
- fallback: options?.fallback,
1423
- interpolation: options?.interpolation,
1424
- });
1425
- },
1426
- /**
1427
- * Get content with interpolation for this component.
1428
- * @deprecated Use get() with interpolation option instead.
1429
- */
1430
- getWithInterpolation: (key, interpolation, fallback) => {
1431
- return this.fromContent({
1432
- className,
1433
- key,
1434
- interpolation,
1435
- fallback,
1436
- });
1437
- },
1438
- /**
1439
- * Get multiple content strings for this component.
1440
- */
1441
- getMultiple: (keys) => {
1442
- return this.fromMultipleContent(className, keys);
1443
- },
1444
- /**
1445
- * Get a single content string synchronously for this component.
1446
- */
1447
- getText: (key, fallback) => {
1448
- return this.getText(className, key, fallback);
1449
- },
1450
- /**
1451
- * Check if a content key exists for this component.
1452
- */
1453
- hasContent: (key) => {
1454
- return this.hasContent(className, key);
1455
- },
1456
- };
1124
+ // Legacy getters/setters for backward compatibility
1125
+ get Lang() {
1126
+ return this.currentLang;
1127
+ }
1128
+ set Lang(lang) {
1129
+ this.setLang(lang);
1457
1130
  }
1458
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ContentService, deps: [{ token: LangService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1459
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ContentService, providedIn: 'root' }); }
1131
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, deps: [{ token: ValtechConfigService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1132
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, providedIn: 'root' }); }
1460
1133
  }
1461
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ContentService, decorators: [{
1134
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LangService, decorators: [{
1462
1135
  type: Injectable,
1463
1136
  args: [{
1464
1137
  providedIn: 'root',
1465
1138
  }]
1466
- }], ctorParameters: () => [{ type: LangService }] });
1139
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1140
+ type: Inject,
1141
+ args: [ValtechConfigService]
1142
+ }] }] });
1467
1143
 
1468
1144
  /**
1469
1145
  * val-button
@@ -1507,10 +1183,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1507
1183
  * @output onClick - Emits when the button is clicked
1508
1184
  */
1509
1185
  class ButtonComponent {
1510
- constructor(download, icon, navigation, contentService) {
1186
+ constructor(download, icon, navigation, langService) {
1511
1187
  this.download = download;
1512
1188
  this.navigation = navigation;
1513
- this.contentService = contentService;
1189
+ this.langService = langService;
1514
1190
  this.states = ComponentStates;
1515
1191
  this.subscriptions = new Subscription();
1516
1192
  /**
@@ -1532,26 +1208,25 @@ class ButtonComponent {
1532
1208
  setupDisplayText() {
1533
1209
  if (this.props.text) {
1534
1210
  // Static text takes precedence
1535
- this.displayText$ = of(this.props.text);
1211
+ if (this.props.contentInterpolation) {
1212
+ // Static text with interpolation
1213
+ const interpolatedText = interpolateStaticContent(this.props.text, this.props.contentInterpolation);
1214
+ this.displayText$ = of(interpolatedText);
1215
+ }
1216
+ else {
1217
+ // Simple static text
1218
+ this.displayText$ = of(this.props.text);
1219
+ }
1536
1220
  }
1537
1221
  else if (this.props.contentKey && this.props.contentClass) {
1538
1222
  // Reactive content from language service
1539
1223
  if (this.props.contentInterpolation) {
1540
1224
  // With interpolation
1541
- this.displayText$ = this.contentService.fromContentWithInterpolation({
1542
- className: this.props.contentClass,
1543
- key: this.props.contentKey,
1544
- fallback: this.props.contentFallback,
1545
- interpolation: this.props.contentInterpolation,
1546
- });
1225
+ this.displayText$ = this.langService.getContentWithInterpolation(this.props.contentClass, this.props.contentKey, this.props.contentInterpolation, this.props.contentFallback);
1547
1226
  }
1548
1227
  else {
1549
1228
  // Simple reactive content
1550
- this.displayText$ = this.contentService.fromContent({
1551
- className: this.props.contentClass,
1552
- key: this.props.contentKey,
1553
- fallback: this.props.contentFallback,
1554
- });
1229
+ this.displayText$ = this.langService.getContent(this.props.contentClass, this.props.contentKey, this.props.contentFallback);
1555
1230
  }
1556
1231
  }
1557
1232
  else {
@@ -1575,7 +1250,7 @@ class ButtonComponent {
1575
1250
  }
1576
1251
  this.onClick.emit(this.props.token);
1577
1252
  }
1578
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ButtonComponent, deps: [{ token: DownloadService }, { token: IconService }, { token: NavigationService }, { token: ContentService }], target: i0.ɵɵFactoryTarget.Component }); }
1253
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ButtonComponent, deps: [{ token: DownloadService }, { token: IconService }, { token: NavigationService }, { token: LangService }], target: i0.ɵɵFactoryTarget.Component }); }
1579
1254
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ButtonComponent, isStandalone: true, selector: "val-button", inputs: { props: "props" }, outputs: { onClick: "onClick" }, ngImport: i0, template: `
1580
1255
  <ion-button
1581
1256
  [type]="props.type"
@@ -1617,7 +1292,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1617
1292
  <ion-text *ngIf="props.state !== states.WORKING">{{ displayText$ | async }}</ion-text>
1618
1293
  </ion-button>
1619
1294
  `, 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;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}@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)}ion-button{font-family:var(--ion-default-font),Arial,sans-serif;min-width:6.25rem;margin:0}ion-button.small{--padding-bottom: 12px;--padding-end: 14px;--padding-start: 14px;--padding-top: 12px}ion-button.button-clear{padding:0;margin:0}\n"] }]
1620
- }], ctorParameters: () => [{ type: DownloadService }, { type: IconService }, { type: NavigationService }, { type: ContentService }], propDecorators: { props: [{
1295
+ }], ctorParameters: () => [{ type: DownloadService }, { type: IconService }, { type: NavigationService }, { type: LangService }], propDecorators: { props: [{
1621
1296
  type: Input
1622
1297
  }], onClick: [{
1623
1298
  type: Output
@@ -1894,9 +1569,9 @@ const SecondarySolidBlockIconHrefButton = (text, icon, href, target) => {
1894
1569
  * @input props: DisplayMetadata - Configuration for the display (content/contentConfig, color, size)
1895
1570
  */
1896
1571
  class DisplayComponent {
1897
- constructor(contentService) {
1898
- this.contentService = contentService;
1572
+ constructor() {
1899
1573
  this.subscriptions = new Subscription();
1574
+ this.langService = inject(LangService);
1900
1575
  }
1901
1576
  ngOnInit() {
1902
1577
  this.initializeDisplayContent();
@@ -1912,18 +1587,15 @@ class DisplayComponent {
1912
1587
  }
1913
1588
  // Use reactive content if configured
1914
1589
  if (this.props.contentConfig) {
1915
- this.displayContent$ = this.contentService.fromContent({
1916
- className: this.props.contentConfig.className || '_global',
1917
- key: this.props.contentConfig.key,
1918
- fallback: this.props.contentConfig.fallback || this.props.contentConfig.key,
1919
- interpolation: this.props.contentConfig.interpolation,
1920
- });
1590
+ this.displayContent$ = this.langService.getContent(this.props.contentConfig.className || '_global', this.props.contentConfig.key, this.props.contentConfig.fallback || this.props.contentConfig.key
1591
+ // interpolation: this.props.contentConfig.interpolation,
1592
+ );
1921
1593
  return;
1922
1594
  }
1923
1595
  // No content configured - use empty string
1924
1596
  this.displayContent$ = of('');
1925
1597
  }
1926
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DisplayComponent, deps: [{ token: ContentService }], target: i0.ɵɵFactoryTarget.Component }); }
1598
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DisplayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1927
1599
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DisplayComponent, isStandalone: true, selector: "val-display", inputs: { props: "props" }, ngImport: i0, template: `
1928
1600
  <ion-text [color]="props.color">
1929
1601
  <p [class]="props.size">
@@ -1941,7 +1613,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1941
1613
  </p>
1942
1614
  </ion-text>
1943
1615
  `, 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;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}@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:1.5rem;line-height:2rem;font-weight:800}@media (min-width: 768px){.small{font-size:2rem;line-height:2.5rem}}.medium{font-size:2rem;line-height:2.5rem;font-weight:800}@media (min-width: 768px){.medium{font-size:2.5rem;line-height:3rem}}.large{font-size:2.5rem;line-height:3rem;font-weight:800}@media (min-width: 768px){.large{font-size:3rem;line-height:3.5rem}}.xlarge{font-size:3rem;line-height:2.5rem;font-weight:800}@media (min-width: 768px){.xlarge{font-size:4.375rem;line-height:4.125rem}}\n"] }]
1944
- }], ctorParameters: () => [{ type: ContentService }], propDecorators: { props: [{
1616
+ }], ctorParameters: () => [], propDecorators: { props: [{
1945
1617
  type: Input
1946
1618
  }] } });
1947
1619
  /**
@@ -2723,8 +2395,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2723
2395
  * @input props: TextMetadata - Configuration for the text (content, styling, and reactive content options)
2724
2396
  */
2725
2397
  class TextComponent {
2726
- constructor(contentService, linkProcessor) {
2727
- this.contentService = contentService;
2398
+ constructor(langService, linkProcessor) {
2399
+ this.langService = langService;
2728
2400
  this.linkProcessor = linkProcessor;
2729
2401
  this.subscription = new Subscription();
2730
2402
  }
@@ -2741,26 +2413,25 @@ class TextComponent {
2741
2413
  setupDisplayContent() {
2742
2414
  if (this.props.content) {
2743
2415
  // Static content takes precedence
2744
- this.displayContent$ = of(this.props.content);
2416
+ if (this.props.contentInterpolation) {
2417
+ // Static content with interpolation
2418
+ const interpolatedContent = interpolateStaticContent(this.props.content, this.props.contentInterpolation);
2419
+ this.displayContent$ = of(interpolatedContent);
2420
+ }
2421
+ else {
2422
+ // Simple static content
2423
+ this.displayContent$ = of(this.props.content);
2424
+ }
2745
2425
  }
2746
- else if (this.props.contentKey && this.props.contentClass) {
2426
+ else if (shouldUseReactiveContent(this.props)) {
2747
2427
  // Reactive content from language service
2748
2428
  if (this.props.contentInterpolation) {
2749
2429
  // With interpolation
2750
- this.displayContent$ = this.contentService.fromContentWithInterpolation({
2751
- className: this.props.contentClass,
2752
- key: this.props.contentKey,
2753
- fallback: this.props.contentFallback,
2754
- interpolation: this.props.contentInterpolation,
2755
- });
2430
+ this.displayContent$ = this.langService.getContentWithInterpolation(this.props.contentClass, this.props.contentKey, this.props.contentInterpolation, this.props.contentFallback);
2756
2431
  }
2757
2432
  else {
2758
2433
  // Simple reactive content
2759
- this.displayContent$ = this.contentService.fromContent({
2760
- className: this.props.contentClass,
2761
- key: this.props.contentKey,
2762
- fallback: this.props.contentFallback,
2763
- });
2434
+ this.displayContent$ = this.langService.getContent(this.props.contentClass, this.props.contentKey, this.props.contentFallback);
2764
2435
  }
2765
2436
  }
2766
2437
  else {
@@ -2785,7 +2456,7 @@ class TextComponent {
2785
2456
  processedContent = processedContent.replace(/<strong>(.*?)<\/strong>/gi, '<span class="partial-bold">$1</span>');
2786
2457
  return processedContent;
2787
2458
  }
2788
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextComponent, deps: [{ token: ContentService }, { token: LinkProcessorService }], target: i0.ɵɵFactoryTarget.Component }); }
2459
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TextComponent, deps: [{ token: LangService }, { token: LinkProcessorService }], target: i0.ɵɵFactoryTarget.Component }); }
2789
2460
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: TextComponent, isStandalone: true, selector: "val-text", inputs: { props: "props" }, ngImport: i0, template: `
2790
2461
  <ion-text [color]="props.color">
2791
2462
  @if (props.processLinks) {
@@ -2825,7 +2496,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2825
2496
  }
2826
2497
  </ion-text>
2827
2498
  `, 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;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}@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 .partial-bold{font-weight:700}\n"] }]
2828
- }], ctorParameters: () => [{ type: ContentService }, { type: LinkProcessorService }], propDecorators: { props: [{
2499
+ }], ctorParameters: () => [{ type: LangService }, { type: LinkProcessorService }], propDecorators: { props: [{
2829
2500
  type: Input
2830
2501
  }] } });
2831
2502
  /**
@@ -2881,123 +2552,6 @@ function createTextProps(contentConfig, styleConfig = {}) {
2881
2552
  };
2882
2553
  }
2883
2554
 
2884
- /**
2885
- * Enhanced content utilities for multi-language component support.
2886
- * Extends the base content utilities to provide specialized helpers for different component patterns.
2887
- */
2888
- /**
2889
- * Helper class for managing reactive content in components.
2890
- */
2891
- class ComponentContentHelper {
2892
- constructor(contentService, defaultClassName) {
2893
- this.contentService = contentService;
2894
- this.defaultClassName = defaultClassName;
2895
- }
2896
- /**
2897
- * Resolves content based on hybrid configuration (static vs reactive).
2898
- */
2899
- resolveContent(config) {
2900
- // Static content takes precedence
2901
- if (config.content !== undefined) {
2902
- return config.content;
2903
- }
2904
- // Use reactive content if configured
2905
- if (config.contentConfig) {
2906
- return this.contentService.fromContent({
2907
- className: config.contentConfig.className || this.defaultClassName || '_global',
2908
- key: config.contentConfig.key,
2909
- fallback: config.contentConfig.fallback,
2910
- interpolation: config.contentConfig.interpolation,
2911
- });
2912
- }
2913
- // No content configured
2914
- return '';
2915
- }
2916
- /**
2917
- * Resolves multiple text properties for components with multiple text fields.
2918
- */
2919
- resolveMultipleTexts(config) {
2920
- const result = {};
2921
- Object.entries(config.textConfigs).forEach(([property, contentConfig]) => {
2922
- result[property] = this.contentService.fromContent({
2923
- className: contentConfig.className || this.defaultClassName || '_global',
2924
- key: contentConfig.key,
2925
- fallback: contentConfig.fallback,
2926
- interpolation: contentConfig.interpolation,
2927
- });
2928
- });
2929
- return result;
2930
- }
2931
- /**
2932
- * Creates a reactive content configuration for array items.
2933
- * Useful for list components where items might have translatable text.
2934
- */
2935
- createArrayItemConfig(baseKey, index, fallback) {
2936
- return {
2937
- className: this.defaultClassName || '_global',
2938
- key: `${baseKey}.${index}`,
2939
- fallback: fallback,
2940
- };
2941
- }
2942
- /**
2943
- * Helper for button/action text that commonly needs translation.
2944
- */
2945
- createActionConfig(actionKey, fallback) {
2946
- return {
2947
- className: '_global',
2948
- key: actionKey,
2949
- fallback: fallback,
2950
- };
2951
- }
2952
- }
2953
- /**
2954
- * Factory function to create content helpers for components.
2955
- */
2956
- function createComponentContentHelper(contentService, componentClassName) {
2957
- return new ComponentContentHelper(contentService, componentClassName);
2958
- }
2959
- /**
2960
- * Utility function to determine if content should be reactive.
2961
- */
2962
- function shouldUseReactiveContent(config) {
2963
- return config.content === undefined && config.contentConfig !== undefined;
2964
- }
2965
- /**
2966
- * Enhanced props factory for components that support reactive content.
2967
- */
2968
- function createReactiveProps(contentHelper, staticProps, contentConfig) {
2969
- return {
2970
- ...staticProps,
2971
- ...contentConfig,
2972
- };
2973
- }
2974
- /**
2975
- * Helper for list components that need to mix static and reactive content.
2976
- */
2977
- class ListContentHelper {
2978
- constructor(contentHelper) {
2979
- this.contentHelper = contentHelper;
2980
- }
2981
- /**
2982
- * Resolves a list configuration into final items.
2983
- */
2984
- resolveListItems(config) {
2985
- const items = [];
2986
- // Add static items first
2987
- if (config.staticItems) {
2988
- items.push(...config.staticItems);
2989
- }
2990
- // Add reactive items
2991
- if (config.reactiveItems) {
2992
- for (let i = 0; i < config.reactiveItems.count; i++) {
2993
- const item = config.reactiveItems.itemTemplate(this.contentHelper, i);
2994
- items.push(item);
2995
- }
2996
- }
2997
- return items;
2998
- }
2999
- }
3000
-
3001
2555
  /**
3002
2556
  * val-title
3003
2557
  *
@@ -3037,14 +2591,14 @@ class ListContentHelper {
3037
2591
  */
3038
2592
  class TitleComponent {
3039
2593
  constructor() {
3040
- this.contentService = inject(ContentService);
2594
+ this.langService = inject(LangService);
3041
2595
  }
3042
2596
  ngOnInit() {
3043
- this.contentHelper = createComponentContentHelper(this.contentService);
3044
2597
  // Always convert to Observable for consistent template handling
3045
2598
  if (shouldUseReactiveContent(this.props)) {
3046
- // Use reactive content
3047
- this.displayContent$ = this.contentHelper.resolveContent(this.props);
2599
+ // Use reactive content with LangService
2600
+ const config = extractContentConfig(this.props);
2601
+ this.displayContent$ = this.langService.getContent(config.className, config.key, config.fallback);
3048
2602
  }
3049
2603
  else {
3050
2604
  // Convert static content to Observable
@@ -3109,7 +2663,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3109
2663
  * ```typescript
3110
2664
  * const props = createTitleProps(
3111
2665
  * { size: 'large', color: 'primary', bold: true },
3112
- * { contentConfig: { key: 'pageTitle', fallback: 'Default Title' } }
2666
+ * {
2667
+ * contentKey: 'pageTitle',
2668
+ * contentClass: 'MyComponent',
2669
+ * contentFallback: 'Default Title'
2670
+ * }
3113
2671
  * );
3114
2672
  * ```
3115
2673
  */
@@ -3144,8 +2702,8 @@ class AlertBoxComponent {
3144
2702
  getLegacyTextProps() {
3145
2703
  return this.props.text;
3146
2704
  }
3147
- constructor(contentService) {
3148
- this.contentService = contentService;
2705
+ constructor(langService) {
2706
+ this.langService = langService;
3149
2707
  this.subscriptions = new Subscription();
3150
2708
  }
3151
2709
  ngOnInit() {
@@ -3174,7 +2732,7 @@ class AlertBoxComponent {
3174
2732
  this.computedTextProps.contentConfig = reactiveProps.textConfig;
3175
2733
  }
3176
2734
  }
3177
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AlertBoxComponent, deps: [{ token: ContentService }], target: i0.ɵɵFactoryTarget.Component }); }
2735
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AlertBoxComponent, deps: [{ token: LangService }], target: i0.ɵɵFactoryTarget.Component }); }
3178
2736
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: AlertBoxComponent, isStandalone: true, selector: "val-alert-box", inputs: { props: "props" }, ngImport: i0, template: `
3179
2737
  <val-box [props]="props.box">
3180
2738
  <div class="content-container" body>
@@ -3204,7 +2762,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3204
2762
  </div>
3205
2763
  </val-box>
3206
2764
  `, 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;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}@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)}.text{margin-left:.25rem}.content-container{display:flex;align-items:flex-start}\n"] }]
3207
- }], ctorParameters: () => [{ type: ContentService }], propDecorators: { props: [{
2765
+ }], ctorParameters: () => [{ type: LangService }], propDecorators: { props: [{
3208
2766
  type: Input
3209
2767
  }] } });
3210
2768
 
@@ -4402,9 +3960,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
4402
3960
  * @output languageChange: EventEmitter<string> - Emitted when language changes
4403
3961
  */
4404
3962
  class LanguageSelectorComponent {
4405
- constructor(langService, contentService) {
3963
+ constructor(langService) {
4406
3964
  this.langService = langService;
4407
- this.contentService = contentService;
4408
3965
  /**
4409
3966
  * Language selector configuration object.
4410
3967
  * @type {LanguageSelectorMetadata}
@@ -4468,20 +4025,11 @@ class LanguageSelectorComponent {
4468
4025
  }
4469
4026
  else if (this.props.labelConfig) {
4470
4027
  // Reactive label
4471
- this.label$ = this.contentService.fromContent({
4472
- className: this.props.labelConfig.className || '_global',
4473
- key: this.props.labelConfig.key,
4474
- fallback: this.props.labelConfig.fallback || 'Language',
4475
- interpolation: this.props.labelConfig.interpolation,
4476
- });
4028
+ this.label$ = this.langService.getContent(this.props.labelConfig.className || '_global', this.props.labelConfig.key, this.props.labelConfig.fallback || 'Language');
4477
4029
  }
4478
4030
  else {
4479
4031
  // Default label from global content
4480
- this.label$ = this.contentService.fromContent({
4481
- className: '_global',
4482
- key: 'language',
4483
- fallback: 'Idioma',
4484
- });
4032
+ this.label$ = this.langService.getContent('_global', 'language', 'Idioma');
4485
4033
  }
4486
4034
  }
4487
4035
  initializePopoverProps() {
@@ -4562,7 +4110,7 @@ class LanguageSelectorComponent {
4562
4110
  this.languageChange.emit(selectedLanguage);
4563
4111
  }
4564
4112
  }
4565
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LanguageSelectorComponent, deps: [{ token: LangService }, { token: ContentService }], target: i0.ɵɵFactoryTarget.Component }); }
4113
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LanguageSelectorComponent, deps: [{ token: LangService }], target: i0.ɵɵFactoryTarget.Component }); }
4566
4114
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LanguageSelectorComponent, isStandalone: true, selector: "val-language-selector", inputs: { props: "props" }, outputs: { languageChange: "languageChange" }, ngImport: i0, template: `
4567
4115
  <val-popover-selector [props]="popoverProps" (selectionChange)="onLanguageChange($event)"> </val-popover-selector>
4568
4116
  `, isInline: true, styles: [":host{display:inline-block;width:auto}val-popover-selector .popover-selector-container{display:inline-block;width:auto}val-popover-selector .selector-trigger .trigger-text{display:inline-flex;align-items:center;gap:8px;font-weight:700}val-popover-selector .selector-trigger .trigger-text .flag-emoji{font-size:1.2em;line-height:1;filter:drop-shadow(0 1px 2px rgba(0,0,0,.1));transition:transform .2s ease}val-popover-selector .selector-trigger.has-flag .trigger-text{letter-spacing:.025em}val-popover-selector .selector-trigger:hover:not([disabled]) .trigger-text .flag-emoji{transform:scale(1.1)}val-popover-selector .option-content{gap:10px;padding:8px 0}val-popover-selector .option-content .flag-emoji{font-size:1.1em;filter:drop-shadow(0 1px 2px rgba(0,0,0,.08))}val-popover-selector .option-content span{font-weight:600;letter-spacing:.01em}.language-flag{font-size:1.2em;margin-right:6px;vertical-align:middle;line-height:1;filter:drop-shadow(0 1px 3px rgba(0,0,0,.1));transition:all .2s ease}@media (max-width: 768px){val-popover-selector .selector-trigger .trigger-text{font-size:13px;gap:6px}val-popover-selector .selector-trigger .trigger-text .flag-emoji{font-size:1.1em}}val-popover-selector .selector-trigger:hover:not([disabled]) .trigger-text .flag-emoji{transform:scale(1.1) rotate(3deg);transition:transform .25s cubic-bezier(.4,0,.2,1)}val-popover-selector .selector-trigger:active .trigger-text .flag-emoji{transform:scale(1.05)}val-popover-selector .language-changing .flag-emoji{animation:languageSwitch .4s ease-in-out}@keyframes languageSwitch{0%{transform:scale(1)}50%{transform:scale(1.15) rotate(8deg)}to{transform:scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PopoverSelectorComponent, selector: "val-popover-selector", inputs: ["props"], outputs: ["selectionChange"] }] }); }
@@ -4572,7 +4120,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
4572
4120
  args: [{ selector: 'val-language-selector', standalone: true, imports: [CommonModule, PopoverSelectorComponent], template: `
4573
4121
  <val-popover-selector [props]="popoverProps" (selectionChange)="onLanguageChange($event)"> </val-popover-selector>
4574
4122
  `, styles: [":host{display:inline-block;width:auto}val-popover-selector .popover-selector-container{display:inline-block;width:auto}val-popover-selector .selector-trigger .trigger-text{display:inline-flex;align-items:center;gap:8px;font-weight:700}val-popover-selector .selector-trigger .trigger-text .flag-emoji{font-size:1.2em;line-height:1;filter:drop-shadow(0 1px 2px rgba(0,0,0,.1));transition:transform .2s ease}val-popover-selector .selector-trigger.has-flag .trigger-text{letter-spacing:.025em}val-popover-selector .selector-trigger:hover:not([disabled]) .trigger-text .flag-emoji{transform:scale(1.1)}val-popover-selector .option-content{gap:10px;padding:8px 0}val-popover-selector .option-content .flag-emoji{font-size:1.1em;filter:drop-shadow(0 1px 2px rgba(0,0,0,.08))}val-popover-selector .option-content span{font-weight:600;letter-spacing:.01em}.language-flag{font-size:1.2em;margin-right:6px;vertical-align:middle;line-height:1;filter:drop-shadow(0 1px 3px rgba(0,0,0,.1));transition:all .2s ease}@media (max-width: 768px){val-popover-selector .selector-trigger .trigger-text{font-size:13px;gap:6px}val-popover-selector .selector-trigger .trigger-text .flag-emoji{font-size:1.1em}}val-popover-selector .selector-trigger:hover:not([disabled]) .trigger-text .flag-emoji{transform:scale(1.1) rotate(3deg);transition:transform .25s cubic-bezier(.4,0,.2,1)}val-popover-selector .selector-trigger:active .trigger-text .flag-emoji{transform:scale(1.05)}val-popover-selector .language-changing .flag-emoji{animation:languageSwitch .4s ease-in-out}@keyframes languageSwitch{0%{transform:scale(1)}50%{transform:scale(1.15) rotate(8deg)}to{transform:scale(1)}}\n"] }]
4575
- }], ctorParameters: () => [{ type: LangService }, { type: ContentService }], propDecorators: { props: [{
4123
+ }], ctorParameters: () => [{ type: LangService }], propDecorators: { props: [{
4576
4124
  type: Input
4577
4125
  }], languageChange: [{
4578
4126
  type: Output
@@ -8406,6 +7954,191 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8406
7954
  }]
8407
7955
  }], ctorParameters: () => [{ type: i2.ToastController }] });
8408
7956
 
7957
+ /**
7958
+ * Create a reactive content observable from a content key.
7959
+ * This is the primary utility for the `fromContent` pattern with unified support
7960
+ * for both simple content and content with interpolation.
7961
+ *
7962
+ * @param langService - The language service instance
7963
+ * @param config - Content configuration with optional interpolation
7964
+ * @returns Observable that emits the content string and updates on language change
7965
+ *
7966
+ * @example Simple content:
7967
+ * ```typescript
7968
+ * // Component-specific content
7969
+ * this.title$ = fromContent(this.langService, {
7970
+ * className: 'HeaderComponent',
7971
+ * key: 'title',
7972
+ * fallback: 'Default Title'
7973
+ * });
7974
+ *
7975
+ * // Global content (no className needed)
7976
+ * this.saveButton$ = fromContent(this.langService, {
7977
+ * key: 'save'
7978
+ * });
7979
+ * ```
7980
+ *
7981
+ * @example Content with interpolation:
7982
+ * ```typescript
7983
+ * // Content: "Hello {name}, you have {count} messages"
7984
+ * this.greeting$ = fromContent(this.langService, {
7985
+ * className: 'WelcomeComponent',
7986
+ * key: 'greeting',
7987
+ * interpolation: { name: 'John', count: 5 }
7988
+ * });
7989
+ * // Results in: "Hello John, you have 5 messages"
7990
+ * ```
7991
+ */
7992
+ function fromContent(langService, config) {
7993
+ // Use _global as default className if not provided
7994
+ const finalClassName = config.className || '_global';
7995
+ const contentObservable = langService.getContent(finalClassName, config.key, config.fallback);
7996
+ // If interpolation is provided, apply it
7997
+ if (config.interpolation) {
7998
+ return contentObservable.pipe(map(content => interpolateContent(content, config.interpolation)));
7999
+ }
8000
+ return contentObservable;
8001
+ }
8002
+ /**
8003
+ * Create a reactive content observable with interpolation support.
8004
+ *
8005
+ * @deprecated Use fromContent() with interpolation property instead.
8006
+ * This method is kept for backward compatibility.
8007
+ *
8008
+ * @param langService - The language service instance
8009
+ * @param config - Interpolated content configuration
8010
+ * @returns Observable that emits the interpolated content string
8011
+ *
8012
+ * @example Migration:
8013
+ * ```typescript
8014
+ * // OLD (deprecated):
8015
+ * this.greeting$ = fromContentWithInterpolation(this.langService, {
8016
+ * className: 'WelcomeComponent',
8017
+ * key: 'greeting',
8018
+ * interpolation: { name: 'John', count: 5 }
8019
+ * });
8020
+ *
8021
+ * // NEW (recommended):
8022
+ * this.greeting$ = fromContent(this.langService, {
8023
+ * className: 'WelcomeComponent',
8024
+ * key: 'greeting',
8025
+ * interpolation: { name: 'John', count: 5 }
8026
+ * });
8027
+ * ```
8028
+ */
8029
+ function fromContentWithInterpolation(langService, config) {
8030
+ // Delegate to the unified fromContent method
8031
+ return fromContent(langService, config);
8032
+ }
8033
+ /**
8034
+ * Create multiple reactive content observables at once.
8035
+ * Useful when a component needs several content strings.
8036
+ *
8037
+ * @param langService - The language service instance
8038
+ * @param className - The component class name
8039
+ * @param keys - Array of content keys to retrieve
8040
+ * @returns Observable that emits an object with all requested content
8041
+ *
8042
+ * @example
8043
+ * ```typescript
8044
+ * this.content$ = fromMultipleContent(this.langService, 'FormComponent', [
8045
+ * 'title', 'submitButton', 'cancelButton'
8046
+ * ]);
8047
+ *
8048
+ * // In template
8049
+ * <ng-container *ngIf="content$ | async as content">
8050
+ * <h2>{{ content.title }}</h2>
8051
+ * <button>{{ content.submitButton }}</button>
8052
+ * <button>{{ content.cancelButton }}</button>
8053
+ * </ng-container>
8054
+ * ```
8055
+ */
8056
+ function fromMultipleContent(langService, className, keys) {
8057
+ return langService.getMultipleContent(className, keys);
8058
+ }
8059
+ /**
8060
+ * Helper function to interpolate values into a content string.
8061
+ * Replaces placeholders in the format {key} with corresponding values.
8062
+ *
8063
+ * @param content - The content string with placeholders
8064
+ * @param values - Object with values to interpolate
8065
+ * @returns The interpolated string
8066
+ *
8067
+ * @example
8068
+ * ```typescript
8069
+ * const result = interpolateContent("Hello {name}!", { name: "World" });
8070
+ * // Returns: "Hello World!"
8071
+ * ```
8072
+ */
8073
+ function interpolateContent(content, values) {
8074
+ if (!values)
8075
+ return content;
8076
+ return Object.entries(values).reduce((result, [key, value]) => {
8077
+ return result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
8078
+ }, content);
8079
+ }
8080
+ /**
8081
+ * Factory function to create a content helper bound to a specific component class.
8082
+ * This creates a more convenient API for components that need multiple content strings.
8083
+ *
8084
+ * @param langService - The language service instance
8085
+ * @param className - The component class name
8086
+ * @returns Object with convenient methods for content retrieval
8087
+ *
8088
+ * @example
8089
+ * ```typescript
8090
+ * export class MyComponent {
8091
+ * private content = createContentHelper(this.langService, 'MyComponent');
8092
+ *
8093
+ * constructor(private langService: LangService) {}
8094
+ *
8095
+ * ngOnInit() {
8096
+ * this.title$ = this.content.get('title');
8097
+ * this.allContent$ = this.content.getMultiple(['title', 'description']);
8098
+ * }
8099
+ * }
8100
+ * ```
8101
+ */
8102
+ function createContentHelper(langService, className) {
8103
+ return {
8104
+ /**
8105
+ * Get a single content string reactively.
8106
+ */
8107
+ get(key, fallback) {
8108
+ return fromContent(langService, { className, key, fallback });
8109
+ },
8110
+ /**
8111
+ * Get multiple content strings reactively.
8112
+ */
8113
+ getMultiple(keys) {
8114
+ return fromMultipleContent(langService, className, keys);
8115
+ },
8116
+ /**
8117
+ * Get content with interpolation.
8118
+ */
8119
+ getWithInterpolation(key, interpolation, fallback) {
8120
+ return fromContentWithInterpolation(langService, {
8121
+ className,
8122
+ key,
8123
+ interpolation,
8124
+ fallback,
8125
+ });
8126
+ },
8127
+ /**
8128
+ * Get a single content string synchronously (current language only).
8129
+ */
8130
+ getText(key, fallback) {
8131
+ return langService.getText(className, key, fallback);
8132
+ },
8133
+ /**
8134
+ * Check if a content key exists.
8135
+ */
8136
+ hasContent(key) {
8137
+ return langService.hasContent(className, key);
8138
+ },
8139
+ };
8140
+ }
8141
+
8409
8142
  /*
8410
8143
  * Public API Surface of valtech-components
8411
8144
  */
@@ -8414,5 +8147,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
8414
8147
  * Generated bundle index. Do not edit.
8415
8148
  */
8416
8149
 
8417
- export { ARTICLE_SPACING, ActionType, AlertBoxComponent, ArticleBuilder, ArticleComponent, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, ButtonComponent, ButtonGroupComponent, CardComponent, CardSection, CardType, CheckInputComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CommentInputComponent, ComponentContentHelper, ComponentStates, ContentLoaderComponent, ContentService, DateInputComponent, DisplayComponent, DividerComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FileInputComponent, FooterComponent, FormComponent, FormFooterComponent, GlobalContent, HeaderComponent, HintComponent, HourInputComponent, HrefComponent, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InputType, ItemListComponent, LANGUAGES, LangService, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksCakeComponent, ListContentHelper, LocalStorageService, MOTION, NavigationService, NoContentComponent, NotesBoxComponent, NumberInputComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PasswordInputComponent, PinInputComponent, PopoverSelectorComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProgressBarComponent, ProgressStatusComponent, PrompterComponent, RadioInputComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SelectSearchComponent, SimpleComponent, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, TextComponent, TextContent, TextInputComponent, ThemeOption, ThemeService, TitleBlockComponent, TitleComponent, ToastService, ToolbarActionType, ToolbarComponent, ValtechConfigService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, content, createButtonProps, createComponentContentHelper, createContentHelper, createDisplayProps, createReactiveProps, createTextProps, createTitleProps, fromContent, fromContentWithInterpolation, fromMultipleContent, globalContentData, goToTop, interpolateContent, isAtEnd, maxLength, replaceSpecialChars, resolveColor, resolveInputDefaultValue, shouldUseReactiveContent };
8150
+ export { ARTICLE_SPACING, ActionType, AlertBoxComponent, ArticleBuilder, ArticleComponent, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, ButtonComponent, ButtonGroupComponent, CardComponent, CardSection, CardType, CheckInputComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CommentInputComponent, ComponentStates, ContentLoaderComponent, DateInputComponent, DisplayComponent, DividerComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FileInputComponent, FooterComponent, FormComponent, FormFooterComponent, GlobalContent, HeaderComponent, HintComponent, HourInputComponent, HrefComponent, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InputType, ItemListComponent, LANGUAGES, LangService, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksCakeComponent, LocalStorageService, MOTION, NavigationService, NoContentComponent, NotesBoxComponent, NumberInputComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PasswordInputComponent, PinInputComponent, PopoverSelectorComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProgressBarComponent, ProgressStatusComponent, PrompterComponent, RadioInputComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SelectSearchComponent, SimpleComponent, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, TextComponent, TextContent, TextInputComponent, ThemeOption, ThemeService, TitleBlockComponent, TitleComponent, ToastService, ToolbarActionType, ToolbarComponent, ValtechConfigService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, content, createButtonProps, createContentHelper, createDisplayProps, createReactiveContentMetadata, createTextProps, createTitleProps, extractContentConfig, extractContentConfigWithInterpolation, fromContent, fromContentWithInterpolation, fromMultipleContent, globalContentData, goToTop, interpolateContent, interpolateStaticContent, isAtEnd, maxLength, replaceSpecialChars, resolveColor, resolveInputDefaultValue, shouldUseReactiveContent, shouldUseReactiveContentWithInterpolation };
8418
8151
  //# sourceMappingURL=valtech-components.mjs.map