mn-angular-lib 0.0.16 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, effect, Injectable, InjectionToken, inject, computed, Optional, Inject, Input, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { BehaviorSubject } from 'rxjs';
2
+ import { signal, effect, Injectable, InjectionToken, inject, computed, Optional, Inject, Input, ChangeDetectionStrategy, Component, HostBinding, Self, APP_INITIALIZER, SkipSelf, Attribute, Directive } from '@angular/core';
3
+ import { BehaviorSubject, firstValueFrom } from 'rxjs';
4
4
  import * as i1 from '@angular/common';
5
- import { CommonModule } from '@angular/common';
5
+ import { CommonModule, NgClass, NgOptimizedImage } from '@angular/common';
6
+ import { tv } from 'tailwind-variants';
7
+ import * as i1$1 from '@angular/forms';
8
+ import { Validators } from '@angular/forms';
9
+ import * as i1$2 from '@angular/common/http';
6
10
 
7
11
  /**
8
12
  * MnThemeService is responsible for managing the theme configuration of the application.
@@ -179,7 +183,7 @@ const DEFAULT_MN_ALERT_CONFIG = {
179
183
  default: 'alert'
180
184
  },
181
185
  icons: {},
182
- defaultDuration: 4000,
186
+ fallbackDuration: 4000,
183
187
  finalize: (a) => a
184
188
  };
185
189
 
@@ -201,7 +205,7 @@ class MnAlertStore {
201
205
  alerts$ = this._alerts$.asObservable();
202
206
  show(partial) {
203
207
  // Ensure every alert has a numeric duration: use provided or fall back to per-kind default
204
- const computedDuration = partial.duration ?? DEFAULT_MN_ALERT_CONFIG.durations[partial.kind] ?? DEFAULT_MN_ALERT_CONFIG.defaultDuration;
208
+ const computedDuration = partial.duration ?? DEFAULT_MN_ALERT_CONFIG.durations[partial.kind] ?? DEFAULT_MN_ALERT_CONFIG.fallbackDuration;
205
209
  const a = { id: uid(), ...partial, duration: computedDuration };
206
210
  this._alerts$.next([...this._alerts$.value, a]);
207
211
  if (typeof a.duration === 'number' && a.duration > 0) {
@@ -250,7 +254,7 @@ class MnAlertService {
250
254
  let duration = input.duration;
251
255
  if (duration == null) {
252
256
  // Prefer user defaultDuration if provided and not null, otherwise use library per-kind default
253
- const userDefault = this.cfg.defaultDuration;
257
+ const userDefault = this.cfg.fallbackDuration;
254
258
  if (typeof userDefault === 'number') {
255
259
  duration = userDefault;
256
260
  }
@@ -293,8 +297,8 @@ class MnAlertService {
293
297
  }
294
298
  else {
295
299
  // userDur is undefined or null -> fallback to user defaultDuration if numeric
296
- if (typeof this.cfg.defaultDuration === 'number') {
297
- duration = this.cfg.defaultDuration;
300
+ if (typeof this.cfg.fallbackDuration === 'number') {
301
+ duration = this.cfg.fallbackDuration;
298
302
  }
299
303
  else {
300
304
  duration = this.cfg.durations[kind];
@@ -340,11 +344,11 @@ class MnAlertOutletComponent {
340
344
  };
341
345
  }
342
346
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnAlertOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
343
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: MnAlertOutletComponent, isStandalone: true, selector: "mn-alert-outlet", inputs: { template: "template" }, ngImport: i0, template: "@if (alerts$ | async; as alerts) {\n @for (a of alerts; track trackById) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"contextFor(a)\">\n </ng-container>\n }\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
347
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: MnAlertOutletComponent, isStandalone: true, selector: "mn-alert-outlet", inputs: { template: "template" }, ngImport: i0, template: "@if (alerts$ | async; as alerts) {\n @for (a of alerts; track trackById($index, a)) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"contextFor(a)\">\n </ng-container>\n }\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
344
348
  }
345
349
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnAlertOutletComponent, decorators: [{
346
350
  type: Component,
347
- args: [{ selector: 'mn-alert-outlet', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (alerts$ | async; as alerts) {\n @for (a of alerts; track trackById) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"contextFor(a)\">\n </ng-container>\n }\n}\n" }]
351
+ args: [{ selector: 'mn-alert-outlet', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (alerts$ | async; as alerts) {\n @for (a of alerts; track trackById($index, a)) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"contextFor(a)\">\n </ng-container>\n }\n}\n" }]
348
352
  }], ctorParameters: () => [], propDecorators: { template: [{
349
353
  type: Input,
350
354
  args: [{ required: true }]
@@ -356,9 +360,910 @@ class Test {
356
360
  }
357
361
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: Test, decorators: [{
358
362
  type: Component,
359
- args: [{ selector: 'lib-test', imports: [], template: "<p>test works!</p>\n", styles: ["p{color:var(--mn-primary)}\n"] }]
363
+ args: [{ selector: 'lib-test', standalone: true, imports: [], template: "<p>test works!</p>\n", styles: ["p{color:var(--mn-primary)}\n"] }]
360
364
  }] });
361
365
 
366
+ const mnButtonVariants = tv({
367
+ base: 'hover:cursor-pointer',
368
+ variants: {
369
+ size: {
370
+ sm: 'px-2 py-1 text-sm',
371
+ md: 'px-3 py-1.5 text-base',
372
+ lg: 'px-4 py-2 text-lg',
373
+ },
374
+ variant: {
375
+ fill: '',
376
+ outline: 'bg-transparent border',
377
+ text: 'bg-transparent',
378
+ },
379
+ // Intentionally empty; resolved via compoundVariants
380
+ color: {
381
+ primary: '',
382
+ secondary: '',
383
+ danger: '',
384
+ warning: '',
385
+ success: '',
386
+ },
387
+ borderRadius: {
388
+ none: 'rounded-none',
389
+ xs: 'rounded-xs',
390
+ sm: 'rounded-sm',
391
+ md: 'rounded-md',
392
+ lg: 'rounded-lg',
393
+ xl: 'rounded-xl',
394
+ two_xl: 'rounded-2xl',
395
+ three_xl: 'rounded-3xl',
396
+ four_xl: 'rounded-4xl',
397
+ },
398
+ disabled: {
399
+ true: 'opacity-50 pointer-events-none',
400
+ }
401
+ },
402
+ compoundVariants: [
403
+ // Fill
404
+ { variant: 'fill', color: 'primary', class: 'bg-blue-600 text-white hover:bg-blue-700' },
405
+ { variant: 'fill', color: 'secondary', class: 'bg-gray-600 text-white hover:bg-gray-700' },
406
+ { variant: 'fill', color: 'danger', class: 'bg-red-600 text-white hover:bg-red-700' },
407
+ { variant: 'fill', color: 'warning', class: 'bg-amber-500 text-black hover:bg-amber-600' },
408
+ { variant: 'fill', color: 'success', class: 'bg-green-600 text-white hover:bg-green-700' },
409
+ // Outline
410
+ { variant: 'outline', color: 'primary', class: 'border-blue-600 text-blue-600 hover:bg-blue-100' },
411
+ { variant: 'outline', color: 'secondary', class: 'border-gray-600 text-gray-700 hover:bg-gray-100' },
412
+ { variant: 'outline', color: 'danger', class: 'border-red-600 text-red-600 hover:bg-red-100' },
413
+ { variant: 'outline', color: 'warning', class: 'border-amber-500 text-amber-600 hover:bg-amber-100' },
414
+ { variant: 'outline', color: 'success', class: 'border-green-600 text-green-600 hover:bg-green-100' },
415
+ // Text
416
+ { variant: 'text', color: 'primary', class: 'text-blue-600 hover:bg-blue-100' },
417
+ { variant: 'text', color: 'secondary', class: 'text-gray-700 hover:bg-gray-100' },
418
+ { variant: 'text', color: 'danger', class: 'text-red-600 hover:bg-red-100' },
419
+ { variant: 'text', color: 'warning', class: 'text-amber-600 hover:bg-amber-100' },
420
+ { variant: 'text', color: 'success', class: 'text-green-600 hover:bg-green-100' },
421
+ ],
422
+ defaultVariants: {
423
+ size: 'md',
424
+ variant: 'fill',
425
+ color: 'primary',
426
+ borderRadius: 'xl',
427
+ disabled: false,
428
+ },
429
+ });
430
+
431
+ class MnButton {
432
+ data = {};
433
+ // Bind the computed classes to the host element
434
+ get hostClasses() {
435
+ return mnButtonVariants({
436
+ size: this.data.size,
437
+ variant: this.data.variant,
438
+ color: this.data.color,
439
+ borderRadius: this.data.borderRadius,
440
+ disabled: this.data.disabled,
441
+ });
442
+ }
443
+ // For accessibility (works for both <button> and <a>)
444
+ get ariaDisabled() {
445
+ return this.data.disabled ? 'true' : null;
446
+ }
447
+ // Only meaningful for <button>. For <a> it does nothing semantically.
448
+ get disabledAttr() {
449
+ return this.data.disabled ? '' : null;
450
+ }
451
+ // Make disabled anchors unfocusable + prevent activation
452
+ get tabIndex() {
453
+ return this.data.disabled ? '-1' : null;
454
+ }
455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
456
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: MnButton, isStandalone: true, selector: "button[mnButton], a[mnButton]", inputs: { data: "data" }, host: { properties: { "class": "this.hostClasses", "attr.aria-disabled": "this.ariaDisabled", "attr.disabled": "this.disabledAttr", "attr.tabindex": "this.tabIndex" } }, ngImport: i0, template: "<ng-content></ng-content>\n" });
457
+ }
458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnButton, decorators: [{
459
+ type: Component,
460
+ args: [{ selector: 'button[mnButton], a[mnButton]', standalone: true, template: "<ng-content></ng-content>\n" }]
461
+ }], propDecorators: { data: [{
462
+ type: Input
463
+ }], hostClasses: [{
464
+ type: HostBinding,
465
+ args: ['class']
466
+ }], ariaDisabled: [{
467
+ type: HostBinding,
468
+ args: ['attr.aria-disabled']
469
+ }], disabledAttr: [{
470
+ type: HostBinding,
471
+ args: ['attr.disabled']
472
+ }], tabIndex: [{
473
+ type: HostBinding,
474
+ args: ['attr.tabindex']
475
+ }] } });
476
+
477
+ /**
478
+ * MnInputField Adapters
479
+ *
480
+ * This module implements the Adapter Pattern to handle type-specific behavior
481
+ * for different HTML input types in the MnInputField component.
482
+ *
483
+ * The adapter pattern allows the component to support multiple input types
484
+ * (text, number, date, time, etc.) without coupling the component logic to
485
+ * type-specific implementations. Each adapter handles:
486
+ * - Parsing: converting raw string input to the appropriate data type
487
+ * - Formatting: converting typed values back to string for display
488
+ * - Attributes: providing type-specific DOM attributes (min, max, step, inputmode)
489
+ * - Validation: implementing type-specific validation rules
490
+ *
491
+ * This approach keeps the component code clean and makes it easy to add
492
+ * support for new input types by creating new adapters.
493
+ */
494
+ /**
495
+ * Utility function to convert empty strings to null.
496
+ * This is a common pattern for optional form fields where empty input
497
+ * should be treated as "no value" rather than an empty string.
498
+ *
499
+ * @param raw - Raw input string
500
+ * @returns The input string if non-empty, null if empty
501
+ *
502
+ * @example
503
+ * emptyToNull('hello') // => 'hello'
504
+ * emptyToNull('') // => null
505
+ */
506
+ const emptyToNull = (raw) => (raw === '' ? null : raw);
507
+ /**
508
+ * Default adapter for text-based input types.
509
+ * Used for: text, email, password, search, tel, url
510
+ *
511
+ * Behavior:
512
+ * - Empty strings are converted to null
513
+ * - Values are stored as strings in the FormControl
514
+ * - No special DOM attributes
515
+ * - No additional validation (relies on Angular's built-in validators)
516
+ */
517
+ const defaultTextAdapter = {
518
+ parse: (raw) => emptyToNull(raw),
519
+ format: (val) => (val == null ? '' : String(val)),
520
+ attrs: () => ({}),
521
+ validate: () => null,
522
+ };
523
+ /**
524
+ * Adapter for date and time input types.
525
+ * Used for: date, time, datetime-local
526
+ *
527
+ * Behavior:
528
+ * - Empty strings are converted to null
529
+ * - Values are stored as ISO 8601 strings in the FormControl
530
+ * - Provides min/max attributes from startDate/endDate props
531
+ * - Validates date/time ranges using string comparison
532
+ *
533
+ * Note: String comparison works for ISO 8601 dates/times because they are
534
+ * lexicographically ordered (e.g., '2024-01-15' < '2024-12-31').
535
+ */
536
+ const dateTimeAdapter = {
537
+ parse: (raw) => emptyToNull(raw),
538
+ format: (val) => (val == null ? '' : String(val)),
539
+ attrs: (props) => ({
540
+ min: props.startDate ?? null,
541
+ max: props.endDate ?? null,
542
+ }),
543
+ validate: (props, _control, currentRaw) => {
544
+ const value = currentRaw;
545
+ if (!value)
546
+ return null; // Don't validate empty values (use 'required' validator for that)
547
+ const min = props.startDate;
548
+ const max = props.endDate;
549
+ // Validate minimum date/time constraint
550
+ if (min && value < min) {
551
+ return { mnMin: { min, actual: value } };
552
+ }
553
+ // Validate maximum date/time constraint
554
+ if (max && value > max) {
555
+ return { mnMax: { max, actual: value } };
556
+ }
557
+ return null;
558
+ },
559
+ };
560
+ /**
561
+ * Adapter for number input type.
562
+ *
563
+ * Behavior:
564
+ * - Empty strings are converted to null
565
+ * - Valid numbers are parsed to number type
566
+ * - Invalid numbers (NaN, Infinity) are converted to null
567
+ * - Values are stored as numbers (or null) in the FormControl
568
+ * - Sets inputmode='decimal' for optimized mobile keyboards
569
+ * - No additional validation (relies on Angular's built-in validators)
570
+ *
571
+ * Note: The browser's native number input validation handles
572
+ * basic number format validation automatically.
573
+ */
574
+ const numberAdapter = {
575
+ parse: (raw) => {
576
+ if (raw === '')
577
+ return null;
578
+ const num = Number(raw);
579
+ return Number.isFinite(num) ? num : null;
580
+ },
581
+ format: (val) => (val == null ? '' : String(val)),
582
+ attrs: () => ({
583
+ inputmode: 'decimal',
584
+ }),
585
+ validate: () => null,
586
+ };
587
+ /**
588
+ * Selects the appropriate adapter based on the input type.
589
+ * This is the main factory function used by the MnInputField component
590
+ * to determine which adapter to use for a given input type.
591
+ *
592
+ * @param type - The input type (e.g., 'text', 'email', 'date', 'number')
593
+ * @returns The appropriate adapter instance
594
+ *
595
+ * @example
596
+ * pickAdapter('text') // => defaultTextAdapter
597
+ * pickAdapter('email') // => defaultTextAdapter
598
+ * pickAdapter('date') // => dateTimeAdapter
599
+ * pickAdapter('number') // => numberAdapter
600
+ */
601
+ function pickAdapter(type) {
602
+ // Date/time inputs use the dateTimeAdapter for range validation
603
+ if (type === 'date' || type === 'time' || type === 'datetime-local') {
604
+ return dateTimeAdapter;
605
+ }
606
+ // Number inputs use the numberAdapter for type conversion
607
+ if (type === 'number') {
608
+ return numberAdapter;
609
+ }
610
+ // All other input types use the default text adapter
611
+ return defaultTextAdapter;
612
+ }
613
+
614
+ const mnInputFieldVariants = tv({
615
+ base: 'bg-white border-1 border-gray-500 placeholder-gray-500 text-sm',
616
+ variants: {
617
+ shadow: {
618
+ true: 'shadow-lg',
619
+ },
620
+ size: {
621
+ sm: 'p-2',
622
+ md: 'p-3',
623
+ lg: 'p-4',
624
+ },
625
+ borderRadius: {
626
+ none: 'rounded-none',
627
+ xs: 'rounded-xs',
628
+ sm: 'rounded-sm',
629
+ md: 'rounded-md',
630
+ lg: 'rounded-lg',
631
+ xl: 'rounded-xl',
632
+ two_xl: 'rounded-2xl',
633
+ three_xl: 'rounded-3xl',
634
+ four_xl: 'rounded-4xl',
635
+ },
636
+ fullWidth: {
637
+ true: 'w-full',
638
+ }
639
+ },
640
+ defaultVariants: {
641
+ size: 'md',
642
+ borderRadius: 'md',
643
+ }
644
+ });
645
+
646
+ class MnErrorMessage {
647
+ errorMessage;
648
+ id;
649
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnErrorMessage, deps: [], target: i0.ɵɵFactoryTarget.Component });
650
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: MnErrorMessage, isStandalone: true, selector: "lib-mn-error-message", inputs: { errorMessage: "errorMessage", id: "id" }, ngImport: i0, template: "<div [id]=\"id + '-error'\" class=\"text-red-500 mt-2 text-sm\">\n {{ errorMessage }}\n</div>\n" });
651
+ }
652
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnErrorMessage, decorators: [{
653
+ type: Component,
654
+ args: [{ selector: 'lib-mn-error-message', imports: [], template: "<div [id]=\"id + '-error'\" class=\"text-red-500 mt-2 text-sm\">\n {{ errorMessage }}\n</div>\n" }]
655
+ }], propDecorators: { errorMessage: [{
656
+ type: Input,
657
+ args: [{ required: true }]
658
+ }], id: [{
659
+ type: Input,
660
+ args: [{ required: true }]
661
+ }] } });
662
+
663
+ /**
664
+ * MnInputField Component
665
+ *
666
+ * A flexible, accessible input field component that implements Angular's ControlValueAccessor
667
+ * and Validator interfaces. Supports multiple input types, custom validation messages,
668
+ * and configurable error display (single or multiple errors).
669
+ *
670
+ * Features:
671
+ * - Works with Angular Reactive Forms (FormControl, FormGroup)
672
+ * - Supports standard and date/time input types
673
+ * - Built-in error messages with internationalization support
674
+ * - Custom error messages per field
675
+ * - Priority-based error display or show all errors
676
+ * - Full accessibility (ARIA attributes)
677
+ * - Type-safe adapter pattern for different input types
678
+ *
679
+ * @example
680
+ * ```typescript
681
+ * <mn-input-field
682
+ * formControlName="email"
683
+ * [props]="{
684
+ * id: 'email',
685
+ * type: 'email',
686
+ * label: 'Email Address',
687
+ * size: 'md',
688
+ * borderRadius: 'md',
689
+ * errorMessages: { required: 'Email is required' }
690
+ * }"
691
+ * ></mn-input-field>
692
+ * ```
693
+ */
694
+ class MnInputField {
695
+ ngControl;
696
+ /** Configuration properties for the input field */
697
+ props;
698
+ /** Current raw string value of the input element */
699
+ value = null;
700
+ /** Whether the input is disabled */
701
+ isDisabled = false;
702
+ /** Callback function to notify Angular forms of value changes */
703
+ onChange = () => { };
704
+ /** Callback function to notify Angular forms when input is touched/blurred */
705
+ onTouched = () => { };
706
+ /**
707
+ * Built-in default error messages in English.
708
+ * These are used when useBuiltInErrorMessages is true (default).
709
+ * Can be overridden per-field using props.errorMessages.
710
+ */
711
+ builtInErrorMessages = {
712
+ required: 'This field is required',
713
+ email: 'Please enter a valid email address',
714
+ minlength: (args) => `Minimum ${args.requiredLength} characters required`,
715
+ maxlength: (args) => `Maximum ${args.requiredLength} characters allowed`,
716
+ mnMin: (args) => `Date/time must be from ${args.min} onwards`,
717
+ mnMax: (args) => `Date/time must be up to ${args.max}`,
718
+ };
719
+ /**
720
+ * Constructor - Registers this component as the ControlValueAccessor
721
+ * for the injected NgControl (FormControl).
722
+ *
723
+ * @param ngControl - Angular's NgControl (injected via Dependency Injection)
724
+ */
725
+ constructor(ngControl) {
726
+ this.ngControl = ngControl;
727
+ if (this.ngControl)
728
+ this.ngControl.valueAccessor = this;
729
+ }
730
+ /**
731
+ * Gets the appropriate adapter based on the input type.
732
+ * Adapters handle type-specific formatting, parsing, and validation.
733
+ */
734
+ get adapter() {
735
+ return pickAdapter(this.props.type);
736
+ }
737
+ // ========== ControlValueAccessor Implementation ==========
738
+ /**
739
+ * Writes a new value to the input element (called by Angular Forms).
740
+ * Formats the value using the type-specific adapter.
741
+ *
742
+ * @param val - The value to write (type depends on input type)
743
+ */
744
+ writeValue(val) {
745
+ this.value = this.adapter.format(val);
746
+ }
747
+ /**
748
+ * Registers a callback function to be called when the input value changes.
749
+ *
750
+ * @param fn - Callback function to notify Angular Forms of changes
751
+ */
752
+ registerOnChange(fn) {
753
+ this.onChange = fn;
754
+ }
755
+ /**
756
+ * Registers a callback function to be called when the input is touched/blurred.
757
+ *
758
+ * @param fn - Callback function to notify Angular Forms of touch events
759
+ */
760
+ registerOnTouched(fn) {
761
+ this.onTouched = fn;
762
+ }
763
+ /**
764
+ * Sets the disabled state of the input element.
765
+ *
766
+ * @param isDisabled - Whether the input should be disabled
767
+ */
768
+ setDisabledState(isDisabled) {
769
+ this.isDisabled = isDisabled;
770
+ }
771
+ // ========== Event Handlers ==========
772
+ /**
773
+ * Handles input events from the input element.
774
+ * Parses the raw string value and notifies Angular Forms.
775
+ *
776
+ * @param raw - Raw string value from the input element
777
+ */
778
+ handleInput(raw) {
779
+ this.value = raw;
780
+ this.onChange(this.adapter.parse(raw));
781
+ }
782
+ /**
783
+ * Handles blur events from the input element.
784
+ * Notifies Angular Forms that the input has been touched.
785
+ */
786
+ handleBlur() {
787
+ this.onTouched();
788
+ }
789
+ // ========== Validator Implementation ==========
790
+ /**
791
+ * Validates the control using the type-specific adapter.
792
+ * Called by Angular Forms during validation.
793
+ *
794
+ * @param control - The AbstractControl to validate
795
+ * @returns ValidationErrors if invalid, null if valid
796
+ */
797
+ validate(control) {
798
+ return this.adapter.validate(this.props, control, this.value);
799
+ }
800
+ // ========== Template Attribute Getters ==========
801
+ /**
802
+ * Gets all DOM attributes from the adapter.
803
+ * These are input-type-specific attributes (min, max, step, inputmode).
804
+ */
805
+ get domAttrs() {
806
+ return this.adapter.attrs(this.props);
807
+ }
808
+ /** Min attribute for date/time/number inputs */
809
+ get minAttr() {
810
+ return this.domAttrs.min ?? null;
811
+ }
812
+ /** Max attribute for date/time/number inputs */
813
+ get maxAttr() {
814
+ return this.domAttrs.max ?? null;
815
+ }
816
+ /** Step attribute for number/date/time inputs */
817
+ get stepAttr() {
818
+ return this.domAttrs.step ?? null;
819
+ }
820
+ /** Inputmode attribute for mobile keyboard optimization */
821
+ get inputmodeAttr() {
822
+ return this.domAttrs.inputmode ?? null;
823
+ }
824
+ // ========== Error Handling ==========
825
+ /**
826
+ * Gets the FormControl instance from Angular Forms.
827
+ * Returns null if no control is attached.
828
+ */
829
+ get control() {
830
+ return this.ngControl?.control ?? null;
831
+ }
832
+ /**
833
+ * Determines whether to show error messages.
834
+ * Errors are shown when the control is invalid and has been touched or modified.
835
+ */
836
+ get showError() {
837
+ const c = this.control;
838
+ return !!c && c.invalid && (c.touched || c.dirty);
839
+ }
840
+ /**
841
+ * Picks the error key to display based on errorPriority.
842
+ * Used when showAllErrors is false (default).
843
+ *
844
+ * @param errors - ValidationErrors object from the control
845
+ * @returns The error key to display
846
+ */
847
+ pickErrorKey(errors) {
848
+ // If priority is specified, use the first matching error from the priority list
849
+ if (this.props.errorPriority) {
850
+ for (const key of this.props.errorPriority) {
851
+ if (errors[key] !== undefined) {
852
+ return key;
853
+ }
854
+ }
855
+ }
856
+ // Otherwise, use the first error key
857
+ return Object.keys(errors)[0];
858
+ }
859
+ isRequired() {
860
+ if (!this.control)
861
+ return false;
862
+ return this.control.hasValidator(Validators.required);
863
+ }
864
+ /**
865
+ * Resolves a single error message for a specific error key.
866
+ * Checks custom messages, built-in messages, and fallback in order.
867
+ *
868
+ * @param errorKey - The error key (e.g., 'required', 'email')
869
+ * @param errors - All validation errors on the control
870
+ * @returns The resolved error message string
871
+ */
872
+ resolveErrorMessageForKey(errorKey, errors) {
873
+ const errorArgs = errors[errorKey];
874
+ // Priority: custom > built-in > fallback > default
875
+ const customMsg = this.props.errorMessages?.[errorKey];
876
+ const useBuiltIn = this.props.useBuiltInErrorMessages !== false;
877
+ const builtInMsg = useBuiltIn ? this.builtInErrorMessages[errorKey] : undefined;
878
+ const fallbackMsg = this.props.defaultErrorMessage;
879
+ const msgDef = customMsg ?? builtInMsg ?? fallbackMsg ?? 'Invalid input';
880
+ // If the message is a function, call it with error arguments
881
+ if (typeof msgDef === 'function') {
882
+ return msgDef(errorArgs, errors);
883
+ }
884
+ return msgDef;
885
+ }
886
+ /**
887
+ * Gets all error messages for the current control state.
888
+ * Returns an array of error messages (used when showAllErrors is true).
889
+ *
890
+ * @returns Array of error message strings
891
+ */
892
+ get errorMessages() {
893
+ const errors = this.control?.errors;
894
+ if (!errors)
895
+ return [];
896
+ const errorKeys = Object.keys(errors);
897
+ return errorKeys.map(key => this.resolveErrorMessageForKey(key, errors));
898
+ }
899
+ /**
900
+ * Gets a single error message for the current control state.
901
+ * Uses errorPriority to determine which error to show (when showAllErrors is false).
902
+ *
903
+ * @returns Single error message string, or null if no errors
904
+ */
905
+ get errorMessage() {
906
+ const errors = this.control?.errors;
907
+ if (!errors)
908
+ return null;
909
+ const errorKey = this.pickErrorKey(errors);
910
+ return this.resolveErrorMessageForKey(errorKey, errors);
911
+ }
912
+ // ========== Resolved Properties ==========
913
+ /** Resolved ID for the input element */
914
+ get resolvedId() {
915
+ return this.props.id;
916
+ }
917
+ /** Resolved name attribute for the input element */
918
+ get resolvedName() {
919
+ return this.props?.name ?? null;
920
+ }
921
+ /**
922
+ * Computes the CSS classes from tailwind-variants based on the props.
923
+ * Returns the variant classes for styling the input element.
924
+ */
925
+ get inputClasses() {
926
+ return mnInputFieldVariants({
927
+ size: this.props.size,
928
+ borderRadius: this.props.borderRadius,
929
+ shadow: this.props.shadow,
930
+ fullWidth: this.props.fullWidth,
931
+ });
932
+ }
933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInputField, deps: [{ token: i1$1.NgControl, optional: true, self: true }], target: i0.ɵɵFactoryTarget.Component });
934
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: MnInputField, isStandalone: true, selector: "mn-input-field", inputs: { props: "props" }, ngImport: i0, template: "<div class=\"flex flex-col\" [class.is-fullwidth]=\"props.fullWidth\">\n <!-- Label -->\n @if (props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500 \">*</span>\n }\n </label>\n }\n\n <!-- Input Element -->\n <input\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"props.type\"\n [attr.placeholder]=\"props.placeholder || null\"\n [attr.aria-label]=\"props.ariaLabel || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [disabled]=\"isDisabled\"\n [attr.min]=\"minAttr\"\n [attr.max]=\"maxAttr\"\n [value]=\"value ?? ''\"\n [ngClass]=\"inputClasses\"\n (input)=\"handleInput(($any($event.target)).value)\"\n (blur)=\"handleBlur()\"\n />\n\n <!-- Error Messages -->\n @if (showError) {\n <!-- Show all errors mode -->\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1\">\n @for (error of errorMessages; track $index) {\n <lib-mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></lib-mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage != null) {\n <lib-mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></lib-mn-error-message>\n\n }\n }\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MnErrorMessage, selector: "lib-mn-error-message", inputs: ["errorMessage", "id"] }] });
935
+ }
936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInputField, decorators: [{
937
+ type: Component,
938
+ args: [{ selector: 'mn-input-field', standalone: true, imports: [NgClass, MnErrorMessage], template: "<div class=\"flex flex-col\" [class.is-fullwidth]=\"props.fullWidth\">\n <!-- Label -->\n @if (props.label) {\n <label class=\"pl-2 pb-1 flex flex-row gap-x-0.5! text-base!\" [attr.for]=\"resolvedId\">\n <p>{{ props.label }}</p>\n @if (isRequired()) {\n <span class=\"text-red-500 \">*</span>\n }\n </label>\n }\n\n <!-- Input Element -->\n <input\n [id]=\"resolvedId\"\n [attr.name]=\"resolvedName\"\n [type]=\"props.type\"\n [attr.placeholder]=\"props.placeholder || null\"\n [attr.aria-label]=\"props.ariaLabel || props.label || null\"\n [attr.aria-invalid]=\"showError || null\"\n [attr.aria-describedby]=\"showError ? resolvedId + '-error' : null\"\n [disabled]=\"isDisabled\"\n [attr.min]=\"minAttr\"\n [attr.max]=\"maxAttr\"\n [value]=\"value ?? ''\"\n [ngClass]=\"inputClasses\"\n (input)=\"handleInput(($any($event.target)).value)\"\n (blur)=\"handleBlur()\"\n />\n\n <!-- Error Messages -->\n @if (showError) {\n <!-- Show all errors mode -->\n @if (props.showAllErrors) {\n <div class=\"flex flex-col gap-y-1\">\n @for (error of errorMessages; track $index) {\n <lib-mn-error-message [errorMessage]=\"error\" [id]=\"resolvedId + '-' + $index\"></lib-mn-error-message>\n }\n </div>\n } @else {\n @if (errorMessage != null) {\n <lib-mn-error-message [errorMessage]=\"errorMessage\" [id]=\"resolvedId\"></lib-mn-error-message>\n\n }\n }\n }\n</div>\n" }]
939
+ }], ctorParameters: () => [{ type: i1$1.NgControl, decorators: [{
940
+ type: Optional
941
+ }, {
942
+ type: Self
943
+ }] }], propDecorators: { props: [{
944
+ type: Input,
945
+ args: [{ required: true }]
946
+ }] } });
947
+
948
+ /**
949
+ * Represents the current section path based on nested mn-section directives.
950
+ */
951
+ const MN_SECTION_PATH = new InjectionToken('MN_SECTION_PATH', {
952
+ providedIn: 'root',
953
+ factory: () => [],
954
+ });
955
+ /**
956
+ * Represents the current component instance id provided by [mn-instance].
957
+ */
958
+ const MN_INSTANCE_ID = new InjectionToken('MN_INSTANCE_ID', {
959
+ providedIn: 'root',
960
+ factory: () => null,
961
+ });
962
+
963
+ function isPlainObject(value) {
964
+ return (typeof value === 'object' &&
965
+ value !== null &&
966
+ Object.prototype.toString.call(value) === '[object Object]');
967
+ }
968
+ class MnConfigService {
969
+ http;
970
+ _config = null;
971
+ constructor(http) {
972
+ this.http = http;
973
+ }
974
+ /**
975
+ * Load the configuration JSON from the provided URL and cache it in memory.
976
+ * Consumers should typically call this via the APP_INITIALIZER helper.
977
+ */
978
+ async load(url) {
979
+ let json = await firstValueFrom(this.http.get(url, { responseType: 'json' }));
980
+ if (typeof json === 'string') {
981
+ try {
982
+ json = JSON.parse(json);
983
+ }
984
+ catch {
985
+ json = {};
986
+ }
987
+ }
988
+ const cfg = (isPlainObject(json) ? json : {});
989
+ const defaults = (isPlainObject(cfg.defaults) ? cfg.defaults : {});
990
+ const overrides = isPlainObject(cfg.overrides) ? cfg.overrides : cfg.overrides ?? {};
991
+ this._config = { defaults, overrides };
992
+ }
993
+ /**
994
+ * Resolve a configuration object for a component, optionally scoped to a section path
995
+ * and optionally overridden by an instance id.
996
+ */
997
+ resolve(componentName, sectionPath = [], instanceId) {
998
+ const baseConfig = isPlainObject(this._config?.defaults)
999
+ ? (isPlainObject(this._config.defaults[componentName])
1000
+ ? { ...this._config.defaults[componentName] }
1001
+ : {})
1002
+ : {};
1003
+ const leaf = this.walkOverrides(this._config?.overrides ?? {}, sectionPath);
1004
+ let resolved = baseConfig;
1005
+ if (leaf && isPlainObject(leaf[componentName])) {
1006
+ resolved = this.deepMerge(resolved, leaf[componentName]);
1007
+ }
1008
+ if (instanceId) {
1009
+ const instKey = `#${instanceId}`;
1010
+ if (leaf && isPlainObject(leaf[instKey])) {
1011
+ resolved = this.deepMerge(resolved, leaf[instKey]);
1012
+ }
1013
+ }
1014
+ return resolved;
1015
+ }
1016
+ /**
1017
+ * Walk the overrides nested object using the provided section path and return the leaf node.
1018
+ * If any segment is missing or the current node is not a plain object, returns undefined.
1019
+ */
1020
+ walkOverrides(overridesRoot, sectionPath) {
1021
+ let node = overridesRoot;
1022
+ for (const segment of sectionPath) {
1023
+ if (!isPlainObject(node))
1024
+ return undefined;
1025
+ node = node[segment];
1026
+ if (node === undefined)
1027
+ return undefined;
1028
+ }
1029
+ return node;
1030
+ }
1031
+ /**
1032
+ * Deep merge two plain-object trees. Arrays and non-plain values are replaced by the patch.
1033
+ * Does not mutate inputs; returns a new object.
1034
+ */
1035
+ deepMerge(base, patch) {
1036
+ const out = { ...base };
1037
+ for (const key of Object.keys(patch)) {
1038
+ const bVal = base[key];
1039
+ const pVal = patch[key];
1040
+ if (isPlainObject(bVal) && isPlainObject(pVal)) {
1041
+ out[key] = this.deepMerge(bVal, pVal);
1042
+ }
1043
+ else {
1044
+ // replace for arrays, primitives, null, undefined, and non-plain objects
1045
+ out[key] = pVal;
1046
+ }
1047
+ }
1048
+ return out;
1049
+ }
1050
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnConfigService, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
1051
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnConfigService, providedIn: 'root' });
1052
+ }
1053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnConfigService, decorators: [{
1054
+ type: Injectable,
1055
+ args: [{ providedIn: 'root' }]
1056
+ }], ctorParameters: () => [{ type: i1$2.HttpClient }] });
1057
+
1058
+ /**
1059
+ * Helper to provide a resolved, typed component config via DI.
1060
+ *
1061
+ * Usage in a component/module providers:
1062
+ * const MY_CFG = new InjectionToken<MyCfg>('MY_CFG');
1063
+ * providers: [ provideMnComponentConfig(MY_CFG, 'my-component') ]
1064
+ * Then in the component:
1065
+ * readonly cfg = inject(MY_CFG)
1066
+ */
1067
+ function provideMnComponentConfig(token, componentName, initial) {
1068
+ return {
1069
+ provide: token,
1070
+ deps: [MnConfigService, [new Optional(), MN_SECTION_PATH], [new Optional(), MN_INSTANCE_ID]],
1071
+ useFactory: (svc, sectionPath, instanceId) => {
1072
+ const resolved = svc.resolve(componentName, sectionPath ?? [], instanceId ?? undefined);
1073
+ // Apply optional initial (local defaults) over resolved? We prefer resolved to override initial local defaults,
1074
+ // so merge initial first, then resolved on top.
1075
+ return Object.assign({}, initial ?? {}, resolved);
1076
+ },
1077
+ };
1078
+ }
1079
+
1080
+ const MN_TEST_COMPONENT_CONFIG = new InjectionToken('MN_TEST_COMPONENT_CONFIG');
1081
+ class MnTestComponent {
1082
+ cfg = inject(MN_TEST_COMPONENT_CONFIG);
1083
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnTestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1084
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: MnTestComponent, isStandalone: true, selector: "mn-test-component", providers: [
1085
+ provideMnComponentConfig(MN_TEST_COMPONENT_CONFIG, 'test-component'),
1086
+ ], ngImport: i0, template: `
1087
+ <div class="mn-test" [style.color]="(cfg.color ?? 'inherit')">
1088
+ {{ cfg.text ?? 'Hello from component' }}
1089
+ </div>
1090
+ `, isInline: true, styles: [".mn-test{font-weight:600;padding:8px 12px;border:1px dashed #ddd;border-radius:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1091
+ }
1092
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnTestComponent, decorators: [{
1093
+ type: Component,
1094
+ args: [{ selector: 'mn-test-component', standalone: true, imports: [CommonModule], providers: [
1095
+ provideMnComponentConfig(MN_TEST_COMPONENT_CONFIG, 'test-component'),
1096
+ ], template: `
1097
+ <div class="mn-test" [style.color]="(cfg.color ?? 'inherit')">
1098
+ {{ cfg.text ?? 'Hello from component' }}
1099
+ </div>
1100
+ `, styles: [".mn-test{font-weight:600;padding:8px 12px;border:1px dashed #ddd;border-radius:6px}\n"] }]
1101
+ }] });
1102
+
1103
+ class MnDualHorizontalImage {
1104
+ _images = [];
1105
+ set images(value) {
1106
+ this._images = (value ?? []).slice(0, 2);
1107
+ }
1108
+ get images() {
1109
+ return this._images;
1110
+ }
1111
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnDualHorizontalImage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1112
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: MnDualHorizontalImage, isStandalone: true, selector: "lib-mn-dual-horizontal-image", inputs: { images: "images" }, ngImport: i0, template: "<div class=\"flex flex-col w-full -space-y-5 md:-space-y-10 lg:-space-y-5\">\n @for (image of images; track image.id) {\n <div class=\"w-[75%] overflow-hidden rounded-3xl shadow-md md:w-[65%] lg:w-[80%] first:self-start last:self-end last:mb-4\">\n <img [ngSrc]=\"image.url\" width=\"150\" height=\"100\" class=\"size-full object-cover\" [alt]=\"image.alt\" />\n </div>\n } @empty {\n <p>No images found</p>\n }\n</div>\n\n", dependencies: [{ kind: "directive", type: NgOptimizedImage, selector: "img[ngSrc]", inputs: ["ngSrc", "ngSrcset", "sizes", "width", "height", "decoding", "loading", "priority", "loaderParams", "disableOptimizedSrcset", "fill", "placeholder", "placeholderConfig", "src", "srcset"] }] });
1113
+ }
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnDualHorizontalImage, decorators: [{
1115
+ type: Component,
1116
+ args: [{ selector: 'lib-mn-dual-horizontal-image', standalone: true, imports: [
1117
+ NgOptimizedImage
1118
+ ], template: "<div class=\"flex flex-col w-full -space-y-5 md:-space-y-10 lg:-space-y-5\">\n @for (image of images; track image.id) {\n <div class=\"w-[75%] overflow-hidden rounded-3xl shadow-md md:w-[65%] lg:w-[80%] first:self-start last:self-end last:mb-4\">\n <img [ngSrc]=\"image.url\" width=\"150\" height=\"100\" class=\"size-full object-cover\" [alt]=\"image.alt\" />\n </div>\n } @empty {\n <p>No images found</p>\n }\n</div>\n\n" }]
1119
+ }], propDecorators: { images: [{
1120
+ type: Input
1121
+ }] } });
1122
+
1123
+ const mnInformationCardVariants = tv({
1124
+ base: '',
1125
+ variants: {
1126
+ bottomBorder: {
1127
+ true: 'border-b border-b-2 border-brand-500',
1128
+ },
1129
+ shadow: {
1130
+ true: 'shadow-lg',
1131
+ },
1132
+ textPosition: {
1133
+ left: 'text-left',
1134
+ center: 'text-center',
1135
+ right: 'text-right',
1136
+ },
1137
+ borderRadius: {
1138
+ none: 'rounded-none',
1139
+ xs: 'rounded-xs',
1140
+ sm: 'rounded-sm',
1141
+ md: 'rounded-md',
1142
+ lg: 'rounded-lg',
1143
+ xl: 'rounded-xl',
1144
+ two_xl: 'rounded-2xl',
1145
+ three_xl: 'rounded-3xl',
1146
+ four_xl: 'rounded-4xl',
1147
+ },
1148
+ },
1149
+ });
1150
+
1151
+ class MnInformationCard {
1152
+ data;
1153
+ get hostClasses() {
1154
+ return mnInformationCardVariants({
1155
+ bottomBorder: this.data.bottomBorder,
1156
+ shadow: this.data.shadow,
1157
+ textPosition: this.data.textPosition,
1158
+ });
1159
+ }
1160
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInformationCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1161
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: MnInformationCard, isStandalone: true, selector: "lib-mn-information-card", inputs: { data: "data" }, ngImport: i0, template: "<div class=\"flex flex-col items-center gap-y-4 p-4 size-full\" [ngClass]=\"hostClasses\">\n<ng-content select=\"[header]\"></ng-content>\n<ng-content></ng-content>\n<ng-content select=\"[footer]\"></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
1162
+ }
1163
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInformationCard, decorators: [{
1164
+ type: Component,
1165
+ args: [{ selector: 'lib-mn-information-card', standalone: true, imports: [
1166
+ NgClass
1167
+ ], template: "<div class=\"flex flex-col items-center gap-y-4 p-4 size-full\" [ngClass]=\"hostClasses\">\n<ng-content select=\"[header]\"></ng-content>\n<ng-content></ng-content>\n<ng-content select=\"[footer]\"></ng-content>\n</div>\n" }]
1168
+ }], propDecorators: { data: [{
1169
+ type: Input,
1170
+ args: [{ required: true }]
1171
+ }] } });
1172
+
1173
+ /**
1174
+ * Types for mn-lib configuration.
1175
+ */
1176
+
1177
+ /**
1178
+ * Provides an APP_INITIALIZER that loads the mn-lib configuration from the given URL
1179
+ * during application bootstrap. The consuming application is responsible for providing
1180
+ * HttpClient (e.g., via HttpClientModule or provideHttpClient()).
1181
+ */
1182
+ function provideMnConfig(url) {
1183
+ return [
1184
+ {
1185
+ provide: APP_INITIALIZER,
1186
+ multi: true,
1187
+ useFactory: (svc) => () => svc.load(url),
1188
+ deps: [MnConfigService],
1189
+ },
1190
+ ];
1191
+ }
1192
+
1193
+ class MnSectionDirective {
1194
+ /** Section name contributed by this DOM node to the section path */
1195
+ mnSection;
1196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnSectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1197
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: MnSectionDirective, isStandalone: true, selector: "[mn-section]", inputs: { mnSection: ["mn-section", "mnSection"] }, providers: [
1198
+ {
1199
+ provide: MN_SECTION_PATH,
1200
+ // Read parent MN_SECTION_PATH from ancestor injector (skipSelf to avoid self-reference),
1201
+ // and read the attribute value using Attribute so it's available at provider creation time.
1202
+ deps: [[new Optional(), new SkipSelf(), MN_SECTION_PATH], new Attribute('mn-section')],
1203
+ useFactory: (parentPath, attr) => {
1204
+ const parent = Array.isArray(parentPath) ? parentPath : [];
1205
+ const name = (attr ?? '').trim();
1206
+ return name ? [...parent, name] : [...parent];
1207
+ },
1208
+ },
1209
+ ], ngImport: i0 });
1210
+ }
1211
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnSectionDirective, decorators: [{
1212
+ type: Directive,
1213
+ args: [{
1214
+ selector: '[mn-section]',
1215
+ standalone: true,
1216
+ providers: [
1217
+ {
1218
+ provide: MN_SECTION_PATH,
1219
+ // Read parent MN_SECTION_PATH from ancestor injector (skipSelf to avoid self-reference),
1220
+ // and read the attribute value using Attribute so it's available at provider creation time.
1221
+ deps: [[new Optional(), new SkipSelf(), MN_SECTION_PATH], new Attribute('mn-section')],
1222
+ useFactory: (parentPath, attr) => {
1223
+ const parent = Array.isArray(parentPath) ? parentPath : [];
1224
+ const name = (attr ?? '').trim();
1225
+ return name ? [...parent, name] : [...parent];
1226
+ },
1227
+ },
1228
+ ],
1229
+ }]
1230
+ }], propDecorators: { mnSection: [{
1231
+ type: Input,
1232
+ args: ['mn-section']
1233
+ }] } });
1234
+
1235
+ class MnInstanceDirective {
1236
+ /** Instance id for targeting per-component instance overrides */
1237
+ mnInstance;
1238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInstanceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1239
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: MnInstanceDirective, isStandalone: true, selector: "[mn-instance]", inputs: { mnInstance: ["mn-instance", "mnInstance"] }, providers: [
1240
+ {
1241
+ provide: MN_INSTANCE_ID,
1242
+ // Read the attribute at provider creation time using Attribute token; Inputs may not be set yet.
1243
+ deps: [new Attribute('mn-instance')],
1244
+ useFactory: (attr) => (attr ?? '').trim() || null,
1245
+ },
1246
+ ], ngImport: i0 });
1247
+ }
1248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: MnInstanceDirective, decorators: [{
1249
+ type: Directive,
1250
+ args: [{
1251
+ selector: '[mn-instance]',
1252
+ standalone: true,
1253
+ providers: [
1254
+ {
1255
+ provide: MN_INSTANCE_ID,
1256
+ // Read the attribute at provider creation time using Attribute token; Inputs may not be set yet.
1257
+ deps: [new Attribute('mn-instance')],
1258
+ useFactory: (attr) => (attr ?? '').trim() || null,
1259
+ },
1260
+ ],
1261
+ }]
1262
+ }], propDecorators: { mnInstance: [{
1263
+ type: Input,
1264
+ args: ['mn-instance']
1265
+ }] } });
1266
+
362
1267
  /*
363
1268
  * Public API Surface of mn-lib
364
1269
  */
