@neuravision/ng-construct 0.4.2 → 0.5.1

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,23 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { ElementRef, TemplateRef, OnDestroy, AfterViewInit, Signal, AfterContentInit, AfterContentChecked, PipeTransform } from '@angular/core';
3
- import { ControlValueAccessor } from '@angular/forms';
2
+ import { InjectionToken, ElementRef, TemplateRef, OnDestroy, AfterViewInit, Signal, AfterContentInit, AfterContentChecked, PipeTransform } from '@angular/core';
3
+ import * as _neuravision_ng_construct from '@neuravision/ng-construct';
4
+ import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
4
5
 
5
6
  type AfAlertVariant = 'info' | 'success' | 'warning' | 'danger';
7
+ /** Translatable strings used by the alert component. */
8
+ interface AfAlertI18n {
9
+ /** Label for the dismiss button (default: `'Dismiss alert'`). */
10
+ dismiss: string;
11
+ /** Screen-reader announcement when the alert is dismissed (default: `'Alert dismissed'`). */
12
+ dismissed: string;
13
+ }
14
+ /**
15
+ * Injection token to provide custom i18n strings for {@link AfAlertComponent}.
16
+ *
17
+ * @example
18
+ * providers: [{ provide: AF_ALERT_I18N, useValue: { dismiss: 'Schliessen', dismissed: 'Warnung geschlossen' } }]
19
+ */
20
+ declare const AF_ALERT_I18N: InjectionToken<AfAlertI18n>;
6
21
  /**
7
22
  * Alert component for displaying contextual feedback messages.
8
23
  *
@@ -10,6 +25,14 @@ type AfAlertVariant = 'info' | 'success' | 'warning' | 'danger';
10
25
  * - `danger` / `warning` → `role="alert"` (assertive announcement)
11
26
  * - `info` / `success` → `role="status"` (polite announcement)
12
27
  *
28
+ * ### Accessibility
29
+ * - The dismiss button is keyboard-accessible via Tab, activated by Enter/Space (native `<button>`)
30
+ * - When dismissed, a screen-reader announcement is made via an `aria-live="polite"` region
31
+ * - The icon slot is marked `aria-hidden="true"` to prevent redundant announcements
32
+ * - The `aria-label` on the dismiss button is configurable via {@link AF_ALERT_I18N} for i18n
33
+ * - Supports `forced-colors` (Windows High Contrast) mode
34
+ * - Uses CSS logical properties for RTL layout support
35
+ *
13
36
  * @example
14
37
  * <af-alert variant="warning" [dismissible]="true" (dismissed)="onDismiss()">
15
38
  * <span icon>⚠</span>
@@ -21,14 +44,17 @@ type AfAlertVariant = 'info' | 'success' | 'warning' | 'danger';
21
44
  * </af-alert>
22
45
  */
23
46
  declare class AfAlertComponent {
24
- /** Color variant determining the alert's visual style and ARIA role */
47
+ protected readonly i18n: AfAlertI18n;
48
+ /** Color variant determining the alert's visual style and ARIA role. */
25
49
  variant: _angular_core.InputSignal<AfAlertVariant>;
26
- /** Whether the alert can be dismissed by the user */
50
+ /** Whether the alert can be dismissed by the user. */
27
51
  dismissible: _angular_core.InputSignal<boolean>;
28
- /** Emits when the user dismisses the alert */
52
+ /** Emits when the user dismisses the alert. */
29
53
  dismissed: _angular_core.OutputEmitterRef<void>;
30
- /** Controls alert visibility */
54
+ /** Controls alert visibility. */
31
55
  visible: _angular_core.WritableSignal<boolean>;
56
+ /** @internal Screen-reader announcement text. */
57
+ liveAnnouncement: _angular_core.WritableSignal<string>;
32
58
  alertClasses: _angular_core.Signal<string>;
33
59
  /**
34
60
  * Maps variant to ARIA role:
@@ -36,12 +62,50 @@ declare class AfAlertComponent {
36
62
  * - info/success → 'status' (polite, non-intrusive announcement)
37
63
  */
38
64
  alertRole: _angular_core.Signal<"alert" | "status">;
39
- /** Hides the alert and emits the dismissed event */
65
+ /** Hides the alert and emits the dismissed event. */
40
66
  dismiss(): void;
41
67
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfAlertComponent, never>;
42
68
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfAlertComponent, "af-alert", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; }, { "dismissed": "dismissed"; }, never, ["[icon]", "[title]", "*", "[actions]"], true, never>;
43
69
  }
44
70
 
71
+ /**
72
+ * Test harness for AfAlertComponent.
73
+ *
74
+ * Provides a semantic API for interacting with the alert in tests,
75
+ * abstracting DOM details behind readable method names.
76
+ *
77
+ * @example
78
+ * const harness = new AfAlertHarness(fixture.nativeElement);
79
+ * expect(harness.getVariant()).toBe('info');
80
+ * expect(harness.getRole()).toBe('status');
81
+ * harness.dismiss();
82
+ */
83
+ declare class AfAlertHarness {
84
+ private readonly hostEl;
85
+ constructor(container: HTMLElement);
86
+ /** Returns the inner `.ct-alert` wrapper element, or `null` if the alert is not visible. */
87
+ getAlertElement(): HTMLElement | null;
88
+ /** Returns the `data-variant` attribute value. Throws if the alert is not visible. */
89
+ getVariant(): string;
90
+ /** Returns the ARIA `role` attribute value. Throws if the alert is not visible. */
91
+ getRole(): string;
92
+ /** Returns the full trimmed text content of the alert. Throws if not visible. */
93
+ getText(): string;
94
+ /** Returns the trimmed text content of the title slot. Throws if not visible. */
95
+ getTitle(): string;
96
+ /** Returns whether the alert is currently visible in the DOM. */
97
+ isVisible(): boolean;
98
+ /** Returns whether the alert has a dismiss button. Throws if not visible. */
99
+ isDismissible(): boolean;
100
+ /** Clicks the dismiss button. Throws if the alert is not visible or not dismissible. */
101
+ dismiss(): void;
102
+ /** Returns the dismiss button element, or `null` if not present. */
103
+ getDismissButton(): HTMLButtonElement | null;
104
+ /** Returns the `aria-live` region element for screen-reader announcements. */
105
+ getLiveRegion(): HTMLElement | null;
106
+ private requireVisible;
107
+ }
108
+
45
109
  /**
46
110
  * Individual accordion item used within af-accordion.
47
111
  *
@@ -53,6 +117,8 @@ declare class AfAlertComponent {
53
117
  declare class AfAccordionItemComponent {
54
118
  private itemId;
55
119
  private accordion;
120
+ private announcer;
121
+ private i18n;
56
122
  /** Heading text displayed in the accordion trigger. */
57
123
  heading: _angular_core.InputSignal<string>;
58
124
  /** Whether this item is expanded (supports two-way binding). */
