@neuravision/ng-construct 0.2.0 → 0.3.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.
@@ -2,6 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { input, output, signal, computed, ChangeDetectionStrategy, Component, inject, forwardRef, model, booleanAttribute, viewChild, contentChildren, effect, contentChild, ElementRef, TemplateRef, Directive, Injectable, viewChildren, Renderer2, InjectionToken, DOCUMENT, numberAttribute, Pipe } from '@angular/core';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
4
  import { NgTemplateOutlet } from '@angular/common';
5
+ import { RouterLink, RouterLinkActive } from '@angular/router';
5
6
 
6
7
  /**
7
8
  * Alert component for displaying contextual feedback messages.
@@ -1631,7 +1632,7 @@ class AfCardComponent {
1631
1632
  }
1632
1633
  }
1633
1634
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1634
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfCardComponent, isStandalone: true, selector: "af-card", inputs: { interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, queries: [{ propertyName: "headerRef", first: true, predicate: ["[header]"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "footerRef", first: true, predicate: ["[footer]"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
1635
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.2", type: AfCardComponent, isStandalone: true, selector: "af-card", inputs: { interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, elevation: { classPropertyName: "elevation", publicName: "elevation", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, queries: [{ propertyName: "headerRef", first: true, predicate: ["[header]"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "footerRef", first: true, predicate: ["[footer]"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
1635
1636
  <section
1636
1637
  [class]="cardClasses()"
1637
1638
  [style]="cardStyles()"
@@ -1640,20 +1641,16 @@ class AfCardComponent {
1640
1641
  [attr.aria-label]="ariaLabel() || null"
1641
1642
  (click)="onCardClick()"
1642
1643
  (keydown)="onCardKeydown($event)">
1643
- @if (hasHeader()) {
1644
- <div class="ct-card__header">
1645
- <ng-content select="[header]" />
1646
- </div>
1647
- }
1644
+ <div class="ct-card__header" [hidden]="!hasHeader()">
1645
+ <ng-content select="[header]" />
1646
+ </div>
1648
1647
  <div class="ct-card__body">
1649
1648
  <ng-content select="[body]" />
1650
1649
  <ng-content />
1651
1650
  </div>
1652
- @if (hasFooter()) {
1653
- <div class="ct-card__footer">
1654
- <ng-content select="[footer]" />
1655
- </div>
1656
- }
1651
+ <div class="ct-card__footer" [hidden]="!hasFooter()">
1652
+ <ng-content select="[footer]" />
1653
+ </div>
1657
1654
  </section>
1658
1655
  `, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1659
1656
  }
@@ -1668,20 +1665,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
1668
1665
  [attr.aria-label]="ariaLabel() || null"
1669
1666
  (click)="onCardClick()"
1670
1667
  (keydown)="onCardKeydown($event)">
1671
- @if (hasHeader()) {
1672
- <div class="ct-card__header">
1673
- <ng-content select="[header]" />
1674
- </div>
1675
- }
1668
+ <div class="ct-card__header" [hidden]="!hasHeader()">
1669
+ <ng-content select="[header]" />
1670
+ </div>
1676
1671
  <div class="ct-card__body">
1677
1672
  <ng-content select="[body]" />
1678
1673
  <ng-content />
1679
1674
  </div>
1680
- @if (hasFooter()) {
1681
- <div class="ct-card__footer">
1682
- <ng-content select="[footer]" />
1683
- </div>
1684
- }
1675
+ <div class="ct-card__footer" [hidden]="!hasFooter()">
1676
+ <ng-content select="[footer]" />
1677
+ </div>
1685
1678
  </section>
1686
1679
  `, styles: [":host{display:block}\n"] }]
1687
1680
  }], propDecorators: { interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], elevation: [{ type: i0.Input, args: [{ isSignal: true, alias: "elevation", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }], headerRef: [{ type: i0.ContentChild, args: ['[header]', { ...{ read: ElementRef }, isSignal: true }] }], footerRef: [{ type: i0.ContentChild, args: ['[footer]', { ...{ read: ElementRef }, isSignal: true }] }] } });
@@ -2099,11 +2092,9 @@ class AfModalComponent {
2099
2092
  <ng-content select="[body]"></ng-content>
2100
2093
  <ng-content></ng-content>
2101
2094
  </div>
2102
- @if (hasFooter()) {
2103
- <div class="ct-modal__footer">
2104
- <ng-content select="[footer]"></ng-content>
2105
- </div>
2106
- }
2095
+ <div class="ct-modal__footer" [hidden]="!hasFooter()">
2096
+ <ng-content select="[footer]"></ng-content>
2097
+ </div>
2107
2098
  </div>
2108
2099
  </div>
2109
2100
  }
@@ -2143,11 +2134,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
2143
2134
  <ng-content select="[body]"></ng-content>
2144
2135
  <ng-content></ng-content>
2145
2136
  </div>
2146
- @if (hasFooter()) {
2147
- <div class="ct-modal__footer">
2148
- <ng-content select="[footer]"></ng-content>
2149
- </div>
2150
- }
2137
+ <div class="ct-modal__footer" [hidden]="!hasFooter()">
2138
+ <ng-content select="[footer]"></ng-content>
2139
+ </div>
2151
2140
  </div>
2152
2141
  </div>
2153
2142
  }
@@ -6194,17 +6183,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
6194
6183
 
6195
6184
  let nextId = 0;
6196
6185
  /**
6197
- * Individual navigation item used within af-navbar.
6186
+ * Individual navigation item used within af-navbar or af-toolbar.
6187
+ *
6188
+ * Supports Angular Router via `routerLink`, standard links via `href`,
6189
+ * and button mode when neither is provided. Content projection allows
6190
+ * icons and custom markup inside the link.
6198
6191
  *
6199
6192
  * @example
6200
- * <af-nav-item label="Dashboard" href="/dashboard" [active]="true" />
6193
+ * <af-nav-item label="Dashboard" routerLink="/dashboard">
6194
+ * <af-icon name="dashboard" /> Dashboard
6195
+ * </af-nav-item>
6201
6196
  */
