@neuravision/ng-construct 0.5.0 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neuravision/ng-construct",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Angular components for the Construct Design System",
5
5
  "keywords": [
6
6
  "angular",
@@ -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,54 +721,171 @@ 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>;
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;
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;
740
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>;
741
889
 
742
890
  /**
743
891
  * Textarea component with form control support
@@ -3082,5 +3230,5 @@ declare class AfFormatLabelPipe implements PipeTransform {
3082
3230
  static ɵpipe: _angular_core.ɵɵPipeDeclaration<AfFormatLabelPipe, "afFormatLabel", true>;
3083
3231
  }
3084
3232
 
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 };
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 };