@@ -367,5 +1272,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
367
1272
  * Generated bundle index. Do not edit.
368
1273
  */
369
1274
 
370
- export { DEFAULT_MN_ALERT_CONFIG, MN_ALERT_CONFIG, MN_THEME, MN_THEME_DEFAULTS, MnAlertOutletComponent, MnAlertService, MnAlertStore, MnThemeService, Test, injectTheme, injectThemeSignal, provideMnAlerts, provideMnTheme, provideMnThemeDynamic };
1275
+ export { DEFAULT_MN_ALERT_CONFIG, MN_ALERT_CONFIG, MN_INSTANCE_ID, MN_SECTION_PATH, MN_TEST_COMPONENT_CONFIG, MN_THEME, MN_THEME_DEFAULTS, MnAlertOutletComponent, MnAlertService, MnAlertStore, MnButton, MnConfigService, MnDualHorizontalImage, MnInformationCard, MnInputField, MnInstanceDirective, MnSectionDirective, MnTestComponent, MnThemeService, Test, dateTimeAdapter, defaultTextAdapter, injectTheme, injectThemeSignal, mnInformationCardVariants, mnInputFieldVariants, numberAdapter, pickAdapter, provideMnAlerts, provideMnComponentConfig, provideMnConfig, provideMnTheme, provideMnThemeDynamic };
371
1276
  //# sourceMappingURL=mn-angular-lib.mjs.map