6202
6197
  class AfNavItemComponent {
6203
- /** Text label for the navigation item. */
6198
+ /** Text label shown as fallback when no content is projected. Also used by the mobile menu. */
6204
6199
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
6205
6200
  /** URL for the navigation link. Renders as `<a>` when provided, `<button>` otherwise. */
6206
6201
  href = input('', ...(ngDevMode ? [{ debugName: "href" }] : []));
6207
- /** Marks this item as the currently active page. */
6202
+ /** Angular Router link. Renders as `<a>` with routerLink and auto-active detection. */
6203
+ routerLink = input(null, ...(ngDevMode ? [{ debugName: "routerLink" }] : []));
6204
+ /** Marks this item as the currently active page (used for href/button mode, routerLink auto-detects). */
6208
6205
  active = input(false, { ...(ngDevMode ? { debugName: "active" } : {}), transform: booleanAttribute });
6209
6206
  /** Disables interaction with this item. */
6210
6207
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}), transform: booleanAttribute });
@@ -6225,76 +6222,106 @@ class AfNavItemComponent {
6225
6222
  this.clicked.emit(event);
6226
6223
  }
6227
6224
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfNavItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6228
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfNavItemComponent, isStandalone: true, selector: "af-nav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, viewQueries: [{ propertyName: "linkRef", first: true, predicate: ["linkEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
6225
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: AfNavItemComponent, isStandalone: true, selector: "af-nav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, viewQueries: [{ propertyName: "linkRef", first: true, predicate: ["linkEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
6229
6226
  <li class="ct-navbar__item" role="none">
