ng-hub-ui-nav 22.14.3 → 22.16.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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  A flexible, accessible, and highly customizable navigation component for Angular 21+. It supports horizontal menus, vertical sidebars, mobile collapse modes, stacked drill-down panels, projected start/end slots, and scroll-spy integration.
9
9
 
10
10
  > [!IMPORTANT]
11
- > Version `22.14.3` targets Angular 22 and follows the signal-first architecture used across `ng-hub-ui`.
11
+ > Version `22.16.0` targets Angular 22 and follows the signal-first architecture used across `ng-hub-ui`.
12
12
 
13
13
  ## Documentation and Live Examples
14
14
 
@@ -150,23 +150,36 @@ export class ExampleComponent {
150
150
  ### Scroll Spy
151
151
 
152
152
  ```html
153
- <section
154
- hubNavScrollSpy
155
- (activeSectionChange)="activeSection = $event"
156
- >
153
+ <section hubNavScrollSpy (activeSectionChange)="activeSection = $event">
157
154
  <section id="overview" hubNavScrollSpySection>...</section>
158
155
  <section id="api" hubNavScrollSpySection>...</section>
159
156
  </section>
160
157
  ```
161
158
 
159
+ #### Scroll Spy joined to a nav
160
+
161
+ Bind the spy to the nav and the two halves work as one: the section under the reader marks the
162
+ matching entry — by `id` or by `fragment` — and a click on an entry scrolls to its section. No
163
+ URL is written and no history entry is left, so an in-page index costs nothing but the scroll.
164
+
165
+ ```html
166
+ <hub-nav #index [items]="sections" [config]="{ orientation: 'vertical' }" />
167
+
168
+ <div #body class="page__body" hubNavScrollSpy [nav]="index" [scrollContainer]="body" [clickSettleMs]="1000" [offset]="96">
169
+ <section id="overview" hubNavScrollSpySection>...</section>
170
+ <section id="api" hubNavScrollSpySection>...</section>
171
+ </div>
172
+ ```
173
+
174
+ `scrollContainer` is the element that actually scrolls — declare it when the automatic walk up
175
+ the tree would find the wrong one, or when the container only becomes scrollable once its
176
+ content arrives. `clickSettleMs` keeps the section a click asked for while the jump is still in
177
+ flight, so a stray wheel notch does not land the mark halfway.
178
+
162
179
  ### Collapsed Icon Rail
163
180
 
164
181
  ```html
165
- <hub-nav
166
- [items]="items"
167
- [(rail)]="rail"
168
- [config]="{ orientation: 'vertical', verticalExpandMode: 'accordion' }"
169
- >
182
+ <hub-nav [items]="items" [(rail)]="rail" [config]="{ orientation: 'vertical', verticalExpandMode: 'accordion' }">
170
183
  <!-- The slot context exposes the rail state, e.g. to swap the logo for a mark -->
171
184
  <ng-template hubNavStart let-rail="rail">
172
185
  <span class="brand">{{ rail ? 'A' : 'Acme ERP' }}</span>
@@ -182,26 +195,28 @@ A toggle button ships on the outer edge of the primary column: an arrow inside a
182
195
 
183
196
  #### Inputs
184
197
 
185
- | Input | Type | Default | Description |
186
- |---|---|---|---|
187
- | `items` | `HubNavItem[]` | required | Navigation tree to render. |
188
- | `config` | `Partial<HubNavConfig>` | `{}` | Per-instance config merged with global defaults. |
189
- | `navClass` | `string` | `''` | Additional class applied to the internal `<nav>`. |
190
- | `itemTemplate` | `TemplateRef<unknown> \| null` | `null` | Optional custom item template. |
191
- | `autoOpenFromRoute` | `boolean` | `false` | Opens matching dropdowns/panels from the current route, and leaves exactly one section open when roots expand differently: arriving at an accordion root drops the panel of the panel root you left, and the other way round. It also re-derives the stack when the viewport comes back above `collapseBreakpoint`; with the input off, a stack opened by hand survives that round trip. |
192
- | `rail` | `boolean` (two-way `model`) | `false` | Desktop-only icon rail for vertical navs. Ignored below `collapseBreakpoint`. Bind with `[(rail)]`. |
193
- | `color` | `'primary' \| 'success' \| 'danger' \| 'warning' \| 'info' \| string \| undefined` | `undefined` (reads as `primary`) | Semantic accent for the hover/active affordances. A bareword — semantic name, registered accent or CSS named colour — resolves through `--hub-sys-color-<name>`; a literal `#hex` / `rgb()` / `oklch()` / `var()` is passed through unchanged. |
198
+ | Input | Type | Default | Description |
199
+ | ------------------- | ---------------------------------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
200
+ | `items` | `HubNavItem[]` | required | Navigation tree to render. |
201
+ | `config` | `Partial<HubNavConfig>` | `{}` | Per-instance config merged with global defaults. |
202
+ | `navClass` | `string` | `''` | Additional class applied to the internal `<nav>`. |
203
+ | `itemTemplate` | `TemplateRef<unknown> \| null` | `null` | Optional custom item template. |
204
+ | `activeItemId` | `string \| null` (two-way `model`) | `null` | Entry to mark active, by `id` or `fragment`, when the router is not what says where the reader is. While it is set it answers for the whole menu: route matching stands down, so the mark cannot land on two entries at once, and the marked entry announces `aria-current="location"`. Bind with `[(activeItemId)]`. |
205
+ | `autoOpenFromRoute` | `boolean` | `false` | Opens matching dropdowns/panels from the current route, and leaves exactly one section open when roots expand differently: arriving at an accordion root drops the panel of the panel root you left, and the other way round. It also re-derives the stack when the viewport comes back above `collapseBreakpoint`; with the input off, a stack opened by hand survives that round trip. |
206
+ | `rail` | `boolean` (two-way `model`) | `false` | Desktop-only icon rail for vertical navs. Ignored below `collapseBreakpoint`. Bind with `[(rail)]`. |
207
+ | `color` | `'primary' \| 'success' \| 'danger' \| 'warning' \| 'info' \| string \| undefined` | `undefined` (reads as `primary`) | Semantic accent for the hover/active affordances. A bareword — semantic name, registered accent or CSS named colour — resolves through `--hub-sys-color-<name>`; a literal `#hex` / `rgb()` / `oklch()` / `var()` is passed through unchanged. |
194
208
 
195
209
  #### Outputs
196
210
 
197
- | Output | Type | Description |
198
- |---|---|---|
199
- | `itemClick` | `OutputEmitterRef<HubNavItem>` | Emitted when a link item is activated. |
200
- | `dropdownOpen` | `OutputEmitterRef<HubNavItem>` | Emitted when a dropdown opens. |
201
- | `dropdownClose` | `OutputEmitterRef<HubNavItem>` | Emitted when a dropdown closes. |
202
- | `mobileToggle` | `OutputEmitterRef<boolean>` | Emitted when the responsive mobile panel opens or closes. |
203
- | `panelChange` | `OutputEmitterRef<HubNavPanelEvent>` | Emitted when a panel opens, closes, drills down, or drills back. |
204
- | `railChange` | `OutputEmitterRef<boolean>` | Emitted when the rail model flips — persist it app-side to restore the rail on boot. |
211
+ | Output | Type | Description |
212
+ | -------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
213
+ | `itemClick` | `OutputEmitterRef<HubNavItem>` | Emitted when an entry is activated, with or without a route. Headers, separators and disabled items never emit. |
214
+ | `activeItemIdChange` | `OutputEmitterRef<string \| null>` | Emitted when the marked entry changes, which is how a bound `hubNavScrollSpy` reports the section under the reader. |
215
+ | `dropdownOpen` | `OutputEmitterRef<HubNavItem>` | Emitted when a dropdown opens. |
216
+ | `dropdownClose` | `OutputEmitterRef<HubNavItem>` | Emitted when a dropdown closes. |
217
+ | `mobileToggle` | `OutputEmitterRef<boolean>` | Emitted when the responsive mobile panel opens or closes. |
218
+ | `panelChange` | `OutputEmitterRef<HubNavPanelEvent>` | Emitted when a panel opens, closes, drills down, or drills back. |
219
+ | `railChange` | `OutputEmitterRef<boolean>` | Emitted when the rail model flips — persist it app-side to restore the rail on boot. |
205
220
 
206
221
  ### `HubNavConfig`
207
222
 
@@ -235,7 +250,6 @@ interface HubNavConfig {
235
250
 
236
251
  `labels` overrides the built-in accessible strings (`toggleNavigation`, `closeNavigation`, `collapseNavigation`, `expandNavigation`, `goBack`, `closePanel`, `toggleSection` — the last one supports a `{label}` placeholder) per instance. Without an override, each label resolves from the shared `HUBUI.NAV.*` dictionary keys (`provideHubTranslationAdapter()` in `ng-hub-ui-utils`) and finally falls back to English.
237
252
 
238
-
239
253
  ### `HubNavItem`
240
254
 
241
255
  ```typescript
@@ -252,6 +266,7 @@ interface HubNavItem {
252
266
  badge?: string;
253
267
  badgeClass?: string;
254
268
  disabled?: boolean;
269
+ active?: boolean;
255
270
  cssClass?: string;
256
271
  data?: unknown;
257
272
  expandMode?: 'accordion' | 'flyout' | 'panel';
@@ -272,6 +287,26 @@ marked: at `/products/categories` the catalogue entry is marked and
272
287
  `/products` is not, while at `/products/42/edit` the list keeps its mark
273
288
  because nothing more specific matches.
274
289
 
290
+ #### Marking without a route
291
+
292
+ Two things outrank the router, in this order. `active` on the item is read as written, `false`
293
+ included, so an entry can refuse the mark on its own route:
294
+
295
+ ```typescript
296
+ { id: 'overview', label: 'Overview', type: 'link', active: true }
297
+ ```
298
+
299
+ `activeItemId` on the nav names one entry for the whole menu, by `id` or by `fragment`. While it
300
+ is set no route matching runs at all, which is what keeps two entries from lighting up at once;
301
+ set it back to `null` to hand the decision to the URL again.
302
+
303
+ ```html
304
+ <hub-nav [items]="sections" [activeItemId]="currentSection()" />
305
+ ```
306
+
307
+ Either way the marked entry announces itself as `aria-current="location"` rather than `page`:
308
+ the reader is here, but the page did not change to say so.
309
+
275
310
  Set `routerLinkActiveOptions: { exact: true }` on an item that should only be
276
311
  marked on its exact route:
277
312
 
@@ -284,7 +319,7 @@ marked on its exact route:
284
319
  - `hubNavStart`: projects content into the start slot.
285
320
  - `hubNavEnd`: projects content into the end slot.
286
321
  - `hubNavItemTemplate`: overrides item rendering.
287
- - `hubNavScrollSpy`: tracks visible sections in a scroll container.
322
+ - `hubNavScrollSpy`: tracks visible sections in a scroll container. Takes `enabled`, `offset`, `sectionSelector`, `nav`, `scrollContainer` and `clickSettleMs`; emits `activeSectionChange` and exposes `scrollTo(sectionId, behavior?)`.
288
323
  - `hubNavScrollSpySection`: marks a section as spy-trackable.
289
324
 
290
325
  ## Styling