@neuravision/ng-construct 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuravision/ng-construct",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Angular components for the Construct Design System",
5
5
  "keywords": [
6
6
  "angular",
@@ -12,7 +12,7 @@
12
12
  "@angular/common": "^21.1.0",
13
13
  "@angular/core": "^21.1.0",
14
14
  "@angular/router": "^21.1.0",
15
- "@neuravision/construct": "^1.1.4",
15
+ "@neuravision/construct": "^1.2.0",
16
16
  "@lucide/angular": "^1.0.0-rc.0"
17
17
  },
18
18
  "dependencies": {
@@ -412,6 +412,12 @@ declare class AfAppShellV2Component {
412
412
 
413
413
  type AfAvatarSize = 'sm' | 'md' | 'lg' | 'xl';
414
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;
415
421
  /**
416
422
  * Avatar component displaying a user image with fallback to initials.
417
423
  *
@@ -419,9 +425,16 @@ type AfAvatarStatus = 'online' | 'offline' | 'busy' | 'away';
419
425
  * fails to load or no `src` is given, initials derived from `name` are
420
426
  * shown instead.
421
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
+ *
422
434
  * @example
423
435
  * <af-avatar src="/photo.jpg" name="Jane Doe" alt="Jane Doe" size="lg" />
424
436
  * <af-avatar name="John Smith" status="online" />
437
+ * <af-avatar name="Jane Doe" colorSeed="user-uuid-7b3e2a4d" />
425
438
  */
426
439
  declare class AfAvatarComponent {
427
440
  /** Image URL. Falls back to initials when missing or on load error. */
@@ -434,6 +447,12 @@ declare class AfAvatarComponent {
434
447
  alt: _angular_core.InputSignal<string>;
435
448
  /** Online status indicator. */
436
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>;
437
456
  /** Tracks whether the image failed to load. */
438
457
  imageError: _angular_core.WritableSignal<boolean>;
439
458
  /** Whether to render the `<img>` element. */
@@ -442,11 +461,23 @@ declare class AfAvatarComponent {
442
461
  initials: _angular_core.Signal<string>;
443
462
  /** Accessible label for the avatar. */
444
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>;
445
470
  avatarClasses: _angular_core.Signal<string>;
446
471
  /** Handles image load failure by switching to initials fallback. */
447
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;
448
479
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfAvatarComponent, never>;
449
- 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>;
450
481
  }
451
482
 
452
483
  type AfButtonVariant = 'primary' | 'secondary' | 'ghost' | 'outline' | 'danger' | 'accent' | 'link';
@@ -690,55 +721,172 @@ interface AfSelectOption {
690
721
  disabled?: boolean;
691
722
  }
692
723
  /**
693
- * 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.
694
727
  *
695
- * @example
728
+ * For a custom dropdown with keyboard-navigated listbox, see `af-select-menu`.
729
+ *
730
+ * @example Basic usage with ngModel
696
731
  * <af-select
697
732
  * label="Role"
698
733
  * [options]="roleOptions"
699
734
  * [(ngModel)]="selectedRole"
700
735
  * hint="Choose your primary role"
701
- * ></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.
702
753
  */
703
754
  declare class AfSelectComponent implements ControlValueAccessor {
704
755
  private static nextId;
705
- /** Select label */
756
+ protected readonly i18n: _neuravision_ng_construct.AfSelectI18n;
757
+ private readonly announcer;
758
+ /** Label shown above the select. */
706
759
  label: _angular_core.InputSignal<string>;
707
- /** Placeholder option */
760
+ /** Placeholder option shown when no value is selected. */
708
761
  placeholder: _angular_core.InputSignal<string>;
709
- /** Options array */
762
+ /** Available options. */
710
763
  options: _angular_core.InputSignal<AfSelectOption[]>;
711
- /** Hint text shown below select */
764
+ /** Hint text shown below the select. */
712
765
  hint: _angular_core.InputSignal<string>;
713
- /** Error message */
766
+ /** Error message — shows error state when non-empty. */
714
767
  error: _angular_core.InputSignal<string>;
715
- /** Whether select is required */
768
+ /** Whether the field is required. */
716
769
  required: _angular_core.InputSignal<boolean>;
717
- /** Whether select is disabled */
770
+ /** Whether the select is disabled. */
718
771
  disabled: _angular_core.ModelSignal<boolean>;
719
- /** Value comparison function (for object values) */
772
+ /** Size variant. */
773
+ size: _angular_core.InputSignal<"sm" | "md" | "lg">;
774
+ /** Value comparison function for object values. */
720
775
  compareWith: _angular_core.InputSignal<(a: unknown, b: unknown) => boolean>;
721
- /** Unique select ID */
776
+ /** Unique select ID. */
722
777
  selectId: _angular_core.InputSignal<string>;
723
- value: unknown;
724
- 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;
725
782
  onTouched: () => void;
783
+ labelId: _angular_core.Signal<string>;
726
784
  hintId: _angular_core.Signal<string>;
727
785
  errorId: _angular_core.Signal<string>;
728
- getAriaDescribedBy(): string | null;
729
- get isPlaceholderSelected(): boolean;
730
- private hasMatchingOption;
786
+ selectClasses: _angular_core.Signal<string>;
787
+ ariaDescribedBy: _angular_core.Signal<string | null>;
788
+ isPlaceholderSelected: _angular_core.Signal<boolean>;
731
789
  isOptionSelected(option: AfSelectOption): boolean;
732
- onChange(event: Event): void;
733
- /** ControlValueAccessor implementation */
790
+ handleChange(event: Event): void;
791
+ /** @docs-private */
734
792
  writeValue(value: unknown): void;
793
+ /** @docs-private */
735
794
  registerOnChange(fn: (value: unknown) => void): void;
795
+ /** @docs-private */
736
796
  registerOnTouched(fn: () => void): void;
797
+ /** @docs-private */
737
798
  setDisabledState(isDisabled: boolean): void;
799
+ private hasMatchingOption;
738
800
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfSelectComponent, never>;
739
- 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>;
740
802
  }
741
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;
863
+ }
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
+
742
890
  /**
743
891
  * Textarea component with form control support
744
892
  *
@@ -3068,6 +3216,288 @@ declare class AfTableCellComponent {
3068
3216
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfTableCellComponent, "af-table-cell", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3069
3217
  }
3070
3218
 
3219
+ /**
3220
+ * Node descriptor consumed by `af-tree`.
3221
+ *
3222
+ * Generic over the application data type `T` so consumers can attach typed
3223
+ * payloads without losing type safety in slot templates.
3224
+ */
3225
+ interface TreeNode<T = unknown> {
3226
+ /** Stable id; used as the default `trackBy` key. */
3227
+ id: string;
3228
+ /** Human-readable label; rendered as a11y fallback when no `nodeContent` slot is provided. */
3229
+ label: string;
3230
+ /** Arbitrary application data — typed via the generic parameter. */
3231
+ data?: T;
3232
+ /** Child nodes; `undefined` triggers async-load on first expand, `[]` is a known-empty leaf. */
3233
+ children?: TreeNode<T>[];
3234
+ /** Disabled nodes cannot receive focus, expand, select, or activate. */
3235
+ disabled?: boolean;
3236
+ /** Force leaf rendering even when `children` is undefined (skips async-load hint). */
3237
+ isLeaf?: boolean;
3238
+ /** Renders a busy indicator on the row — set while children are loading. */
3239
+ loading?: boolean;
3240
+ /** Free-form metadata; e.g. `{ orphan: true }` enables the warning state. */
3241
+ meta?: Record<string, unknown>;
3242
+ }
3243
+ /** Selection mode for the tree. */
3244
+ type TreeSelectionMode = 'none' | 'single' | 'multiple';
3245
+ /** Payload of the `nodeToggle` output. */
3246
+ interface TreeToggleEvent<T = unknown> {
3247
+ node: TreeNode<T>;
3248
+ expanded: boolean;
3249
+ }
3250
+ /**
3251
+ * Context object passed to slot templates so consumers can render bespoke
3252
+ * content while still receiving the active filter for custom highlighting.
3253
+ */
3254
+ interface TreeNodeTemplateContext<T = unknown> {
3255
+ $implicit: TreeNode<T>;
3256
+ node: TreeNode<T>;
3257
+ level: number;
3258
+ filter: string;
3259
+ expanded: boolean;
3260
+ selected: boolean;
3261
+ focused: boolean;
3262
+ }
3263
+
3264
+ /**
3265
+ * Accessible Tree component implementing the WAI-ARIA Tree View pattern.
3266
+ *
3267
+ * Wraps the Construct `ct-tree` CSS component into a signal-based, OnPush
3268
+ * Angular component with full keyboard navigation, type-ahead, single /
3269
+ * multi-selection, async-load support, and client-side filtering.
3270
+ *
3271
+ * @example Static org tree
3272
+ * <af-tree
3273
+ * [nodes]="organizations()"
3274
+ * ariaLabel="Organizations"
3275
+ * [showIndentGuides]="true"
3276
+ * selection="single"
3277
+ * [(selectedIds)]="selected"
3278
+ * (nodeActivate)="open($event)">
3279
+ * <ng-template #nodeContent let-node>
3280
+ * <af-icon name="folder" />
3281
+ * <span>{{ node.label }}</span>
3282
+ * <af-badge>{{ node.data.customerType }}</af-badge>
3283
+ * </ng-template>
3284
+ * </af-tree>
3285
+ *
3286
+ * @example Async lazy-load
3287
+ * <af-tree
3288
+ * [nodes]="nodes()"
3289
+ * ariaLabel="File system"
3290
+ * (loadChildren)="onLoad($event)" />
3291
+ *
3292
+ * @accessibility
3293
+ * - Container exposes `role="tree"` and the required `aria-label`.
3294
+ * - Each node is a `<li role="treeitem">` carrying `aria-level`,
3295
+ * `aria-setsize`, `aria-posinset`, and (when expandable) `aria-expanded`.
3296
+ * - Roving tabindex on the `<li>` so screen readers announce treeitem role,
3297
+ * level and selection state when focus lands.
3298
+ * - Keyboard: `↑`/`↓` move focus, `→` expands or steps into children,
3299
+ * `←` collapses or steps to the parent, `Home`/`End` jump, `Enter`
3300
+ * activates, `Space` toggles selection (multi) or activates (single),
3301
+ * `*` expands all sibling branches, A–Z performs incremental type-ahead.
3302
+ * - Selection state is mirrored via `aria-selected` only when
3303
+ * `selection !== 'none'` — leaves implicit selection off in static trees.
3304
+ * - `aria-busy="true"` is rendered on rows whose `node.loading` is `true`.
3305
+ * - Custom slot templates receive the active filter so they can highlight
3306
+ * matches consistently with the default renderer.
3307
+ */
3308
+ declare class AfTreeComponent<T = unknown> {
3309
+ /** Internal id used to scope live-region announcements (debug aid). */
3310
+ protected readonly uid: number;
3311
+ /** I18n bundle resolved via {@link AF_TREE_I18N}. Public so the recursive child component can render labels. */
3312
+ readonly i18n: _neuravision_ng_construct.AfTreeI18n;
3313
+ private readonly announcer;
3314
+ private readonly host;
3315
+ private readonly injector;
3316
+ /** Hierarchical node list. */
3317
+ nodes: _angular_core.InputSignal<TreeNode<T>[]>;
3318
+ /** Container `aria-label` — required by the WAI-ARIA Tree pattern. */
3319
+ ariaLabel: _angular_core.InputSignal<string>;
3320
+ /** Selection mode. Defaults to `'none'`. */
3321
+ selection: _angular_core.InputSignal<TreeSelectionMode>;
3322
+ /** Two-way bound set of expanded node ids. */
3323
+ expandedIds: _angular_core.ModelSignal<Set<string>>;
3324
+ /** Two-way bound set of selected node ids. */
3325
+ selectedIds: _angular_core.ModelSignal<Set<string>>;
3326
+ /** Case-insensitive substring filter; auto-expands ancestors of matches. */
3327
+ filter: _angular_core.InputSignal<string>;
3328
+ /** Render `.ct-tree--guides` (vertical indent lines). */
3329
+ showIndentGuides: _angular_core.InputSignalWithTransform<boolean, unknown>;
3330
+ /** Render `.ct-tree--dense` modifier. */
3331
+ dense: _angular_core.InputSignalWithTransform<boolean, unknown>;
3332
+ /** Render `.ct-tree--bordered` (surface variant). */
3333
+ bordered: _angular_core.InputSignalWithTransform<boolean, unknown>;
3334
+ /** TrackBy override — defaults to `node.id`. */
3335
+ trackBy: _angular_core.InputSignal<(node: TreeNode<T>) => unknown>;
3336
+ /** Emits when a node is activated (Enter or row click). */
3337
+ nodeActivate: _angular_core.OutputEmitterRef<TreeNode<T>>;
3338
+ /** Emits when a node is expanded or collapsed. */
3339
+ nodeToggle: _angular_core.OutputEmitterRef<TreeToggleEvent<T>>;
3340
+ /** Emits when focus moves to a node. */
3341
+ nodeFocus: _angular_core.OutputEmitterRef<TreeNode<T>>;
3342
+ /** Emits the first time a node with `children === undefined` is expanded (lazy-load hook). */
3343
+ loadChildren: _angular_core.OutputEmitterRef<TreeNode<T>>;
3344
+ /** Template for custom node content; falls back to `node.label` with highlight. */
3345
+ nodeContent: _angular_core.Signal<TemplateRef<TreeNodeTemplateContext<T>> | undefined>;
3346
+ /** Template for action buttons rendered visible-on-hover/focus. */
3347
+ nodeActions: _angular_core.Signal<TemplateRef<TreeNodeTemplateContext<T>> | undefined>;
3348
+ /** Template for warning slot (e.g. orphan indicators). */
3349
+ nodeWarning: _angular_core.Signal<TemplateRef<TreeNodeTemplateContext<T>> | undefined>;
3350
+ /** Template shown when the (filtered) tree is empty. */
3351
+ emptySlot: _angular_core.Signal<TemplateRef<unknown> | undefined>;
3352
+ /** Currently focused node id (drives the roving tabindex). */
3353
+ readonly focusedId: _angular_core.WritableSignal<string | null>;
3354
+ /** Tracks nodes whose `loadChildren` has already fired so we don't refire on re-expand. */
3355
+ private readonly loadedIds;
3356
+ /** Type-ahead buffer + reset timer. */
3357
+ private typeBuffer;
3358
+ private typeTimer;
3359
+ /** Flat list of visible (expanded-path) nodes. Used for keyboard nav. */
3360
+ protected readonly visibleNodeOrder: _angular_core.Signal<TreeNode<T>[]>;
3361
+ /** Top-level filtered nodes — used by the template. */
3362
+ protected readonly visibleNodes: _angular_core.Signal<TreeNode<T>[]>;
3363
+ protected readonly treeClasses: _angular_core.Signal<string>;
3364
+ /**
3365
+ * Set of node ids matching the current filter. Empty set means no filter active.
3366
+ * Filtering is case-insensitive substring on `node.label`.
3367
+ */
3368
+ private readonly filterMatches;
3369
+ /** Auto-expanded ids derived from active filter (ancestors of matches). */
3370
+ private readonly autoExpandedIds;
3371
+ constructor();
3372
+ /** A node is expandable when it has unloaded children, real children, or is loading. */
3373
+ isExpandable(node: TreeNode<T>): boolean;
3374
+ isExpanded(node: TreeNode<T>): boolean;
3375
+ isSelected(node: TreeNode<T>): boolean;
3376
+ /** Returns the children list filtered by the active filter (or all if none). */
3377
+ filterChildren(children: TreeNode<T>[]): TreeNode<T>[];
3378
+ /** Wraps the first case-insensitive match of the active filter in `<mark>` tags. */
3379
+ highlight(label: string): string;
3380
+ /** Toggle the expanded state of `node`; fires `loadChildren` on first lazy-expand. */
3381
+ toggle(node: TreeNode<T>): void;
3382
+ /**
3383
+ * Activate `node` — always emits `nodeActivate` and updates selection per
3384
+ * mode. In `multiple` mode `Enter` activates without toggling selection so
3385
+ * keyboard users can drive a primary action without disturbing checkboxes.
3386
+ */
3387
+ activate(node: TreeNode<T>, source: 'click' | 'enter' | 'space'): void;
3388
+ private applySingleSelection;
3389
+ private toggleMultiSelection;
3390
+ protected handleKeydown(event: KeyboardEvent): void;
3391
+ protected handleClick(event: MouseEvent): void;
3392
+ private expandClosedSiblings;
3393
+ private typeahead;
3394
+ /** Move focus to `node` — updates the roving tabindex and pulls DOM focus into the tree. */
3395
+ private moveFocus;
3396
+ /** Programmatically focus the tree row matching `id`. */
3397
+ focusNode(id: string): void;
3398
+ /** Walk the tree until a node with the given id is found. */
3399
+ findNode(id: string, list?: TreeNode<T>[]): TreeNode<T> | null;
3400
+ /** Locate the parent of `id` — returns null when the node is at root level. */
3401
+ findParent(id: string, list?: TreeNode<T>[]): TreeNode<T> | null;
3402
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AfTreeComponent<any>, never>;
3403
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AfTreeComponent<any>, "af-tree", never, { "nodes": { "alias": "nodes"; "required": true; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": true; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "expandedIds": { "alias": "expandedIds"; "required": false; "isSignal": true; }; "selectedIds": { "alias": "selectedIds"; "required": false; "isSignal": true; }; "filter": { "alias": "filter"; "required": false; "isSignal": true; }; "showIndentGuides": { "alias": "showIndentGuides"; "required": false; "isSignal": true; }; "dense": { "alias": "dense"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "trackBy": { "alias": "trackBy"; "required": false; "isSignal": true; }; }, { "expandedIds": "expandedIdsChange"; "selectedIds": "selectedIdsChange"; "nodeActivate": "nodeActivate"; "nodeToggle": "nodeToggle"; "nodeFocus": "nodeFocus"; "loadChildren": "loadChildren"; }, ["nodeContent", "nodeActions", "nodeWarning", "emptySlot"], never, true, never>;
3404
+ }
3405
+
3406
+ /** Translatable strings used by the tree component. */
3407
+ interface AfTreeI18n {
3408
+ /** Screen-reader announcement when a node is expanded. Use `{label}` placeholder. */
3409
+ expanded: string;
3410
+ /** Screen-reader announcement when a node is collapsed. Use `{label}` placeholder. */
3411
+ collapsed: string;
3412
+ /** Screen-reader announcement when a node is selected. Use `{label}` placeholder. */
3413
+ selected: string;
3414
+ /** `aria-label` for the chevron toggle button (button is `aria-hidden`, used as fallback). */
3415
+ toggleLabel: string;
3416
+ /** Visually-hidden text inside the `aria-busy` row announcing async loading. */
3417
+ loadingLabel: string;
3418
+ /** Default `<af-empty-state>` message when the tree has zero nodes (and no `empty` slot). */
3419
+ emptyMessage: string;
3420
+ /** Tooltip / aria-label on the orphan warning marker. */
3421
+ orphanLabel: string;
3422
+ }
3423
+ /**
3424
+ * Injection token to override tree screen-reader announcements
3425
+ * and visible labels for i18n.
3426
+ *
3427
+ * @example
3428
+ * providers: [{
3429
+ * provide: AF_TREE_I18N,
3430
+ * useValue: {
3431
+ * expanded: '{label} ausgeklappt',
3432
+ * collapsed: '{label} eingeklappt',
3433
+ * selected: '{label} ausgewählt',
3434
+ * toggleLabel: 'Aufklappen / Zuklappen',
3435
+ * loadingLabel: 'Lädt …',
3436
+ * emptyMessage: 'Keine Einträge',
3437
+ * orphanLabel: 'Übergeordneter Eintrag fehlt',
3438
+ * },
3439
+ * }]
3440
+ */
3441
+ declare const AF_TREE_I18N: InjectionToken<AfTreeI18n>;
3442
+
3443
+ /**
3444
+ * Test harness for {@link AfTreeComponent}.
3445
+ *
3446
+ * Wraps the rendered DOM behind a semantic API so specs and host apps can
3447
+ * navigate the tree without coupling to internal CSS class names.
3448
+ *
3449
+ * @example
3450
+ * const harness = new AfTreeHarness(fixture.nativeElement);
3451
+ * harness.getNode('root').focus();
3452
+ * harness.pressKey('ArrowDown');
3453
+ * expect(harness.focusedId()).toBe('child-1');
3454
+ */
3455
+ declare class AfTreeHarness {
3456
+ private readonly hostEl;
3457
+ constructor(container: HTMLElement);
3458
+ /** Container `<ul role="tree">` element. */
3459
+ getRootElement(): HTMLElement | null;
3460
+ /** Returns harnesses for every visible (rendered) treeitem in document order. */
3461
+ getVisibleNodes(): AfTreeNodeHarness[];
3462
+ /** Returns the harness for the node with the given id, or null when not rendered. */
3463
+ getNode(id: string): AfTreeNodeHarness | null;
3464
+ /** Id of the currently focused node (the one whose `<li>` carries `tabindex=0`). */
3465
+ focusedId(): string | null;
3466
+ /** Dispatches a keydown on the focused row (or first row if none focused). */
3467
+ pressKey(key: string): void;
3468
+ /** Returns whether `aria-multiselectable` is set on the root list. */
3469
+ isMultiselectable(): boolean;
3470
+ /** Returns the `aria-label` of the root list. */
3471
+ getAriaLabel(): string | null;
3472
+ /** True when the empty-state is rendered (no rows visible). */
3473
+ isEmpty(): boolean;
3474
+ }
3475
+ /** Test harness for a single `<li role="treeitem">` rendered by `af-tree`. */
3476
+ declare class AfTreeNodeHarness {
3477
+ private readonly liEl;
3478
+ constructor(liEl: HTMLElement);
3479
+ /** Stable id of the node (mirrors `TreeNode.id`). */
3480
+ getId(): string;
3481
+ /** Trimmed label text of the node. */
3482
+ getLabel(): string;
3483
+ /** 1-based depth of the node. */
3484
+ getLevel(): number;
3485
+ isExpanded(): boolean;
3486
+ isExpandable(): boolean;
3487
+ isSelected(): boolean;
3488
+ isDisabled(): boolean;
3489
+ isBusy(): boolean;
3490
+ isFocused(): boolean;
3491
+ /** Programmatically focus the row's `<li>` element. */
3492
+ focus(): void;
3493
+ /** Click the row (centre area, not the toggle / actions). */
3494
+ clickRow(): void;
3495
+ /** Click the chevron toggle button. */
3496
+ clickToggle(): void;
3497
+ /** Return the underlying `<li>` element for advanced assertions. */
3498
+ getElement(): HTMLElement;
3499
+ }
3500
+
3071
3501
  /**
3072
3502
  * Transforms snake_case or SCREAMING_SNAKE_CASE strings to Title Case labels.
3073
3503
  *
@@ -3082,5 +3512,5 @@ declare class AfFormatLabelPipe implements PipeTransform {
3082
3512
  static ɵpipe: _angular_core.ɵɵPipeDeclaration<AfFormatLabelPipe, "afFormatLabel", true>;
3083
3513
  }
3084
3514
 
3085
- export { AF_ACCORDION_I18N, AF_ALERT_I18N, AF_INPUT_I18N, AF_SELECT_MENU_I18N, 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, AfSelectMenuComponent, AfSelectMenuHarness, AfSidebarComponent, AfSkeletonComponent, AfSkipLinkComponent, AfSliderComponent, AfSpinnerComponent, AfSwitchComponent, AfTabPanelComponent, AfTableBodyComponent, AfTableCellComponent, AfTableComponent, AfTableHeaderCellComponent, AfTableHeaderComponent, AfTableRowComponent, AfTabsComponent, AfTextareaComponent, AfToastContainerComponent, AfToastService, AfToggleGroupComponent, AfToolbarComponent, AfTooltipDirective };
3086
- 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, AfSelectMenuI18n, AfSelectMenuOption, AfSelectOption, AfShellPanelState, AfShellSidebarState, AfSidebarMode, AfSkeletonVariant, AfSliderSize, AfSortDirection, AfSortState, AfSpinnerSize, AfTab, AfTableCellType, AfTableVariant, AfToast, AfToastVariant, AfToggleGroupSize, AfToggleItem, AfTooltipPosition };
3515
+ export { AF_ACCORDION_I18N, AF_ALERT_I18N, AF_INPUT_I18N, AF_SELECT_I18N, AF_SELECT_MENU_I18N, AF_TREE_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, AfTreeComponent, AfTreeHarness, AfTreeNodeHarness };
3516
+ 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, AfTreeI18n, TreeNode, TreeNodeTemplateContext, TreeSelectionMode, TreeToggleEvent };