6230
- @if (href()) {
6227
+ @if (routerLink()) {
6231
6228
  <a
6232
6229
  #linkEl
6233
6230
  class="ct-navbar__link"
6231
+ [routerLink]="routerLink()!"
6232
+ routerLinkActive="ct-navbar__link--active"
6233
+ role="menuitem"
6234
+ [attr.aria-disabled]="disabled() || null"
6235
+ [attr.tabindex]="rovingTabindex()"
6236
+ (click)="onClick($event)">
6237
+ <ng-content>{{ label() }}</ng-content>
6238
+ </a>
6239
+ } @else if (href()) {
6240
+ <a
6241
+ #linkEl
6242
+ class="ct-navbar__link"
6243
+ [class.ct-navbar__link--active]="active()"
6234
6244
  [href]="href()"
6235
6245
  role="menuitem"
6236
6246
  [attr.aria-current]="active() ? 'page' : null"
6237
6247
  [attr.aria-disabled]="disabled() || null"
6238
6248
  [attr.tabindex]="rovingTabindex()"
6239
6249
  (click)="onClick($event)">
6240
- {{ label() }}
6250
+ <ng-content>{{ label() }}</ng-content>
6241
6251
  </a>
6242
6252
  } @else {
6243
6253
  <button
6244
6254
  #linkEl
6245
6255
  class="ct-navbar__link"
6256
+ [class.ct-navbar__link--active]="active()"
6246
6257
  type="button"
6247
6258
  role="menuitem"
6248
6259
  [attr.aria-current]="active() ? 'page' : null"
6249
6260
  [attr.aria-disabled]="disabled() || null"
6250
6261
  [attr.tabindex]="rovingTabindex()"
6251
6262
  (click)="onClick($event)">
6252
- {{ label() }}
6263
+ <ng-content>{{ label() }}</ng-content>
6253
6264
  </button>
6254
6265
  }
6255
6266
  </li>
6256
- `, isInline: true, styles: [":host{display:contents}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6267
+ `, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6257
6268
  }