@@ -109,6 +175,69 @@ declare class AfAccordionComponent {
109
175
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfAccordionComponent, "af-accordion", never, { "multi": { "alias": "multi"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; }, {}, ["items"], ["*"], true, never>;
110
176
  }
111
177
 
178
+ /**
179
+ * Test harness for AfAccordionItemComponent.
180
+ *
181
+ * Provides a semantic API for interacting with a single accordion item,
182
+ * abstracting DOM details behind readable method names.
183
+ */
184
+ declare class AfAccordionItemHarness {
185
+ private readonly hostEl;
186
+ constructor(hostEl: HTMLElement);
187
+ /** Returns the heading text. */
188
+ getHeading(): string;
189
+ /** Returns whether the item is expanded. */
190
+ isExpanded(): boolean;
191
+ /** Returns whether the item is disabled. */
192
+ isDisabled(): boolean;
193
+ /** Clicks the trigger to toggle the item. */
194
+ toggle(): void;
195
+ /** Returns the trigger (summary) element. */
196
+ getTriggerElement(): HTMLElement;
197
+ /** Returns the content panel element. */
198
+ getContentElement(): HTMLElement;
199
+ /** Returns the `aria-controls` value of the trigger. */
200
+ getAriaControls(): string | null;
201
+ /** Returns the `aria-expanded` value of the trigger. */
202
+ getAriaExpanded(): string | null;
203
+ }
204
+ /**
205
+ * Test harness for AfAccordionComponent.
206
+ *
207
+ * @example
208
+ * const harness = new AfAccordionHarness(fixture.nativeElement);
209
+ * expect(harness.getItems()).toHaveLength(3);
210
+ * harness.getItem(0).toggle();
211
+ */
212
+ declare class AfAccordionHarness {
213
+ private readonly hostEl;
214
+ constructor(container: HTMLElement);
215
+ /** Returns harnesses for all accordion items. */
216
+ getItems(): AfAccordionItemHarness[];
217
+ /** Returns the harness for the accordion item at the given index. */
218
+ getItem(index: number): AfAccordionItemHarness;
219
+ /** Returns whether the bordered variant is applied. */
220
+ isBordered(): boolean;
221
+ }
222
+
223
+ /** Translatable strings used by the accordion component. */
224
+ interface AfAccordionI18n {
225
+ /** Announcement when a section is expanded. Use `{heading}` as placeholder. */
226
+ expanded: string;
227
+ /** Announcement when a section is collapsed. Use `{heading}` as placeholder. */
228
+ collapsed: string;
229
+ }
230
+ /**
231
+ * Injection token to override accordion screen-reader announcements.
232
+ *
233
+ * @example
234
+ * providers: [{
235
+ * provide: AF_ACCORDION_I18N,
236
+ * useValue: { expanded: '{heading} ausgeklappt', collapsed: '{heading} eingeklappt' },
237
+ * }]
238
+ */
239
+ declare const AF_ACCORDION_I18N: InjectionToken<AfAccordionI18n>;
240
+
112
241
  type AfShellSidebarState = 'expanded' | 'collapsed' | 'hidden';
113
242
  type AfShellPanelState = 'open' | 'closed';
114
243
  /**
@@ -283,6 +412,12 @@ declare class AfAppShellV2Component {
283
412
 
284
413
  type AfAvatarSize = 'sm' | 'md' | 'lg' | 'xl';
285
414
  type AfAvatarStatus = 'online' | 'offline' | 'busy' | 'away';
415
+ /**
416
+ * Number of distinct colors in the seeded avatar palette. Must match the
417
+ * `[data-seed-color="N"]` selectors shipped by `@neuravision/construct`
418
+ * (see `components/avatar.css`). Bump together when Construct adds slots.
419
+ */
420
+ declare const AVATAR_SEED_PALETTE_SIZE = 8;
286
421
  /**
287
422
  * Avatar component displaying a user image with fallback to initials.
288
423
  *
@@ -290,9 +425,16 @@ type AfAvatarStatus = 'online' | 'offline' | 'busy' | 'away';
290
425
  * fails to load or no `src` is given, initials derived from `name` are
291
426
  * shown instead.
292
427
  *
428
+ * Set `colorSeed` to give each user a stable, deterministic background
429
+ * color picked from the Construct DS palette — useful in lists where the
430
+ * eye should recognize repeat individuals at a glance. The seed is hashed
431
+ * locally and bound to `data-seed-color`; an empty seed leaves the
432
+ * attribute off and the avatar keeps the default background.
433
+ *
293
434
  * @example
294
435
  * <af-avatar src="/photo.jpg" name="Jane Doe" alt="Jane Doe" size="lg" />
295
436
  * <af-avatar name="John Smith" status="online" />
437
+ * <af-avatar name="Jane Doe" colorSeed="user-uuid-7b3e2a4d" />
296
438
  */
297
439
  declare class AfAvatarComponent {
298
440
  /** Image URL. Falls back to initials when missing or on load error. */
@@ -305,6 +447,12 @@ declare class AfAvatarComponent {
305
447
  alt: _angular_core.InputSignal<string>;
306
448
  /** Online status indicator. */
307
449
  status: _angular_core.InputSignal<AfAvatarStatus | undefined>;
450
+ /**
451
+ * Stable identifier (e.g. userUUID, email, username) hashed into a
452
+ * deterministic palette index. The same seed always produces the same
453
+ * color. Leave empty to keep the default avatar background.
454
+ */
455
+ colorSeed: _angular_core.InputSignal<string>;
308
456
  /** Tracks whether the image failed to load. */
309
457
  imageError: _angular_core.WritableSignal<boolean>;
310
458
  /** Whether to render the `<img>` element. */
@@ -313,26 +461,60 @@ declare class AfAvatarComponent {
313
461
  initials: _angular_core.Signal<string>;
314
462
  /** Accessible label for the avatar. */
315
463
  ariaLabel: _angular_core.Signal<string>;
464
+ /**
465
+ * Palette index in `[1, AVATAR_SEED_PALETTE_SIZE]` derived from `colorSeed`,
466
+ * or `null` when no seed is set. Returning `null` causes Angular to omit
467
+ * the `data-seed-color` attribute, preserving the unseeded default.
468
+ */
469
+ seedColorIndex: _angular_core.Signal<number | null>;
316
470
  avatarClasses: _angular_core.Signal<string>;
317
471
  /** Handles image load failure by switching to initials fallback. */
318
472
  onImageError(): void;
473
+ /**
474
+ * Dependency-free 32-bit string hash (djb2-style). Pure and stable across
475
+ * runs and environments — same input always yields the same non-negative
476
+ * integer.
477
+ */
478
+ private hashSeed;
319
479
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfAvatarComponent, never>;
320
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfAvatarComponent, "af-avatar", never, { "src": { "alias": "src"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
480
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfAvatarComponent, "af-avatar", never, { "src": { "alias": "src"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "colorSeed": { "alias": "colorSeed"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
321
481
  }
322
482
 
323
483
  type AfButtonVariant = 'primary' | 'secondary' | 'ghost' | 'outline' | 'danger' | 'accent' | 'link';
324
484
  type AfButtonSize = 'sm' | 'md' | 'lg';
325
485
  type AfButtonType = 'button' | 'submit' | 'reset';
326
486
  /**
327
- * Button component from the Construct Design System
487
+ * Button component from the Construct Design System.
328
488
  *
329
- * @example
330
- * <af-button variant="primary" (clicked)="handleClick()">Click me</af-button>
489
+ * Wraps a native `<button>` element with design system tokens and
490
+ * variant/size modifiers.
491
+ *
492
+ * @example Basic usage
493
+ * <af-button variant="primary" (clicked)="save()">Save</af-button>
331
494
  *
332
- * @example Icon-only button
495
+ * @example Variants
496
+ * <af-button variant="secondary">Secondary</af-button>
497
+ * <af-button variant="ghost">Ghost</af-button>
498
+ * <af-button variant="outline">Outline</af-button>
499
+ * <af-button variant="danger">Danger</af-button>
500
+ * <af-button variant="accent">Accent</af-button>
501
+ * <af-button variant="link">Link</af-button>
502
+ *
503
+ * @example Icon-only button (ariaLabel required)
333
504
  * <af-button variant="ghost" size="sm" iconOnly ariaLabel="Delete item">
334
505
  * <af-icon name="delete" />
335
506
  * </af-button>
507
+ *
508
+ * @example Disabled button
509
+ * <af-button [disabled]="true">Cannot click</af-button>
510
+ *
511
+ * @accessibility
512
+ * - Uses native `<button>` element — keyboard (Enter/Space) and screen reader support built-in.
513
+ * - Disabled state uses the native `disabled` attribute.
514
+ * - Icon-only buttons must provide `ariaLabel` for screen reader users.
515
+ * A dev-mode warning is emitted if `ariaLabel` is missing on an icon-only button.
516
+ * - Focus indicator: 2px outline via `:focus-visible` (design system CSS).
517
+ * - Reduced motion: `transform` animation disabled via `prefers-reduced-motion`.
336
518
  */
337
519
  declare class AfButtonComponent {
338
520
  /** Button variant/style */
@@ -352,14 +534,53 @@ declare class AfButtonComponent {
352
534
  /** Click event emitter */
353
535
  clicked: _angular_core.OutputEmitterRef<MouseEvent>;
354
536
  buttonClasses: _angular_core.Signal<string>;
537
+ private readonly iconOnlyWarning;
355
538
  handleClick(event: MouseEvent): void;
356
539
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfButtonComponent, never>;
357
540
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfButtonComponent, "af-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "iconOnly": { "alias": "iconOnly"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; }, { "clicked": "clicked"; }, never, ["*"], true, never>;
358
541
  }
359
542
 
543
+ /**
544
+ * Test harness for AfButtonComponent.
545
+ *
546
+ * Provides a semantic API for interacting with the button in tests,
547
+ * abstracting DOM details behind readable method names.
548
+ *
549
+ * @example
550
+ * const harness = new AfButtonHarness(fixture.nativeElement);
551
+ * expect(harness.getText()).toBe('Save');
552
+ * expect(harness.isDisabled()).toBe(false);
553
+ * harness.click();
554
+ */
555
+ declare class AfButtonHarness {
556
+ private readonly hostEl;
557
+ constructor(container: HTMLElement);
558
+ /** Returns the inner native `<button>` element. */
559
+ getButtonElement(): HTMLButtonElement;
560
+ /** Returns the trimmed text content of the button. */
561
+ getText(): string;
562
+ /** Returns whether the button is disabled. */
563
+ isDisabled(): boolean;
564
+ /** Clicks the inner button element. */
565
+ click(): void;
566
+ /** Returns the `aria-label` attribute value, or `null` if absent. */
567
+ getAriaLabel(): string | null;
568
+ /** Returns the `title` attribute value, or `null` if absent. */
569
+ getTitle(): string | null;
570
+ /** Returns the `type` attribute of the button. */
571
+ getType(): string;
572
+ /** Returns the full `class` attribute string of the inner button. */
573
+ getClasses(): string;
574
+ /** Returns whether the inner button has the given CSS class. */
575
+ hasClass(className: string): boolean;
576
+ }
577
+
360
578
  type AfInputType = 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url';
361
579
  /**
362
- * Input field component with form control support
580
+ * Input field component with form control support.
581
+ *
582
+ * Wraps a native `<input>` element with label, hint, error, and icon slots.
583
+ * Implements `ControlValueAccessor` for seamless `ngModel` and `formControl` integration.
363
584
  *
364
585
  * @example
365
586
  * <af-input
@@ -368,107 +589,304 @@ type AfInputType = 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' |
368
589
  * placeholder="name@company.com"
369
590
  * [(ngModel)]="email"
370
591
  * hint="We will not share this."
371
- * ></af-input>
592
+ * />
372
593
  *
373
594
  * @example
374
595
  * <af-input
375
596
  * label="Name"
376
597
  * [error]="nameError"
377
598
  * required
378
- * ></af-input>
599
+ * />
379
600
  */
380
601
  declare class AfInputComponent implements ControlValueAccessor {
381
602
  private static nextId;
382
- /** Input label */
603
+ readonly i18n: _neuravision_ng_construct.AfInputI18n;
604
+ /** Input label. */
383
605
  label: _angular_core.InputSignal<string>;
384
- /** Input type */
606
+ /** Input type. */
385
607
  type: _angular_core.InputSignal<AfInputType>;
386
- /** Placeholder text */
608
+ /** Placeholder text. */
387
609
  placeholder: _angular_core.InputSignal<string>;
388
- /** Hint text shown below input */
610
+ /** Hint text shown below input. */
389
611
  hint: _angular_core.InputSignal<string>;
390
- /** Error message - shows error state and message */
612
+ /** Error message shows error state and message. */
391
613
  error: _angular_core.InputSignal<string>;
392
- /** Whether input is required */
393
- required: _angular_core.InputSignal<boolean>;
394
- /** Whether input is disabled */
614
+ /** Whether input is required. */
615
+ required: _angular_core.InputSignalWithTransform<boolean, unknown>;
616
+ /** Whether input is disabled. */
395
617
  disabled: _angular_core.ModelSignal<boolean>;
396
- /** Icon position (if icon content is projected) */
618
+ /** Icon position (if icon content is projected). */
397
619
  iconPosition: _angular_core.InputSignal<"left" | "right" | null>;
398
- /** Unique input ID */
620
+ /** Unique input ID. */
399
621
  inputId: _angular_core.InputSignal<string>;
400
- value: string;
622
+ /** @docs-private — internal form value managed by CVA. */
623
+ readonly value: _angular_core.WritableSignal<string>;
401
624
  onChange: (value: string) => void;
402
625
  onTouched: () => void;
626
+ /** Computed hint element ID. */
403
627
  hintId: _angular_core.Signal<string>;
628
+ /** Computed error element ID. */
404
629
  errorId: _angular_core.Signal<string>;
630
+ /** Computed CSS classes for the inner input. */
405
631
  inputClasses: _angular_core.Signal<string>;
406
- getAriaDescribedBy(): string | null;
632
+ /** Computed `aria-describedby` value linking to hint or error. */
633
+ ariaDescribedBy: _angular_core.Signal<string | null>;
407
634
  onInput(event: Event): void;
408
- /** ControlValueAccessor implementation */
635
+ /** @docs-private */
409
636
  writeValue(value: string): void;
637
+ /** @docs-private */
410
638
  registerOnChange(fn: (value: string) => void): void;
639
+ /** @docs-private */
411
640
  registerOnTouched(fn: () => void): void;
641
+ /** @docs-private */
412
642
  setDisabledState(isDisabled: boolean): void;
413
643
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfInputComponent, never>;
414
644
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfInputComponent, "af-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "iconPosition": { "alias": "iconPosition"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; }, never, ["[icon]", "[icon]"], true, never>;
415
645
  }
416
646
 
647
+ /**
648
+ * Test harness for AfInputComponent.
649
+ *
650
+ * Provides a semantic API for interacting with the input in tests,
651
+ * abstracting DOM details behind readable method names.
652
+ *
653
+ * @example
654
+ * const harness = new AfInputHarness(fixture.nativeElement);
655
+ * expect(harness.getValue()).toBe('');
656
+ * harness.setValue('hello');
657
+ * expect(harness.getValue()).toBe('hello');
658
+ */
659
+ declare class AfInputHarness {
660
+ private readonly hostEl;
661
+ constructor(container: HTMLElement);
662
+ /** Returns the inner native `<input>` element. */
663
+ getInputElement(): HTMLInputElement;
664
+ /** Returns the current value of the input. */
665
+ getValue(): string;
666
+ /** Sets the input value and dispatches an `input` event. */
667
+ setValue(value: string): void;
668
+ /** Returns the label text, or `null` if no label is rendered. */
669
+ getLabel(): string | null;
670
+ /** Returns the hint text, or `null` if no hint is rendered. */
671
+ getHint(): string | null;
672
+ /** Returns the error text, or `null` if no error is rendered. */
673
+ getError(): string | null;
674
+ /** Returns whether the input is disabled. */
675
+ isDisabled(): boolean;
676
+ /** Returns whether the input is required. */
677
+ isRequired(): boolean;
678
+ /** Returns whether `aria-invalid` is set to `"true"`. */
679
+ isInvalid(): boolean;
680
+ /** Returns the `type` attribute of the input. */
681
+ getType(): string;
682
+ /** Returns the `placeholder` attribute of the input. */
683
+ getPlaceholder(): string;
684
+ /** Returns the `aria-describedby` attribute value, or `null` if absent. */
685
+ getAriaDescribedBy(): string | null;
686
+ /** Focuses the input element. */
687
+ focus(): void;
688
+ /** Blurs the input element and dispatches a `blur` event. */
689
+ blur(): void;
690
+ /** Returns the `id` attribute of the input. */
691
+ getId(): string;
692
+ /** Returns whether the field wrapper has the error modifier class. */
693
+ hasFieldError(): boolean;
694
+ /** Returns whether an icon wrapper is rendered. */
695
+ hasIcon(): boolean;
696
+ /** Returns the full `class` attribute string of the inner input. */
697
+ getClasses(): string;
698
+ /** Returns whether the inner input has the given CSS class. */
699
+ hasClass(className: string): boolean;
700
+ }
701
+
702
+ /** Translatable strings used by the input component. */
703
+ interface AfInputI18n {
704
+ /** Screen-reader label for the required indicator. */
705
+ required: string;
706
+ }
707
+ /**
708
+ * Injection token to override input screen-reader announcements.
709
+ *
710
+ * @example
711
+ * providers: [{
712
+ * provide: AF_INPUT_I18N,
713
+ * useValue: { required: 'Pflichtfeld' },
714
+ * }]
715
+ */
716
+ declare const AF_INPUT_I18N: InjectionToken<AfInputI18n>;
717
+
417
718
  interface AfSelectOption {
418
719
  value: unknown;
419
720
  label: string;
420
721
  disabled?: boolean;
421
722
  }
422
723
  /**
423
- * Select dropdown component with form control support
724
+ * Native select dropdown component with form control support.
725
+ * Wraps a native `<select>` element with design system styling,
726
+ * accessible labelling, and Angular forms integration.
424
727
  *
425
- * @example
728
+ * For a custom dropdown with keyboard-navigated listbox, see `af-select-menu`.
729
+ *
730
+ * @example Basic usage with ngModel
426
731
  * <af-select
427
732
  * label="Role"
428
733
  * [options]="roleOptions"
429
734
  * [(ngModel)]="selectedRole"
430
735
  * hint="Choose your primary role"
431
- * ></af-select>
736
+ * />
737
+ *
738
+ * @example Reactive forms with error state
739
+ * <af-select
740
+ * label="Country"
741
+ * [options]="countries"
742
+ * [formControl]="countryControl"
743
+ * [error]="countryControl.hasError('required') ? 'Required field' : ''"
744
+ * />
745
+ *
746
+ * @accessibility
747
+ * - Uses a native `<select>` element for built-in browser accessibility.
748
+ * - `aria-invalid` is set when an error message is provided.
749
+ * - `aria-describedby` links to hint or error text.
750
+ * - Falls back to `aria-label` via {@link AF_SELECT_I18N} when no `label` input is given.
751
+ * - Screen-reader announcements via {@link AriaLiveAnnouncer} on selection change.
752
+ * - All user-facing strings are configurable via {@link AF_SELECT_I18N} for i18n.
432
753
  */
433
754
  declare class AfSelectComponent implements ControlValueAccessor {
434
755
  private static nextId;
435
- /** Select label */
756
+ protected readonly i18n: _neuravision_ng_construct.AfSelectI18n;
757
+ private readonly announcer;
758
+ /** Label shown above the select. */
436
759
  label: _angular_core.InputSignal<string>;
437
- /** Placeholder option */
760
+ /** Placeholder option shown when no value is selected. */
438
761
  placeholder: _angular_core.InputSignal<string>;
439
- /** Options array */
762
+ /** Available options. */
440
763
  options: _angular_core.InputSignal<AfSelectOption[]>;
441
- /** Hint text shown below select */
764
+ /** Hint text shown below the select. */
442
765
  hint: _angular_core.InputSignal<string>;
443
- /** Error message */
766
+ /** Error message — shows error state when non-empty. */
444
767
  error: _angular_core.InputSignal<string>;
445
- /** Whether select is required */
768
+ /** Whether the field is required. */
446
769
  required: _angular_core.InputSignal<boolean>;
447
- /** Whether select is disabled */
770
+ /** Whether the select is disabled. */
448
771
  disabled: _angular_core.ModelSignal<boolean>;
449
- /** Value comparison function (for object values) */
772
+ /** Size variant. */
773
+ size: _angular_core.InputSignal<"sm" | "md" | "lg">;
774
+ /** Value comparison function for object values. */
450
775
  compareWith: _angular_core.InputSignal<(a: unknown, b: unknown) => boolean>;
451
- /** Unique select ID */
776
+ /** Unique select ID. */
452
777
  selectId: _angular_core.InputSignal<string>;
453
- value: unknown;
454
- onChangeCallback: (value: unknown) => void;
778
+ /** Emits when the user changes the selected value. */
779
+ valueChange: _angular_core.OutputEmitterRef<unknown>;
780
+ private readonly value;
781
+ private onChange;
455
782
  onTouched: () => void;
783
+ labelId: _angular_core.Signal<string>;
456
784
  hintId: _angular_core.Signal<string>;
457
785
  errorId: _angular_core.Signal<string>;
458
- getAriaDescribedBy(): string | null;
459
- get isPlaceholderSelected(): boolean;
460
- private hasMatchingOption;
786
+ selectClasses: _angular_core.Signal<string>;
787
+ ariaDescribedBy: _angular_core.Signal<string | null>;
788
+ isPlaceholderSelected: _angular_core.Signal<boolean>;
461
789
  isOptionSelected(option: AfSelectOption): boolean;
462
- onChange(event: Event): void;
463
- /** ControlValueAccessor implementation */
790
+ handleChange(event: Event): void;
791
+ /** @docs-private */
464
792
  writeValue(value: unknown): void;
793
+ /** @docs-private */
465
794
  registerOnChange(fn: (value: unknown) => void): void;
795
+ /** @docs-private */
466
796
  registerOnTouched(fn: () => void): void;
797
+ /** @docs-private */
467
798
  setDisabledState(isDisabled: boolean): void;
799
+ private hasMatchingOption;
468
800
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfSelectComponent, never>;
469
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfSelectComponent, "af-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "selectId": { "alias": "selectId"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; }, never, never, true, never>;
801
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfSelectComponent, "af-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "selectId": { "alias": "selectId"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; "valueChange": "valueChange"; }, never, never, true, never>;
802
+ }
803
+
804
+ /**
805
+ * Test harness for AfSelectComponent.
806
+ *
807
+ * Provides a semantic API for interacting with the native select in tests,
808
+ * abstracting DOM details behind readable method names.
809
+ *
810
+ * @example
811
+ * const harness = new AfSelectHarness(fixture.nativeElement);
812
+ * expect(harness.isDisabled()).toBe(false);
813
+ * harness.selectByIndex(1);
814
+ * expect(harness.getValue()).toBe('Banana');
815
+ */
816
+ declare class AfSelectHarness {
817
+ private readonly hostEl;
818
+ constructor(container: HTMLElement);
819
+ /** Returns the native `<select>` element. */
820
+ getSelectElement(): HTMLSelectElement;
821
+ /** Returns the current display value of the select. */
822
+ getValue(): string;
823
+ /** Returns the current selected index. */
824
+ getSelectedIndex(): number;
825
+ /** Selects an option by index and dispatches a change event. */
826
+ selectByIndex(index: number): void;
827
+ /** Returns the trimmed label text, or null if no label. */
828
+ getLabel(): string | null;
829
+ /** Returns the trimmed hint text, or null if no hint. */
830
+ getHint(): string | null;
831
+ /** Returns the trimmed error text, or null if no error. */
832
+ getError(): string | null;
833
+ /** Returns whether the select is disabled. */
834
+ isDisabled(): boolean;
835
+ /** Returns whether the select is required. */
836
+ isRequired(): boolean;
837
+ /** Returns whether `aria-invalid="true"` is set. */
838
+ isInvalid(): boolean;
839
+ /** Returns the `aria-describedby` attribute value. */
840
+ getAriaDescribedBy(): string | null;
841
+ /** Returns the `aria-label` attribute value. */
842
+ getAriaLabel(): string | null;
843
+ /** Returns all `<option>` elements. */
844
+ getOptions(): HTMLOptionElement[];
845
+ /** Returns the number of options (including placeholder). */
846
+ getOptionCount(): number;
847
+ /** Returns the trimmed text of the option at the given index. */
848
+ getOptionText(index: number): string;
849
+ /** Returns whether the option at the given index is disabled. */
850
+ isOptionDisabled(index: number): boolean;
851
+ /** Returns whether the option at the given index is selected. */
852
+ isOptionSelected(index: number): boolean;
853
+ /** Returns the select element's ID. */
854
+ getId(): string;
855
+ /** Returns whether the field wrapper has the error class. */
856
+ hasFieldError(): boolean;
857
+ /** Returns whether the select has the given CSS class. */
858
+ hasClass(className: string): boolean;
859
+ /** Returns whether the `.ct-select-wrap` wrapper exists. */
860
+ hasSelectWrap(): boolean;
861
+ /** Dispatches a blur event on the select. */
862
+ blur(): void;
470
863
  }
471
864
 
865
+ /** Translatable strings used by the select component. */
866
+ interface AfSelectI18n {
867
+ /** Screen-reader label for the required asterisk. */
868
+ required: string;
869
+ /** Fallback `aria-label` when no `label` input is provided. */
870
+ selectOption: string;
871
+ /** Announcement when an option is selected. Use `{label}` as placeholder. */
872
+ selected: string;
873
+ }
874
+ /**
875
+ * Injection token to override select screen-reader announcements
876
+ * and the fallback `aria-label`.
877
+ *
878
+ * @example
879
+ * providers: [{
880
+ * provide: AF_SELECT_I18N,
881
+ * useValue: {
882
+ * required: 'Pflichtfeld',
883
+ * selectOption: 'Option auswählen',
884
+ * selected: '{label} ausgewählt',
885
+ * },
886
+ * }]
887
+ */
888
+ declare const AF_SELECT_I18N: InjectionToken<AfSelectI18n>;
889
+
472
890
  /**
473
891
  * Textarea component with form control support
474
892
  *
@@ -1149,77 +1567,226 @@ declare class AfBreadcrumbsComponent {
1149
1567
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfBreadcrumbsComponent, "af-breadcrumbs", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1150
1568
  }
1151
1569
 
1570
+ /** Supported calendar views */
1571
+ type AfDatepickerView = 'days' | 'months' | 'years';
1572
+ /** Selection mode */
1573
+ type AfDatepickerMode = 'single' | 'range';
1574
+ /** Value format emitted by ControlValueAccessor */
1575
+ type AfDatepickerValueFormat = 'date' | 'iso';
1576
+ /** Date range value for range mode */
1577
+ interface AfDateRange {
1578
+ start: Date | null;
1579
+ end: Date | null;
1580
+ }
1152
1581
  interface CalendarDay {
1153
1582
  date: Date;
1154
1583
  isCurrentMonth: boolean;
1155
1584
  isToday: boolean;
1156
1585
  isSelected: boolean;
1586
+ isDisabled: boolean;
1587
+ isUnavailable: boolean;
1588
+ isInRange: boolean;
1589
+ isRangeStart: boolean;
1590
+ isRangeEnd: boolean;
1591
+ }
1592
+ interface MonthItem {
1593
+ index: number;
1594
+ label: string;
1595
+ shortLabel: string;
1596
+ isSelected: boolean;
1597
+ isDisabled: boolean;
1598
+ }
1599
+ interface YearItem {
1600
+ value: number;
1601
+ isSelected: boolean;
1602
+ isDisabled: boolean;
1157
1603
  }
1158
1604
  /**
1159
- * Datepicker component with calendar popup
1605
+ * Datepicker component with calendar popup, month/year views, range selection,
1606
+ * min/max constraints, disabled dates, and full keyboard navigation.
1607
+ *
1608
+ * Implements WAI-ARIA Date Picker Dialog pattern with roving tabindex.
1160
1609
  *
1161
1610
  * @example
1162
1611
  * <af-datepicker
1163
- * label="Select date"
1612
+ * label="Start date"
1164
1613
  * placeholder="Pick a date"
1165
- * [(ngModel)]="selectedDate">
1166
- * </af-datepicker>
1614
+ * [(ngModel)]="selectedDate"
1615
+ * [min]="minDate"
1616
+ * [max]="maxDate"
1617
+ * hint="Choose a date within the project timeline"
1618
+ * />
1619
+ *
1620
+ * @example
1621
+ * <af-datepicker
1622
+ * label="Period"
1623
+ * mode="range"
1624
+ * [(ngModel)]="dateRange"
1625
+ * valueFormat="iso"
1626
+ * />
1167
1627
  */
1168
- declare class AfDatepickerComponent implements ControlValueAccessor {
1628
+ declare class AfDatepickerComponent implements ControlValueAccessor, Validator {
1169
1629
  private static nextId;
1170
- /** Input label */
1630
+ /** Field label */
1171
1631
  label: _angular_core.InputSignal<string>;
1172
- /** Placeholder text */
1632
+ /** Input placeholder text */
1173
1633
  placeholder: _angular_core.InputSignal<string>;
1174
- /** Whether datepicker is disabled */
1634
+ /** Whether the datepicker is disabled */
1175
1635
  disabled: _angular_core.ModelSignal<boolean>;
1176
- /** Date format for display */
1636
+ /** Display format for the selected date */
1177
1637
  dateFormat: _angular_core.InputSignal<string>;
1178
- /** Unique input ID */
1638
+ /** Unique ID for the input element */
1179
1639
  inputId: _angular_core.InputSignal<string>;
1180
- /** Selected date change event */
1640
+ /** Minimum selectable date */
1641
+ min: _angular_core.InputSignal<string | Date | null>;
1642
+ /** Maximum selectable date */
1643
+ max: _angular_core.InputSignal<string | Date | null>;
1644
+ /** Array of specific dates that cannot be selected */
1645
+ disabledDates: _angular_core.InputSignal<Date[]>;
1646
+ /**
1647
+ * Predicate function to determine if a date is selectable.
1648
+ * Return `true` to allow selection, `false` to disable.
1649
+ */
1650
+ dateFilter: _angular_core.InputSignal<((date: Date) => boolean) | null>;
1651
+ /** Hint text displayed below the input */
1652
+ hint: _angular_core.InputSignal<string>;
1653
+ /** Error message — displays error state and message */
1654
+ error: _angular_core.InputSignal<string>;
1655
+ /** Whether the field is required */
1656
+ required: _angular_core.InputSignal<boolean>;
1657
+ /** Selection mode: single date or date range */
1658
+ mode: _angular_core.InputSignal<AfDatepickerMode>;
1659
+ /**
1660
+ * Value format for ControlValueAccessor.
1661
+ * `'date'` emits `Date` objects, `'iso'` emits ISO date strings (`yyyy-MM-dd`).
1662
+ */
1663
+ valueFormat: _angular_core.InputSignal<AfDatepickerValueFormat>;
1664
+ /** Emitted when a single date is selected */
1181
1665
  dateChange: _angular_core.OutputEmitterRef<Date>;
1666
+ /** Emitted when a date range is selected (range mode only) */
1667
+ rangeChange: _angular_core.OutputEmitterRef<AfDateRange>;
1182
1668
  inputRef: _angular_core.Signal<ElementRef<HTMLInputElement> | undefined>;
1183
1669
  popoverRef: _angular_core.Signal<ElementRef<HTMLDivElement> | undefined>;
1184
1670
  weekdayLabels: string[];
1185
- monthNames: string[];
1671
+ weekdayFullLabels: string[];
1186
1672
  selectedDate: _angular_core.WritableSignal<Date | null>;
1673
+ rangeStart: _angular_core.WritableSignal<Date | null>;
1674
+ rangeEnd: _angular_core.WritableSignal<Date | null>;
1675
+ rangeSelecting: _angular_core.WritableSignal<boolean>;
1187
1676
  currentMonth: _angular_core.WritableSignal<number>;
1188
1677
  currentYear: _angular_core.WritableSignal<number>;
1189
1678
  isOpen: _angular_core.WritableSignal<boolean>;
1190
1679
  focusedDate: _angular_core.WritableSignal<Date | null>;
1191
- onChange: (value: Date | null) => void;
1680
+ currentView: _angular_core.WritableSignal<AfDatepickerView>;
1681
+ focusedMonth: _angular_core.WritableSignal<number>;
1682
+ focusedYear: _angular_core.WritableSignal<number>;
1683
+ yearPageStart: _angular_core.WritableSignal<number>;
1684
+ onChange: (value: unknown) => void;
1192
1685
  onTouched: () => void;
1193
- calendarDays: _angular_core.Signal<CalendarDay[]>;
1194
- formattedDate: _angular_core.Signal<string>;
1686
+ private onValidatorChange;
1687
+ parsedMin: _angular_core.Signal<Date | null>;
1688
+ parsedMax: _angular_core.Signal<Date | null>;
1195
1689
  popoverId: _angular_core.Signal<string>;
1690
+ hintId: _angular_core.Signal<string>;
1691
+ errorId: _angular_core.Signal<string>;
1692
+ ariaDescribedBy: _angular_core.Signal<string | null>;
1693
+ dialogAriaLabel: _angular_core.Signal<"Choose date range" | "Choose date">;
1694
+ headerTitle: _angular_core.Signal<string>;
1695
+ titleAriaLabel: _angular_core.Signal<"Switch to month view" | "Switch to year view" | null>;
1696
+ prevButtonAriaLabel: _angular_core.Signal<"Previous month" | "Previous year" | "Previous 12 years">;
1697
+ nextButtonAriaLabel: _angular_core.Signal<"Next month" | "Next year" | "Next 12 years">;
1698
+ gridAriaLabel: _angular_core.Signal<string>;
1699
+ hasClearableValue: _angular_core.Signal<boolean>;
1700
+ formattedValue: _angular_core.Signal<string>;
1701
+ isTodayDisabled: _angular_core.Signal<boolean>;
1702
+ calendarDays: _angular_core.Signal<CalendarDay[]>;
1703
+ monthItems: _angular_core.Signal<MonthItem[]>;
1704
+ yearItems: _angular_core.Signal<YearItem[]>;
1196
1705
  toggle(): void;
1197
- selectDate(date: Date): void;
1198
- previousMonth(): void;
1199
- nextMonth(): void;
1200
- private generateCalendarDays;
1706
+ /** Opens the calendar popover */
1707
+ open(): void;
1708
+ /** Closes the calendar popover */
1709
+ close(returnFocus?: boolean): void;
1710
+ /** Handles click on a calendar day */
1711
+ onDayClick(day: CalendarDay): void;
1712
+ /** Selects a single date and closes the popover */
1713
+ selectSingleDate(date: Date): void;
1714
+ /** Handles range date selection (two-click: start then end) */
1715
+ selectRangeDate(date: Date): void;
1716
+ /** Navigates to today and selects it (single mode) or focuses it */
1717
+ goToToday(): void;
1718
+ /** Clears the selected value */
1719
+ clearValue(event: Event): void;
1720
+ /** Drills up: days -> months -> years */
1721
+ drillUp(): void;
1722
+ /** Navigates to previous period based on current view */
1723
+ navigatePrevious(): void;
1724
+ /** Navigates to next period based on current view */
1725
+ navigateNext(): void;
1726
+ /** Selects a month from the month view and switches to days */
1727
+ selectMonth(monthIndex: number): void;
1728
+ /** Selects a year from the year view and switches to months */
1729
+ selectYear(year: number): void;
1730
+ /** Returns true if the given day matches the keyboard-focused date */
1731
+ isDayHighlighted(day: CalendarDay): boolean;
1732
+ /** Returns the tabindex for a day button (roving tabindex pattern) */
1201
1733
  getDayTabIndex(day: CalendarDay): number;
1734
+ /** Returns true if the given month index matches the keyboard-focused month */
1735
+ isMonthHighlighted(monthIndex: number): boolean;
1736
+ /** Returns the tabindex for a month button */
1737
+ getMonthTabIndex(monthIndex: number): number;
1738
+ /** Returns true if the given year matches the keyboard-focused year */
1739
+ isYearHighlighted(year: number): boolean;
1740
+ /** Returns the tabindex for a year button */
1741
+ getYearTabIndex(year: number): number;
1742
+ /** Returns a date key string for DOM identification */
1202
1743
  getDateKey(date: Date): string;
1203
1744
  onInputKeydown(event: KeyboardEvent): void;
1204
- onGridKeydown(event: KeyboardEvent): void;
1205
- onEscape(): void;
1745
+ /** Keyboard navigation within the day grid */
1746
+ onDayGridKeydown(event: KeyboardEvent): void;
1747
+ /** Keyboard navigation within the month grid */
1748
+ onMonthGridKeydown(event: KeyboardEvent): void;
1749
+ /** Keyboard navigation within the year grid */
1750
+ onYearGridKeydown(event: KeyboardEvent): void;
1206
1751
  onDocumentClick(event: MouseEvent): void;
1207
- private open;
1208
- private close;
1752
+ writeValue(value: unknown): void;
1753
+ registerOnChange(fn: (value: unknown) => void): void;
1754
+ registerOnTouched(fn: () => void): void;
1755
+ setDisabledState(isDisabled: boolean): void;
1756
+ validate(control: AbstractControl): ValidationErrors | null;
1757
+ registerOnValidatorChange(fn: () => void): void;
1758
+ private writeSingleValue;
1759
+ private writeRangeValue;
1760
+ private emitSingleValue;
1761
+ private emitRangeValue;
1762
+ private validateSingle;
1763
+ private validateRange;
1764
+ private buildDay;
1765
+ /** Checks whether a date falls outside min/max bounds */
1766
+ isDateDisabled(date: Date): boolean;
1767
+ /** Checks whether a date is explicitly unavailable (disabledDates or dateFilter) */
1768
+ private isDateUnavailable;
1769
+ private isMonthDisabled;
1770
+ private isYearDisabled;
1209
1771
  private setFocusedDate;
1210
1772
  private focusDayButton;
1773
+ private focusMonthButton;
1774
+ private focusYearButton;
1775
+ /** Finds the next non-disabled date in the given direction */
1776
+ private findNextEnabledDate;
1777
+ private shiftMonth;
1211
1778
  private addDays;
1212
1779
  private addMonths;
1780
+ private addYears;
1213
1781
  private getWeekdayIndex;
1214
- private shiftMonth;
1215
1782
  private isSameDay;
1783
+ private compareDays;
1784
+ private parseDate;
1785
+ private coerceToDate;
1786
+ private toIsoString;
1216
1787
  private formatDate;
1217
- writeValue(value: Date | null): void;
1218
- registerOnChange(fn: (value: Date | null) => void): void;
1219
- registerOnTouched(fn: () => void): void;
1220
- setDisabledState(isDisabled: boolean): void;
1221
1788
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfDatepickerComponent, never>;
1222
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfDatepickerComponent, "af-datepicker", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "dateFormat": { "alias": "dateFormat"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; "dateChange": "dateChange"; }, never, never, true, never>;
1789
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfDatepickerComponent, "af-datepicker", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "dateFormat": { "alias": "dateFormat"; "required": false; "isSignal": true; }; "inputId": { "alias": "inputId"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "disabledDates": { "alias": "disabledDates"; "required": false; "isSignal": true; }; "dateFilter": { "alias": "dateFilter"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "valueFormat": { "alias": "valueFormat"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; "dateChange": "dateChange"; "rangeChange": "rangeChange"; }, never, never, true, never>;
1223
1790
  }
1224
1791
 
1225
1792
  type AfChipVariant = 'default' | 'info' | 'success' | 'warning' | 'danger';
@@ -1394,24 +1961,80 @@ declare class AfTooltipDirective implements OnDestroy {
1394
1961
 
1395
1962
  type AfBadgeVariant = 'default' | 'info' | 'success' | 'warning' | 'danger';
1396
1963
  /**
1397
- * Badge component for status indicators
1964
+ * Badge component for status indicators, labels, and counts.
1398
1965
  *
1399
- * @example
1400
- * <af-badge variant="success" icon="+">Approved</af-badge>
1401
- * <af-badge variant="danger">Blocked</af-badge>
1966
+ * Wraps the Construct Design System `ct-badge` with semantic color
1967
+ * variants and optional icon or dot indicators.
1968
+ *
1969
+ * @example Basic usage
1970
+ * <af-badge variant="success">Approved</af-badge>
1971
+ *
1972
+ * @example With icon
1973
+ * <af-badge variant="danger" icon="+">Blocked</af-badge>
1974
+ *
1975
+ * @example With dot indicator
1976
+ * <af-badge variant="info" dot>Online</af-badge>
1977
+ *
1978
+ * @example Status badge for screen readers
1979
+ * <af-badge variant="warning" role="status" ariaLabel="Build status: failing">
1980
+ * Failing
1981
+ * </af-badge>
1982
+ *
1983
+ * @accessibility
1984
+ * - Non-interactive element — no keyboard navigation required.
1985
+ * - Decorative elements (icon, dot) are hidden from screen readers via `aria-hidden`.
1986
+ * - Use `ariaLabel` when the badge has no visible text or when the visual content
1987
+ * alone does not convey the full meaning.
1988
+ * - Set `role="status"` when the badge reflects a live value that screen readers
1989
+ * should announce on change.
1402
1990
  */
1403
1991
  declare class AfBadgeComponent {
1992
+ /** ARIA role, e.g. `"status"` for live status badges. */
1993
+ role: _angular_core.InputSignal<string>;
1404
1994
  /** Accessible label, useful when the badge has no visible text. */
1405
1995
  ariaLabel: _angular_core.InputSignal<string>;
1406
- /** Color variant. */
1996
+ /** Semantic color variant. */
1407
1997
  variant: _angular_core.InputSignal<AfBadgeVariant>;
1408
- /** Icon character to display */
1998
+ /** Icon character to display before the label. */
1409
1999
  icon: _angular_core.InputSignal<string>;
1410
- /** Show a dot indicator instead of icon */
1411
- dot: _angular_core.InputSignal<boolean>;
2000
+ /** Show a dot indicator instead of an icon. */
2001
+ dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
2002
+ /** Computed CSS classes combining base class and variant/icon modifiers. */
1412
2003
  badgeClasses: _angular_core.Signal<string>;
1413
2004
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfBadgeComponent, never>;
1414
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfBadgeComponent, "af-badge", never, { "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
2005
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfBadgeComponent, "af-badge", never, { "role": { "alias": "role"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
2006
+ }
2007
+
2008
+ /**
2009
+ * Test harness for AfBadgeComponent.
2010
+ *
2011
+ * Provides a semantic API for querying badge state in tests,
2012
+ * abstracting DOM details behind readable method names.
2013
+ *
2014
+ * @example
2015
+ * const harness = new AfBadgeHarness(fixture.nativeElement);
2016
+ * expect(harness.getText()).toBe('Approved');
2017
+ * expect(harness.hasClass('ct-badge--success')).toBe(true);
2018
+ */
2019
+ declare class AfBadgeHarness {
2020
+ private readonly hostEl;
2021
+ constructor(container: HTMLElement);
2022
+ /** Returns the trimmed text content of the badge (projected content only). */
2023
+ getText(): string;
2024
+ /** Returns the full `class` attribute string of the host element. */
2025
+ getClasses(): string;
2026
+ /** Returns whether the host element has the given CSS class. */
2027
+ hasClass(className: string): boolean;
2028
+ /** Returns the `aria-label` attribute value, or `null` if absent. */
2029
+ getAriaLabel(): string | null;
2030
+ /** Returns the `role` attribute value, or `null` if absent. */
2031
+ getRole(): string | null;
2032
+ /** Returns whether the badge contains an icon element. */
2033
+ hasIcon(): boolean;
2034
+ /** Returns whether the badge contains a dot indicator. */
2035
+ hasDot(): boolean;
2036
+ /** Returns the text content of the icon element, or empty string if absent. */
2037
+ getIconText(): string;
1415
2038
  }
1416
2039
 
1417
2040
  type AfProgressBarVariant = 'default' | 'info' | 'success' | 'warning' | 'danger';
@@ -1915,9 +2538,22 @@ interface AfSelectMenuOption {
1915
2538
  * [multiple]="true"
1916
2539
  * [formControl]="rolesControl">
1917
2540
  * </af-select-menu>
2541
+ *
2542
+ * @accessibility
2543
+ * - Implements the WAI-ARIA Listbox pattern with a combobox trigger.
2544
+ * - Keyboard: ArrowDown/Up to move highlight, Enter/Space to select,
2545
+ * Escape to close, Home/End to jump, Tab to select-and-close (single) or close (multi).
2546
+ * - Focus stays on the combobox trigger; `aria-activedescendant` tracks the highlighted option.
2547
+ * - Screen-reader announcements via {@link AriaLiveAnnouncer} for open/close/selection changes.
2548
+ * - All user-facing strings are configurable via {@link AF_SELECT_MENU_I18N} for i18n.
2549
+ * - Uses CSS logical properties for RTL layout support.
2550
+ * - `aria-describedby` links to hint or error text; `aria-invalid` is set on error state.
2551
+ * - Disabled options are marked with `aria-disabled` and skipped during keyboard navigation.
1918
2552
  */
1919
2553
  declare class AfSelectMenuComponent implements ControlValueAccessor {
1920
2554
  private static nextId;
2555
+ protected readonly i18n: _neuravision_ng_construct.AfSelectMenuI18n;
2556
+ private readonly announcer;
1921
2557
  /** Label shown above the select */
1922
2558
  label: _angular_core.InputSignal<string>;
1923
2559
  /** Placeholder text when nothing is selected */
@@ -1983,6 +2619,105 @@ declare class AfSelectMenuComponent implements ControlValueAccessor {
1983
2619
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfSelectMenuComponent, "af-select-menu", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "componentId": { "alias": "componentId"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; }, never, never, true, never>;
1984
2620
  }
1985
2621
 
2622
+ /**
2623
+ * Test harness for AfSelectMenuComponent.
2624
+ *
2625
+ * Provides a semantic API for interacting with the select-menu in tests,
2626
+ * abstracting DOM details behind readable method names.
2627
+ *
2628
+ * @example
2629
+ * const harness = new AfSelectMenuHarness(fixture.nativeElement);
2630
+ * harness.click();
2631
+ * expect(harness.isOpen()).toBe(true);
2632
+ * expect(harness.getOptionCount()).toBe(4);
2633
+ * harness.clickOption(1);
2634
+ * expect(harness.getTriggerText()).toBe('Banana');
2635
+ */
2636
+ declare class AfSelectMenuHarness {
2637
+ private readonly hostEl;
2638
+ constructor(container: HTMLElement);
2639
+ /** Returns the trigger `<button>` element. */
2640
+ getTriggerElement(): HTMLButtonElement;
2641
+ /** Returns the trimmed display text of the trigger. */
2642
+ getTriggerText(): string;
2643
+ /** Clicks the trigger button. */
2644
+ click(): void;
2645
+ /** Returns whether the trigger is disabled. */
2646
+ isDisabled(): boolean;
2647
+ /** Returns whether the listbox is currently open. */
2648
+ isOpen(): boolean;
2649
+ /** Returns the `aria-expanded` attribute value. */
2650
+ getAriaExpanded(): string | null;
2651
+ /** Returns the `aria-label` attribute value. */
2652
+ getAriaLabel(): string | null;
2653
+ /** Returns the `aria-labelledby` attribute value. */
2654
+ getAriaLabelledBy(): string | null;
2655
+ /** Returns the `aria-activedescendant` attribute value. */
2656
+ getAriaActiveDescendant(): string | null;
2657
+ /** Returns the `aria-invalid` attribute value. */
2658
+ getAriaInvalid(): string | null;
2659
+ /** Returns the `aria-required` attribute value. */
2660
+ getAriaRequired(): string | null;
2661
+ /** Returns the `aria-describedby` attribute value. */
2662
+ getAriaDescribedBy(): string | null;
2663
+ /** Returns all option elements inside the listbox. */
2664
+ getOptions(): HTMLElement[];
2665
+ /** Returns the trimmed text of the option at the given index. */
2666
+ getOptionText(index: number): string;
2667
+ /** Returns the number of options. */
2668
+ getOptionCount(): number;
2669
+ /** Returns whether the option at the given index is selected. */
2670
+ isOptionSelected(index: number): boolean;
2671
+ /** Returns whether the option at the given index is disabled. */
2672
+ isOptionDisabled(index: number): boolean;
2673
+ /** Returns whether the option at the given index is highlighted. */
2674
+ isOptionHighlighted(index: number): boolean;
2675
+ /** Clicks the option at the given index. */
2676
+ clickOption(index: number): void;
2677
+ /** Returns the trimmed label text, or empty string if no label. */
2678
+ getLabelText(): string;
2679
+ /** Returns the trimmed hint text, or empty string if no hint. */
2680
+ getHintText(): string;
2681
+ /** Returns the trimmed error text, or empty string if no error. */
2682
+ getErrorText(): string;
2683
+ /** Returns whether the select-menu wrapper has the given CSS class. */
2684
+ hasClass(className: string): boolean;
2685
+ }
2686
+
2687
+ /** Translatable strings used by the select-menu component. */
2688
+ interface AfSelectMenuI18n {
2689
+ /** Fallback `aria-label` when no `label` input is provided. */
2690
+ selectOption: string;
2691
+ /** Announcement when the listbox opens. Use `{count}` as placeholder. */
2692
+ opened: string;
2693
+ /** Announcement when the listbox closes. */
2694
+ closed: string;
2695
+ /** Announcement when an option is selected. Use `{label}` as placeholder. */
2696
+ selected: string;
2697
+ /** Announcement when an option is deselected (multi-select). Use `{label}` as placeholder. */
2698
+ deselected: string;
2699
+ /** Announcement for the number of selected options (multi-select). Use `{count}` as placeholder. */
2700
+ countSelected: string;
2701
+ }
2702
+ /**
2703
+ * Injection token to override select-menu screen-reader announcements
2704
+ * and the fallback `aria-label`.
2705
+ *
2706
+ * @example
2707
+ * providers: [{
2708
+ * provide: AF_SELECT_MENU_I18N,
2709
+ * useValue: {
2710
+ * selectOption: 'Option auswählen',
2711
+ * opened: '{count} Optionen verfügbar',
2712
+ * closed: 'Auswahl geschlossen',
2713
+ * selected: '{label} ausgewählt',
2714
+ * deselected: '{label} abgewählt',
2715
+ * countSelected: '{count} Optionen ausgewählt',
2716
+ * },
2717
+ * }]
2718
+ */
2719
+ declare const AF_SELECT_MENU_I18N: InjectionToken<AfSelectMenuI18n>;
2720
+
1986
2721
  type AfNavbarSize = 'sm' | 'md' | 'lg';
1987
2722
  type AfNavbarVariant = 'default' | 'sticky' | 'fixed' | 'elevated' | 'transparent' | 'dark' | 'bordered';
1988
2723
  /**
@@ -2495,5 +3230,5 @@ declare class AfFormatLabelPipe implements PipeTransform {
2495
3230
  static ɵpipe: _angular_core.ɵɵPipeDeclaration<AfFormatLabelPipe, "afFormatLabel", true>;
2496
3231
  }
2497
3232
 
2498
- export { AfAccordionComponent, AfAccordionItemComponent, AfAlertComponent, AfAppShellComponent, AfAppShellPageHeaderComponent, AfAppShellV2Component, AfAppShellV2ToolbarComponent, AfAvatarComponent, AfBadgeComponent, AfBannerComponent, AfBreadcrumbsComponent, AfButtonComponent, AfCardComponent, AfCellDefDirective, AfCheckboxComponent, AfChipComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfIconComponent, AfInputComponent, AfModalComponent, AfNavItemComponent, AfNavTabsComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfRadioGroupComponent, AfSelectComponent, AfSelectMenuComponent, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective };
2499
- export type { AfAlertVariant, AfAvatarSize, AfAvatarStatus, AfBadgeVariant, AfBannerAppearance, AfBannerPosition, AfBannerVariant, AfBreadcrumb, AfButtonSize, AfButtonType, AfButtonVariant, AfCardElevation, AfCardPadding, AfChipAppearance, AfChipSize, AfChipVariant, AfColumn, AfComboboxOption, AfDataRow, AfDataTableConfig, AfDividerColor, AfDividerOrientation, AfDividerSpacing, AfDrawerPosition, AfDrawerSize, AfDropdownItem, AfEmptyStateSize, AfEmptyStateVariant, AfFileEntry, AfFileValidationError, AfIconSize, AfInputType, AfNavTab, AfNavTabsSize, AfNavTabsVariant, AfNavbarSize, AfNavbarVariant, AfPopoverAlign, AfPopoverPosition, AfPopoverSize, AfProgressBarSize, AfProgressBarVariant, AfSelectMenuOption, AfSelectOption, AfShellPanelState, AfShellSidebarState, AfSidebarMode, AfSkeletonVariant, AfSliderSize, AfSortDirection, AfSortState, AfSpinnerSize, AfTab, AfTableCellType, AfTableVariant, AfToast, AfToastVariant, AfToggleGroupSize, AfToggleItem, AfTooltipPosition };
3233
+ export { AF_ACCORDION_I18N, AF_ALERT_I18N, AF_INPUT_I18N, AF_SELECT_I18N, AF_SELECT_MENU_I18N, AVATAR_SEED_PALETTE_SIZE, AfAccordionComponent, AfAccordionHarness, AfAccordionItemComponent, AfAccordionItemHarness, AfAlertComponent, AfAlertHarness, AfAppShellComponent, AfAppShellPageHeaderComponent, AfAppShellV2Component, AfAppShellV2ToolbarComponent, AfAvatarComponent, AfBadgeComponent, AfBadgeHarness, AfBannerComponent, AfBreadcrumbsComponent, AfButtonComponent, AfButtonHarness, AfCardComponent, AfCellDefDirective, AfCheckboxComponent, AfChipComponent, AfChipInputComponent, AfComboboxComponent, AfDataTableComponent, AfDatepickerComponent, AfDividerComponent, AfDrawerComponent, AfDropdownComponent, AfEmptyStateComponent, AfFieldComponent, AfFileUploadComponent, AfFormatLabelPipe, AfIconComponent, AfInputComponent, AfInputHarness, AfModalComponent, AfNavItemComponent, AfNavTabsComponent, AfNavbarComponent, AfPaginationComponent, AfPopoverComponent, AfPopoverTriggerDirective, AfProgressBarComponent, AfRadioComponent, AfRadioGroupComponent, AfSelectComponent, AfSelectHarness, AfSelectMenuComponent, AfSelectMenuHarness, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective };
3234
+ export type { AfAccordionI18n, AfAlertI18n, AfAlertVariant, AfAvatarSize, AfAvatarStatus, AfBadgeVariant, AfBannerAppearance, AfBannerPosition, AfBannerVariant, AfBreadcrumb, AfButtonSize, AfButtonType, AfButtonVariant, AfCardElevation, AfCardPadding, AfChipAppearance, AfChipSize, AfChipVariant, AfColumn, AfComboboxOption, AfDataRow, AfDataTableConfig, AfDateRange, AfDatepickerMode, AfDatepickerValueFormat, AfDatepickerView, AfDividerColor, AfDividerOrientation, AfDividerSpacing, AfDrawerPosition, AfDrawerSize, AfDropdownItem, AfEmptyStateSize, AfEmptyStateVariant, AfFileEntry, AfFileValidationError, AfIconSize, AfInputI18n, AfInputType, AfNavTab, AfNavTabsSize, AfNavTabsVariant, AfNavbarSize, AfNavbarVariant, AfPopoverAlign, AfPopoverPosition, AfPopoverSize, AfProgressBarSize, AfProgressBarVariant, AfSelectI18n, AfSelectMenuI18n, AfSelectMenuOption, AfSelectOption, AfShellPanelState, AfShellSidebarState, AfSidebarMode, AfSkeletonVariant, AfSliderSize, AfSortDirection, AfSortState, AfSpinnerSize, AfTab, AfTableCellType, AfTableVariant, AfToast, AfToastVariant, AfToggleGroupSize, AfToggleItem, AfTooltipPosition };