ng-hub-ui-panels 21.2.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 ADDED
@@ -0,0 +1,378 @@
1
+ # ng-hub-ui-panels
2
+
3
+ [EspaΓ±ol](./README.es.md) | **English**
4
+
5
+ A versatile, accessible **content-panels** container for Angular that renders as
6
+ **tabs**, **pills**, an **accordion** or plain **cards** from a single API β€” with
7
+ routing, reactive forms, keyboard navigation and CSS-variable theming. Built as
8
+ standalone Angular components on top of Signals.
9
+
10
+ > `ng-hub-ui-panels` supersedes [`ng-hub-ui-accordion`](https://www.npmjs.com/package/ng-hub-ui-accordion). Its accordion view is a drop-in, more capable replacement.
11
+
12
+ ## Documentation and Live Examples
13
+
14
+ This package is part of [Hub UI](https://hubui.dev/), a collection of Angular component libraries for standalone apps.
15
+
16
+ - Docs: https://hubui.dev/panels/overview/
17
+ - Live examples: https://hubui.dev/panels/examples/
18
+ - Hub UI: https://hubui.dev/
19
+
20
+ ## 🧩 Library Family `ng-hub-ui`
21
+
22
+ This library is part of the **ng-hub-ui** ecosystem:
23
+
24
+ - [**ng-hub-ui-accordion**](https://www.npmjs.com/package/ng-hub-ui-accordion) _(deprecated β†’ use panels)_
25
+ - [**ng-hub-ui-action-sheet**](https://www.npmjs.com/package/ng-hub-ui-action-sheet)
26
+ - [**ng-hub-ui-avatar**](https://www.npmjs.com/package/ng-hub-ui-avatar)
27
+ - [**ng-hub-ui-board**](https://www.npmjs.com/package/ng-hub-ui-board)
28
+ - [**ng-hub-ui-breadcrumbs**](https://www.npmjs.com/package/ng-hub-ui-breadcrumbs)
29
+ - [**ng-hub-ui-calendar**](https://www.npmjs.com/package/ng-hub-ui-calendar)
30
+ - [**ng-hub-ui-dropdown**](https://www.npmjs.com/package/ng-hub-ui-dropdown)
31
+ - [**ng-hub-ui-history**](https://www.npmjs.com/package/ng-hub-ui-history)
32
+ - [**ng-hub-ui-modal**](https://www.npmjs.com/package/ng-hub-ui-modal)
33
+ - [**ng-hub-ui-nav**](https://www.npmjs.com/package/ng-hub-ui-nav)
34
+ - [**ng-hub-ui-paginable**](https://www.npmjs.com/package/ng-hub-ui-paginable)
35
+ - [**ng-hub-ui-panels**](https://www.npmjs.com/package/ng-hub-ui-panels) ← You are here
36
+ - [**ng-hub-ui-portal**](https://www.npmjs.com/package/ng-hub-ui-portal)
37
+ - [**ng-hub-ui-sortable**](https://www.npmjs.com/package/ng-hub-ui-sortable)
38
+ - [**ng-hub-ui-stepper**](https://www.npmjs.com/package/ng-hub-ui-stepper)
39
+ - [**ng-hub-ui-utils**](https://www.npmjs.com/package/ng-hub-ui-utils)
40
+
41
+ ---
42
+
43
+ ## πŸš€ Quick Start
44
+
45
+ ### 1. Install
46
+
47
+ ```bash
48
+ npm install ng-hub-ui-panels
49
+ ```
50
+
51
+ ### 2. Import
52
+
53
+ The components are standalone β€” import them directly where you use them:
54
+
55
+ ```ts
56
+ import {
57
+ PanelsComponent,
58
+ PanelComponent,
59
+ PanelHeadingDirective,
60
+ PanelHeaderDirective,
61
+ PanelFooterDirective
62
+ } from 'ng-hub-ui-panels';
63
+ ```
64
+
65
+ ### 3. Use
66
+
67
+ ```html
68
+ <hub-panels>
69
+ <hub-panel heading="Overview">First panel content</hub-panel>
70
+ <hub-panel heading="Details">Second panel content</hub-panel>
71
+ <hub-panel heading="Settings">Third panel content</hub-panel>
72
+ </hub-panels>
73
+ ```
74
+
75
+ ---
76
+
77
+ ## πŸ“¦ Description
78
+
79
+ `ng-hub-ui-panels` unifies the most common content-switching patterns β€” **tabs**,
80
+ **pills** and **accordion** β€” plus a chromeless **card** layout, behind one
81
+ declarative component. Drop `<hub-panel>` panes inside `<hub-panels>` and pick a
82
+ `type`; everything else (keyboard navigation, ARIA wiring, animated collapse,
83
+ routed panels and form binding) works the same across views. A `<hub-panel>` can
84
+ also be used **standalone**, outside any container, where it renders as a card on
85
+ its own.
86
+
87
+ ## 🎯 Features
88
+
89
+ - **Four visualizations** β€” `tabs`, `pills`, `accordion` and `card`, switched with a single `type` input.
90
+ - **Card layout & standalone** β€” `type="card"` renders every panel as an always-visible card; a single `<hub-panel>` also works on its own, outside any container.
91
+ - **Content header/footer slots** β€” `hubPanelHeader` and `hubPanelFooter` mark header/footer bands that render in every view (distinct from the `hubPanelHeading` nav label).
92
+ - **Forms** β€” implements `ControlValueAccessor`; bind the active panel(s) to a `FormControl` or `ngModel` (single or `multiple`), with `bindValue` and `compareWith`.
93
+ - **Routing** β€” a panel with a `routerLink` turns the content area into a `<router-outlet>` that follows the URL.
94
+ - **Keyboard & a11y** β€” roving tabindex, Arrow/Home/End/Delete keys, and correct `role="tablist"`/`tab`/`tabpanel` and accordion `aria-expanded`/`aria-controls` semantics.
95
+ - **Strip layout** β€” `vertical`, `justified` and `scrollable` header strips.
96
+ - **Accordion options** β€” `multiple` expansion and edge-to-edge `flush` layout, with an animated grid-based collapse.
97
+ - **Custom headers** β€” project any markup with the `hubPanelHeading` directive.
98
+ - **Removable panels** β€” opt-in `removable` panels close with a βœ• button or the Delete key.
99
+ - **Theming** β€” every token is a `--hub-panels-*` CSS custom property; the accordion view also honours the `--hub-accordion-*` contract.
100
+
101
+ ---
102
+
103
+ ## πŸ“¦ Installation
104
+
105
+ ```bash
106
+ npm install ng-hub-ui-panels
107
+ ```
108
+
109
+ ### Peer Dependencies
110
+
111
+ ```json
112
+ {
113
+ "@angular/common": ">=21.0.0",
114
+ "@angular/core": ">=21.0.0",
115
+ "@angular/forms": ">=21.0.0",
116
+ "@angular/router": ">=21.0.0"
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## βš™οΈ Usage
123
+
124
+ ### Tabs (default)
125
+
126
+ ```html
127
+ <hub-panels>
128
+ <hub-panel heading="One">First</hub-panel>
129
+ <hub-panel heading="Two">Second</hub-panel>
130
+ </hub-panels>
131
+ ```
132
+
133
+ ### Pills
134
+
135
+ ```html
136
+ <hub-panels type="pills"> … </hub-panels>
137
+ ```
138
+
139
+ ### Accordion
140
+
141
+ ```html
142
+ <hub-panels type="accordion" multiple flush>
143
+ <hub-panel heading="Shipping">…</hub-panel>
144
+ <hub-panel heading="Returns">…</hub-panel>
145
+ </hub-panels>
146
+ ```
147
+
148
+ ### Cards
149
+
150
+ `type="card"` drops the navigation strip entirely: every panel is always visible
151
+ and rendered as a card. Use the `hubPanelHeader` / `hubPanelFooter` slots for the
152
+ card's header and footer bands.
153
+
154
+ ```html
155
+ <hub-panels type="card">
156
+ <hub-panel>
157
+ <div hubPanelHeader>Project summary</div>
158
+ Every panel is always visible and styled as a card.
159
+ <div hubPanelFooter>Updated 2 hours ago</div>
160
+ </hub-panel>
161
+ <hub-panel>
162
+ <div hubPanelHeader>Team</div>
163
+ The same header/footer slots work in tabs, pills and accordion too.
164
+ </hub-panel>
165
+ </hub-panels>
166
+ ```
167
+
168
+ A single `<hub-panel>` can be used on its own, with no container β€” it renders as a
169
+ card by itself:
170
+
171
+ ```html
172
+ <hub-panel>
173
+ <div hubPanelHeader>Standalone card</div>
174
+ Dropped on its own, a hub-panel renders as a card.
175
+ <div hubPanelFooter>
176
+ <button class="btn btn-sm btn-primary">Action</button>
177
+ </div>
178
+ </hub-panel>
179
+ ```
180
+
181
+ > `hubPanelHeader` / `hubPanelFooter` are content bands inside the panel body and
182
+ > render in **every** view. They are different from `hubPanelHeading`, which is the
183
+ > navigational tab label / accordion disclosure button.
184
+
185
+ ### Vertical / Justified / Scrollable
186
+
187
+ ```html
188
+ <hub-panels vertical> … </hub-panels>
189
+ <hub-panels justified> … </hub-panels>
190
+ <div style="max-width: 360px">
191
+ <hub-panels scrollable> … many panels … </hub-panels>
192
+ </div>
193
+ ```
194
+
195
+ ### Reactive forms
196
+
197
+ ```html
198
+ <hub-panels [formControl]="selected">
199
+ <hub-panel heading="Light" value="light">…</hub-panel>
200
+ <hub-panel heading="Dark" value="dark">…</hub-panel>
201
+ </hub-panels>
202
+ ```
203
+
204
+ ```ts
205
+ selected = new FormControl<string>('dark');
206
+ ```
207
+
208
+ With `multiple`, the form value is an array. Use `bindValue="meta.key"` to map
209
+ each panel's `value` object to a primitive, and `compareWith` for custom equality.
210
+
211
+ ### Multiple selection
212
+
213
+ Add `multiple` to let several panels be open at once. In the `tabs` / `pills`
214
+ views the open panes render side by side (or stacked, when `vertical`), each at
215
+ least `--hub-panels-pane-min-width` wide; the content area scrolls when they
216
+ overflow. In the `accordion` view every selected panel expands.
217
+
218
+ ```html
219
+ <hub-panels multiple [formControl]="open">
220
+ <hub-panel heading="Summary" value="summary">…</hub-panel>
221
+ <hub-panel heading="Stats" value="stats">…</hub-panel>
222
+ <hub-panel heading="Activity" value="activity">…</hub-panel>
223
+ </hub-panels>
224
+ ```
225
+
226
+ ```ts
227
+ open = new FormControl<string[]>(['summary', 'stats']);
228
+ ```
229
+
230
+ ### Routed panels
231
+
232
+ ```html
233
+ <hub-panels>
234
+ <hub-panel heading="Profile" routerLink="/account/profile">Loaded via router-outlet</hub-panel>
235
+ <hub-panel heading="Billing" routerLink="/account/billing">Loaded via router-outlet</hub-panel>
236
+ </hub-panels>
237
+ ```
238
+
239
+ When the active panel is routed, the content area renders a `<router-outlet>` and
240
+ the active panel follows the current URL (`tabs` / `pills` views only).
241
+
242
+ ### Custom headers
243
+
244
+ ```html
245
+ <hub-panel>
246
+ <ng-template hubPanelHeading>
247
+ <i class="fa-solid fa-gear"></i> Settings <span class="badge text-bg-primary">3</span>
248
+ </ng-template>
249
+ Panel content
250
+ </hub-panel>
251
+ ```
252
+
253
+ ---
254
+
255
+ ## πŸͺ„ API Reference
256
+
257
+ ### `<hub-panels>` inputs
258
+
259
+ | Input | Type | Default | Description |
260
+ | --- | --- | --- | --- |
261
+ | `type` | `'tabs' \| 'pills' \| 'accordion' \| 'card'` | `'tabs'` | Visualization of the container. `card` removes the strip and shows every panel as a card. |
262
+ | `vertical` | `boolean` | `false` | Stacks the strip beside the content (tabs / pills). |
263
+ | `justified` | `boolean` | `false` | Stretches headers to equal width. |
264
+ | `scrollable` | `boolean` | `false` | Adds scroll buttons when the strip overflows. |
265
+ | `isKeysAllowed` | `boolean` | `true` | Enables keyboard navigation. |
266
+ | `multiple` | `boolean` | `false` | Accordion: allow several panels expanded at once. |
267
+ | `flush` | `boolean` | `false` | Accordion: edge-to-edge layout without outer chrome. |
268
+ | `bindValue` | `string` | `undefined` | Dot-notation path applied to each panel value. |
269
+ | `compareWith` | `(a, b) => boolean` | `===` | Equality used to match form values. |
270
+
271
+ ### `<hub-panels>` outputs
272
+
273
+ | Output | Payload | Description |
274
+ | --- | --- | --- |
275
+ | `panelChange` | `PanelChangeEvent` | Emitted when a different panel is opened (`{ current, prev }`). |
276
+
277
+ ### `<hub-panel>` inputs
278
+
279
+ | Input | Type | Default | Description |
280
+ | --- | --- | --- | --- |
281
+ | `heading` | `string` | `undefined` | Plain-text header (ignored when a `hubPanelHeading` template is present). |
282
+ | `id` | `string` | auto | ARIA pairing id. |
283
+ | `value` | `unknown` | `id` | Value contributed to the form control. |
284
+ | `active` | `boolean` (model) | `false` | Two-way active/expanded state. |
285
+ | `disabled` | `boolean` | `false` | Prevents activation. |
286
+ | `removable` | `boolean` | `false` | Shows a βœ• and enables the Delete key. |
287
+ | `routerLink` | `string \| string[]` | `undefined` | Turns the panel into a routed panel. |
288
+ | `queryParams` | `Params` | `undefined` | Query params for `routerLink`. |
289
+ | `pathMatch` | `'route' \| 'full'` | `'route'` | URL comparison for routed panels. |
290
+ | `customClass` | `string` | `undefined` | Extra classes on the nav item and pane. |
291
+
292
+ ### `<hub-panel>` outputs
293
+
294
+ | Output | Payload | Description |
295
+ | --- | --- | --- |
296
+ | `selectPanel` | `PanelComponent` | Emitted when the panel becomes active. |
297
+ | `deselectPanel` | `PanelComponent` | Emitted when the panel stops being active. |
298
+ | `removed` | `PanelComponent` | Emitted on removal (βœ• or Delete). |
299
+
300
+ ### Directives
301
+
302
+ - `hubPanelHeading` β€” marks an `<ng-template>` inside a `hub-panel` as its custom **navigational** header (tab/pill link or accordion disclosure button).
303
+ - `hubPanelHeader` β€” marks an element inside a `hub-panel` as the content **header** band, rendered at the top of the panel body in every view.
304
+ - `hubPanelFooter` β€” marks an element inside a `hub-panel` as the content **footer** band, rendered at the bottom of the panel body in every view.
305
+
306
+ ### Configuration
307
+
308
+ Provide `PanelsConfig` to change defaults application-wide:
309
+
310
+ ```ts
311
+ providers: [{ provide: PanelsConfig, useValue: { ...new PanelsConfig(), type: 'pills' } }];
312
+ ```
313
+
314
+ ---
315
+
316
+ ## 🎨 Styling
317
+
318
+ Everything is themed through `--hub-panels-*` CSS custom properties. See
319
+ [`docs/css-variables-reference.md`](./docs/css-variables-reference.md) for the full list.
320
+
321
+ ```css
322
+ hub-panels {
323
+ --hub-panels-tab-color-active: #198754;
324
+ --hub-panels-pill-bg-active: #198754;
325
+ }
326
+ ```
327
+
328
+ The `card` view and the header/footer bands have their own tokens
329
+ (`--hub-panels-card-*`, `--hub-panels-card-gap`, `--hub-panels-panel-header-*`).
330
+ Because a standalone `<hub-panel>` has no `.hub-panels` ancestor, target the panel
331
+ itself when theming standalone cards:
332
+
333
+ ```css
334
+ hub-panel {
335
+ --hub-panels-card-border-radius: 0.75rem;
336
+ --hub-panels-panel-header-bg: #eef2ff;
337
+ }
338
+ ```
339
+
340
+ The accordion view also reads the `--hub-accordion-*` contract, so themes written
341
+ for `ng-hub-ui-accordion` keep working.
342
+
343
+ ---
344
+
345
+ ## β™Ώ Accessibility
346
+
347
+ - `tabs` / `pills`: `role="tablist"`, `role="tab"`, `role="tabpanel"`, roving tabindex and `aria-selected`.
348
+ - `accordion`: a disclosure button per panel with `aria-expanded` / `aria-controls` and an inert collapsed region.
349
+ - Keyboard: Arrow keys, Home, End move focus; Delete removes a `removable` panel; Enter/Space toggle accordion headers.
350
+
351
+ ---
352
+
353
+ ## πŸ“š Migration from `ng-hub-ui-accordion`
354
+
355
+ `ng-hub-ui-panels` replaces `ng-hub-ui-accordion`. Map the markup as follows:
356
+
357
+ | Accordion | Panels |
358
+ | --- | --- |
359
+ | `<hub-accordion [multiple]="true">` | `<hub-panels type="accordion" multiple>` |
360
+ | `<hub-accordion [options]="{ flush: true }">` | `<hub-panels type="accordion" flush>` |
361
+ | `<hub-accordion-panel title="…">` | `<hub-panel heading="…">` |
362
+ | `<ng-template hubAccordionPanelHeader>` | `<ng-template hubPanelHeading>` |
363
+ | `(collapsedChange)` | `(panelChange)` |
364
+
365
+ Form binding (`formControl` / `ngModel`, `value`, `bindValue`, `compareWith`) works
366
+ the same. Existing `--hub-accordion-*` theme overrides keep applying.
367
+
368
+ ---
369
+
370
+ ## πŸ“Š Changelog
371
+
372
+ See [CHANGELOG.md](./CHANGELOG.md).
373
+
374
+ ---
375
+
376
+ ## πŸ“„ License
377
+
378
+ MIT Β© Carlos Morcillo