6258
6269
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfNavItemComponent, decorators: [{
6259
6270
  type: Component,
6260
- args: [{ selector: 'af-nav-item', changeDetection: ChangeDetectionStrategy.OnPush, template: `
6271
+ args: [{ selector: 'af-nav-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink, RouterLinkActive], template: `
6261
6272
  <li class="ct-navbar__item" role="none">
6262
- @if (href()) {
6273
+ @if (routerLink()) {
6263
6274
  <a
6264
6275
  #linkEl
6265
6276
  class="ct-navbar__link"
6277
+ [routerLink]="routerLink()!"
6278
+ routerLinkActive="ct-navbar__link--active"
6279
+ role="menuitem"
6280
+ [attr.aria-disabled]="disabled() || null"
6281
+ [attr.tabindex]="rovingTabindex()"
6282
+ (click)="onClick($event)">
6283
+ <ng-content>{{ label() }}</ng-content>
6284
+ </a>
6285
+ } @else if (href()) {
6286
+ <a
6287
+ #linkEl
6288
+ class="ct-navbar__link"
6289
+ [class.ct-navbar__link--active]="active()"
6266
6290
  [href]="href()"
6267
6291
  role="menuitem"
6268
6292
  [attr.aria-current]="active() ? 'page' : null"
6269
6293
  [attr.aria-disabled]="disabled() || null"
6270
6294
  [attr.tabindex]="rovingTabindex()"
6271
6295
  (click)="onClick($event)">
6272
- {{ label() }}
6296
+ <ng-content>{{ label() }}</ng-content>
6273
6297
  </a>
6274
6298
  } @else {
6275
6299
  <button
6276
6300
  #linkEl
6277
6301
  class="ct-navbar__link"
6302
+ [class.ct-navbar__link--active]="active()"
6278
6303
  type="button"
6279
6304
  role="menuitem"
6280
6305
  [attr.aria-current]="active() ? 'page' : null"
6281
6306
  [attr.aria-disabled]="disabled() || null"
6282
6307
  [attr.tabindex]="rovingTabindex()"
6283
6308
  (click)="onClick($event)">
6284
- {{ label() }}
6309
+ <ng-content>{{ label() }}</ng-content>
6285
6310
  </button>
6286
6311
  }
6287
6312
  </li>
6288
6313
  `, styles: [":host{display:contents}\n"] }]
6289
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], linkRef: [{ type: i0.ViewChild, args: ['linkEl', { isSignal: true }] }] } });
6314
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], linkRef: [{ type: i0.ViewChild, args: ['linkEl', { isSignal: true }] }] } });
6290
6315
  /**
6291
6316
  * Responsive navbar with mobile menu, keyboard navigation, and ARIA landmarks.
6292
6317
  *
6293
6318
  * @example
6294
6319
  * <af-navbar ariaLabel="Main navigation">
6295
6320
  * <a brand class="ct-navbar__brand" href="/">My App</a>
6296
- * <af-nav-item label="Dashboard" href="/dashboard" [active]="true" />
6297
- * <af-nav-item label="Settings" href="/settings" />
6321
+ * <af-nav-item label="Dashboard" routerLink="/dashboard">
6322
+ * <af-icon name="dashboard" /> Dashboard
6323
+ * </af-nav-item>
6324
+ * <af-nav-item label="Settings" routerLink="/settings" />
6298
6325
  * <button actions class="ct-button">Profile</button>
6299
6326
  * </af-navbar>
6300
6327
  */
@@ -6517,7 +6544,19 @@ class AfNavbarComponent {
6517
6544
  role="menu"
6518
6545
  aria-label="Mobile navigation">
6519
6546
  @for (item of items(); track item) {
6520
- @if (item.href()) {
6547
+ @if (item.routerLink(); as rl) {
6548
+ <a
6549
+ #mobileLink
6550
+ class="ct-navbar__link"
6551
+ [routerLink]="rl"
6552
+ routerLinkActive="ct-navbar__link--active"
6553
+ role="menuitem"
6554
+ [attr.aria-disabled]="item.disabled() || null"
6555
+ [attr.tabindex]="mobileMenuOpen() ? 0 : -1"
6556
+ (click)="onMobileItemClick($event, item)">
6557
+ {{ item.label() }}
6558
+ </a>
6559
+ } @else if (item.href()) {
6521
6560
  <a
6522
6561
  #mobileLink
6523
6562
  class="ct-navbar__link"
@@ -6545,11 +6584,11 @@ class AfNavbarComponent {
6545
6584
  }
6546
6585
  </div>
6547
6586
  </header>
6548
- `, isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6587
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6549
6588
  }
6550
6589
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AfNavbarComponent, decorators: [{
6551
6590
  type: Component,
6552
- args: [{ selector: 'af-navbar', changeDetection: ChangeDetectionStrategy.OnPush, host: {
6591
+ args: [{ selector: 'af-navbar', changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink, RouterLinkActive], host: {
6553
6592
  '(keydown)': 'handleKeydown($event)',
6554
6593
  '(document:click)': 'onDocumentClick($event)',
6555
6594
  }, template: `
@@ -6590,7 +6629,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
6590
6629
  role="menu"
6591
6630
  aria-label="Mobile navigation">
6592
6631
  @for (item of items(); track item) {
6593
- @if (item.href()) {
6632
+ @if (item.routerLink(); as rl) {
6633
+ <a
6634
+ #mobileLink
6635
+ class="ct-navbar__link"
6636
+ [routerLink]="rl"
6637
+ routerLinkActive="ct-navbar__link--active"
6638
+ role="menuitem"
6639
+ [attr.aria-disabled]="item.disabled() || null"
6640
+ [attr.tabindex]="mobileMenuOpen() ? 0 : -1"
6641
+ (click)="onMobileItemClick($event, item)">
6642
+ {{ item.label() }}
6643
+ </a>
6644
+ } @else if (item.href()) {
6594
6645
  <a
6595
6646
  #mobileLink
6596
6647
  class="ct-navbar__link"