@egose/shadcn-theme-ng-tw 0.1.0 → 0.3.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.
Files changed (165) hide show
  1. package/README.md +2 -2
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert/fesm2022/alert.mjs +1 -1
  5. package/alert-dialog/README.md +471 -5
  6. package/aspect-ratio/README.md +272 -5
  7. package/autocomplete/README.md +502 -2
  8. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  9. package/avatar/README.md +357 -5
  10. package/badge/README.md +318 -2
  11. package/basic-alert/README.md +353 -2
  12. package/breadcrumb/README.md +406 -5
  13. package/button/README.md +482 -2
  14. package/button/fesm2022/button.mjs +85 -107
  15. package/button/types/button.d.ts +5 -8
  16. package/button-group/README.md +318 -5
  17. package/button-group/fesm2022/button-group.mjs +1 -1
  18. package/calendar/README.md +357 -2
  19. package/card/README.md +331 -5
  20. package/carousel/README.md +333 -5
  21. package/carousel/fesm2022/carousel.mjs +4 -1
  22. package/checkbox/README.md +320 -2
  23. package/checkbox/fesm2022/checkbox.mjs +6 -7
  24. package/checkbox/types/checkbox.d.ts +1 -1
  25. package/collapsible/README.md +332 -5
  26. package/combobox/README.md +507 -5
  27. package/combobox/fesm2022/combobox.mjs +5 -2
  28. package/command/README.md +435 -5
  29. package/confirmation-dialog/README.md +301 -2
  30. package/context-menu/README.md +366 -5
  31. package/date-picker/README.md +469 -2
  32. package/date-picker/fesm2022/date-picker.mjs +150 -32
  33. package/date-picker/types/date-picker.d.ts +102 -9
  34. package/dialog/README.md +448 -2
  35. package/drawer/README.md +395 -5
  36. package/dropdown-menu/README.md +417 -5
  37. package/empty/README.md +329 -5
  38. package/field/README.md +385 -5
  39. package/form-autocomplete/README.md +177 -0
  40. package/form-autocomplete/fesm2022/form-autocomplete.mjs +211 -0
  41. package/form-autocomplete/package.json +24 -0
  42. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  43. package/form-checkbox/README.md +322 -2
  44. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  45. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  46. package/form-combobox/README.md +202 -0
  47. package/form-combobox/fesm2022/form-combobox.mjs +273 -0
  48. package/form-combobox/package.json +24 -0
  49. package/form-combobox/types/form-combobox.d.ts +73 -0
  50. package/form-date-picker/README.md +348 -2
  51. package/form-date-picker/fesm2022/form-date-picker.mjs +60 -16
  52. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  53. package/form-date-picker-multi/README.md +191 -0
  54. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +239 -0
  55. package/form-date-picker-multi/package.json +24 -0
  56. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  57. package/form-date-range-picker/README.md +253 -0
  58. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +236 -0
  59. package/form-date-range-picker/package.json +24 -0
  60. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  61. package/form-field/README.md +356 -2
  62. package/form-field-simple/README.md +340 -2
  63. package/form-input-otp/README.md +194 -0
  64. package/form-input-otp/fesm2022/form-input-otp.mjs +194 -0
  65. package/form-input-otp/package.json +24 -0
  66. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  67. package/form-month-year-picker/README.md +188 -0
  68. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +229 -0
  69. package/form-month-year-picker/package.json +24 -0
  70. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  71. package/form-native-select/README.md +205 -0
  72. package/form-native-select/fesm2022/form-native-select.mjs +187 -0
  73. package/form-native-select/package.json +24 -0
  74. package/form-native-select/types/form-native-select.d.ts +57 -0
  75. package/form-phone-input/README.md +188 -0
  76. package/form-phone-input/fesm2022/form-phone-input.mjs +203 -0
  77. package/form-phone-input/package.json +24 -0
  78. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  79. package/form-radio-group/README.md +198 -0
  80. package/form-radio-group/fesm2022/form-radio-group.mjs +211 -0
  81. package/form-radio-group/package.json +24 -0
  82. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  83. package/form-searchable-multiselect/README.md +371 -2
  84. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  85. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  86. package/form-select/README.md +360 -2
  87. package/form-select/fesm2022/form-select.mjs +24 -15
  88. package/form-select/types/form-select.d.ts +18 -1
  89. package/form-slider/README.md +182 -0
  90. package/form-slider/fesm2022/form-slider.mjs +186 -0
  91. package/form-slider/package.json +24 -0
  92. package/form-slider/types/form-slider.d.ts +60 -0
  93. package/form-switch/README.md +173 -0
  94. package/form-switch/fesm2022/form-switch.mjs +178 -0
  95. package/form-switch/package.json +24 -0
  96. package/form-switch/types/form-switch.d.ts +50 -0
  97. package/form-text-input/README.md +381 -2
  98. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  99. package/form-text-input/types/form-text-input.d.ts +18 -1
  100. package/form-textarea/README.md +357 -2
  101. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  102. package/form-textarea/types/form-textarea.d.ts +18 -1
  103. package/form-toggle/README.md +186 -0
  104. package/form-toggle/fesm2022/form-toggle.mjs +241 -0
  105. package/form-toggle/package.json +24 -0
  106. package/form-toggle/types/form-toggle.d.ts +82 -0
  107. package/form-toggle-group/README.md +176 -0
  108. package/form-toggle-group/fesm2022/form-toggle-group.mjs +216 -0
  109. package/form-toggle-group/package.json +24 -0
  110. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  111. package/hover-card/README.md +256 -5
  112. package/icon/README.md +239 -2
  113. package/input/README.md +269 -2
  114. package/input-group/README.md +335 -5
  115. package/input-group/fesm2022/input-group.mjs +23 -13
  116. package/input-group/types/input-group.d.ts +4 -1
  117. package/input-otp/README.md +375 -5
  118. package/item/README.md +385 -5
  119. package/item/fesm2022/item.mjs +3 -3
  120. package/kbd/README.md +291 -5
  121. package/label/README.md +272 -2
  122. package/layout-simple/README.md +193 -2
  123. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  124. package/layout-simple/types/layout-simple.d.ts +174 -137
  125. package/menu/README.md +417 -2
  126. package/menubar/README.md +343 -5
  127. package/native-select/README.md +323 -5
  128. package/native-select/fesm2022/native-select.mjs +18 -6
  129. package/native-select/types/native-select.d.ts +7 -2
  130. package/navigation-menu/README.md +369 -5
  131. package/package.json +57 -1
  132. package/pagination/README.md +388 -5
  133. package/phone-input/README.md +114 -0
  134. package/phone-input/fesm2022/phone-input.mjs +191 -0
  135. package/phone-input/package.json +24 -0
  136. package/phone-input/types/phone-input.d.ts +67 -0
  137. package/popover/README.md +331 -2
  138. package/progress/README.md +311 -5
  139. package/radio-group/README.md +364 -2
  140. package/radio-group/fesm2022/radio-group.mjs +5 -1
  141. package/resizable/README.md +269 -5
  142. package/scroll-area/README.md +233 -5
  143. package/searchable-multiselect/README.md +323 -2
  144. package/select/README.md +437 -2
  145. package/separator/README.md +222 -2
  146. package/sheet/README.md +311 -2
  147. package/sheet/fesm2022/sheet.mjs +1 -1
  148. package/sidebar/README.md +457 -5
  149. package/skeleton/README.md +217 -5
  150. package/slider/README.md +273 -5
  151. package/slider/fesm2022/slider.mjs +17 -13
  152. package/sonner/README.md +346 -2
  153. package/spinner/README.md +284 -2
  154. package/switch/README.md +310 -2
  155. package/switch/fesm2022/switch.mjs +7 -5
  156. package/switch/types/switch.d.ts +2 -1
  157. package/table/README.md +423 -5
  158. package/tabs/README.md +411 -2
  159. package/tabs/fesm2022/tabs.mjs +12 -2
  160. package/textarea/README.md +282 -5
  161. package/toggle/README.md +270 -5
  162. package/toggle-group/README.md +340 -5
  163. package/tooltip/README.md +269 -2
  164. package/typography/README.md +271 -5
  165. package/utils/README.md +303 -2
package/tabs/README.md CHANGED
@@ -1,3 +1,412 @@
1
- # Tabs Subpath
1
+ # Tabs (`@egose/shadcn-theme-ng/tabs`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/tabs` or `@egose/shadcn-theme-ng-tw/tabs`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ Tabbed navigation, equivalent to [shadcn/ui Tabs](https://ui.shadcn.com/docs/components/tabs). This subpath ships six pieces — `HlmTabs` (root), `HlmTabsList` (tab bar, with `default`/`line` variants), `HlmTabsTrigger` (individual tab), `HlmTabsContent` (panel), `HlmTabsContentLazy` (deferred panel), and `HlmTabsPaginatedList` (scrollable tab bar with chevron paginators) — all thin shadcn-styled directives/components over spartan-ng's `@spartan-ng/brain/tabs` primitives (`BrnTabs`, `BrnTabsList`, `BrnTabsTrigger`, `BrnTabsContent`, `BrnTabsPaginatedList`).
4
+
5
+ > **Ships as:** `@egose/shadcn-theme-ng/tabs` and `@egose/shadcn-theme-ng-tw/tabs` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Plain Tailwind (no prefix)
11
+ npm install @egose/shadcn-theme-ng
12
+
13
+ # Or the tw:-prefixed variant
14
+ npm install @egose/shadcn-theme-ng-tw
15
+ ```
16
+
17
+ `@spartan-ng/brain` arrives transitively. See the [package README](../../README.md) for the full peer-dependency table.
18
+
19
+ ## Imports
20
+
21
+ All public symbols are re-exported from `projects/tabs/src/public-api.ts`:
22
+
23
+ ```ts
24
+ import {
25
+ HlmTabs,
26
+ HlmTabsContent,
27
+ HlmTabsContentLazy,
28
+ HlmTabsImports,
29
+ HlmTabsList,
30
+ HlmTabsModule,
31
+ HlmTabsPaginatedList,
32
+ HlmTabsTrigger,
33
+ } from '@egose/shadcn-theme-ng/tabs';
34
+ // tw variant: swap to '@egose/shadcn-theme-ng-tw/tabs'
35
+ ```
36
+
37
+ Standalone-component usage (preferred):
38
+
39
+ ```ts
40
+ import { Component } from '@angular/core';
41
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
42
+
43
+ @Component({
44
+ selector: 'app-demo',
45
+ standalone: true,
46
+ imports: [...HlmTabsImports],
47
+ template: `<!-- tabs markup here -->`,
48
+ })
49
+ export class DemoComponent {}
50
+ ```
51
+
52
+ NgModule usage:
53
+
54
+ ```ts
55
+ import { NgModule } from '@angular/core';
56
+ import { HlmTabsModule } from '@egose/shadcn-theme-ng/tabs';
57
+
58
+ @NgModule({ imports: [HlmTabsModule] })
59
+ export class FeatureModule {}
60
+ ```
61
+
62
+ | Symbol | Kind | Description |
63
+ | ---------------------- | ------------- | ------------------------------------------------------------------- |
64
+ | `HlmTabs` | Directive | Root: `[hlmTabs], hlm-tabs` — wires `BrnTabs`. |
65
+ | `HlmTabsList` | Directive | Tab bar: `[hlmTabsList], hlm-tabs-list` — `default`/`line` variant. |
66
+ | `HlmTabsTrigger` | Directive | Tab button: `[hlmTabsTrigger]` — value key via `hlmTabsTrigger`. |
67
+ | `HlmTabsContent` | Directive | Panel: `[hlmTabsContent]` — value key via `hlmTabsContent`. |
68
+ | `HlmTabsContentLazy` | Directive | Deferred panel: `ng-template[hlmTabsContentLazy]`. |
69
+ | `HlmTabsPaginatedList` | Component | Scrollable bar: `<hlm-paginated-tabs-list>` with chevron buttons. |
70
+ | `HlmTabsImports` | `const` array | All six, spread into `imports: [...]`. |
71
+ | `HlmTabsModule` | NgModule | Imports + re-exports all six. |
72
+
73
+ ## Anatomy / Structure
74
+
75
+ ```html
76
+ <div hlmTabs tab="account">
77
+ <div hlmTabsList>
78
+ <button hlmTabsTrigger="account">Account</button>
79
+ <button hlmTabsTrigger="password">Password</button>
80
+ </div>
81
+
82
+ <div hlmTabsContent="account">
83
+ <p>Account settings…</p>
84
+ </div>
85
+ <div hlmTabsContent="password">
86
+ <p>Change your password…</p>
87
+ </div>
88
+ </div>
89
+ ```
90
+
91
+ Paginated (many tabs, horizontal scroll + chevrons):
92
+
93
+ ```html
94
+ <div hlmTabs tab="tab1">
95
+ <hlm-paginated-tabs-list>
96
+ <button hlmTabsTrigger="tab1">Tab 1</button>
97
+ <button hlmTabsTrigger="tab2">Tab 2</button>
98
+ <!-- …many more… -->
99
+ </hlm-paginated-tabs-list>
100
+
101
+ <div hlmTabsContent="tab1">Panel 1</div>
102
+ <div hlmTabsContent="tab2">Panel 2</div>
103
+ </div>
104
+ ```
105
+
106
+ Lazy panel (content instantiated only when the tab is first activated):
107
+
108
+ ```html
109
+ <div hlmTabs tab="reports">
110
+ <div hlmTabsList>
111
+ <button hlmTabsTrigger="reports">Reports</button>
112
+ </div>
113
+ <ng-template hlmTabsContentLazy>
114
+ <div hlmTabsContent="reports">Heavy report grid…</div>
115
+ </ng-template>
116
+ </div>
117
+ ```
118
+
119
+ > Trigger keys (`hlmTabsTrigger="…"`) must match panel keys (`hlmTabsContent="…"`), and one of them must equal the root `tab` input for an initially active tab.
120
+
121
+ ## API reference
122
+
123
+ ### `HlmTabs` — selector `[hlmTabs], hlm-tabs` (directive, hosts `BrnTabs`)
124
+
125
+ | Input | Type | Default | Description |
126
+ | ---------------- | --------------------------------------- | ------- | ------------------------------------------------------------------ |
127
+ | `tab` | `string` (required) | — | Key of the initially active tab. Must match a trigger/content key. |
128
+ | `orientation` | forwarded to `BrnTabs` | — | `'horizontal' \| 'vertical'`. |
129
+ | `activationMode` | forwarded to `BrnTabs` | — | `'automatic' \| 'manual'`. |
130
+ | `brnTabs` | forwarded to `BrnTabs` (`brnTabs: tab`) | — | Low-level Brn binding for the active tab. Prefer `tab`. |
131
+
132
+ | Output | Description |
133
+ | -------------- | ------------------------------------------------------------ |
134
+ | `tabActivated` | Forwarded from `BrnTabs`. Emits when the active tab changes. |
135
+
136
+ Host: `data-slot="tabs"`, layout `flex gap-2` (column in horizontal orientation).
137
+
138
+ ### `HlmTabsList` — selector `[hlmTabsList], hlm-tabs-list` (directive, hosts `BrnTabsList`)
139
+
140
+ | Input | Type | Default | Description |
141
+ | --------- | --------------------- | ----------- | ----------------------------------------------------------------------------- |
142
+ | `variant` | `'default' \| 'line'` | `'default'` | `default`: muted pill bar. `line`: transparent bar with underline indicators. |
143
+
144
+ Also exports `listVariants` (cva) for reuse. Host sets `data-slot="tabs-list"` + `data-variant`.
145
+
146
+ ### `HlmTabsTrigger` — selector `[hlmTabsTrigger]` (directive, hosts `BrnTabsTrigger`)
147
+
148
+ | Input | Type | Default | Description |
149
+ | ---------------- | -------------------------------------------- | ------- | ------------------------------------------- |
150
+ | `hlmTabsTrigger` | `string` (required, aliased as `triggerFor`) | — | Tab key. Must match a `hlmTabsContent` key. |
151
+ | `disabled` | forwarded to `BrnTabsTrigger` | — | Disables this tab. |
152
+
153
+ Host: `data-slot="tabs-trigger"`. Only use on `<button>` elements.
154
+
155
+ ### `HlmTabsContent` — selector `[hlmTabsContent]` (directive, hosts `BrnTabsContent`)
156
+
157
+ | Input | Type | Default | Description |
158
+ | ---------------- | -------------------------------------------- | ------- | ------------------------------------ |
159
+ | `hlmTabsContent` | `string` (required, aliased as `contentFor`) | — | Panel key. Must match a trigger key. |
160
+
161
+ Host: `data-slot="tabs-content"`, `flex-1 text-sm`.
162
+
163
+ ### `HlmTabsContentLazy` — selector `ng-template[hlmTabsContentLazy]` (directive, hosts `BrnTabsContentLazy`)
164
+
165
+ No inputs/outputs. Wrap a `hlmTabsContent` panel in `<ng-template hlmTabsContentLazy>` to defer instantiation until first activation.
166
+
167
+ ### `HlmTabsPaginatedList` — selector `hlm-paginated-tabs-list` (component, extends `BrnTabsPaginatedList`)
168
+
169
+ Scrollable tab bar with previous/next chevron paginators (uses `buttonVariants({ variant: 'ghost', size: 'icon-sm' })` internally).
170
+
171
+ | Input | Type | Default | Description |
172
+ | ----------------------- | ------------ | ------- | ----------------------------------------------------------------------------- |
173
+ | `tabListClass` | `ClassValue` | `''` | Extra classes merged onto the inner tab-list element (over `listVariants()`). |
174
+ | `paginationButtonClass` | `ClassValue` | `''` | Extra classes merged onto the chevron paginator buttons. |
175
+
176
+ Content children: `BrnTabsTrigger` items are queried (`contentChildren`) to drive pagination. All other `BrnTabsPaginatedList` behavior (scroll, keyboard) is inherited.
177
+
178
+ ## Examples
179
+
180
+ ### 1. Basic tabs
181
+
182
+ ```ts
183
+ // demo-basic.component.ts
184
+ import { Component } from '@angular/core';
185
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
186
+
187
+ @Component({
188
+ selector: 'demo-basic',
189
+ standalone: true,
190
+ imports: [...HlmTabsImports],
191
+ template: `
192
+ <div hlmTabs tab="account">
193
+ <div hlmTabsList>
194
+ <button hlmTabsTrigger="account">Account</button>
195
+ <button hlmTabsTrigger="password">Password</button>
196
+ </div>
197
+ <div hlmTabsContent="account">
198
+ <p class="text-sm">Make changes to your account here.</p>
199
+ </div>
200
+ <div hlmTabsContent="password">
201
+ <p class="text-sm">Change your password here.</p>
202
+ </div>
203
+ </div>
204
+ `,
205
+ })
206
+ export class DemoBasic {}
207
+ ```
208
+
209
+ ### 2. `line` variant + vertical orientation
210
+
211
+ ```ts
212
+ // demo-variants.component.ts
213
+ import { Component } from '@angular/core';
214
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
215
+
216
+ @Component({
217
+ selector: 'demo-variants',
218
+ standalone: true,
219
+ imports: [...HlmTabsImports],
220
+ template: `
221
+ <div hlmTabs tab="overview" orientation="vertical" class="min-h-48">
222
+ <div hlmTabsList variant="line">
223
+ <button hlmTabsTrigger="overview">Overview</button>
224
+ <button hlmTabsTrigger="analytics">Analytics</button>
225
+ <button hlmTabsTrigger="reports" disabled>Reports (soon)</button>
226
+ </div>
227
+ <div hlmTabsContent="overview">Overview panel.</div>
228
+ <div hlmTabsContent="analytics">Analytics panel.</div>
229
+ <div hlmTabsContent="reports">Reports panel.</div>
230
+ </div>
231
+ `,
232
+ })
233
+ export class DemoVariants {}
234
+ ```
235
+
236
+ ### 3. Programmatic control + `tabActivated`
237
+
238
+ Drive the active tab from a signal and react to user changes:
239
+
240
+ ```ts
241
+ // demo-controlled.component.ts
242
+ import { Component, signal } from '@angular/core';
243
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
244
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
245
+
246
+ @Component({
247
+ selector: 'demo-controlled',
248
+ standalone: true,
249
+ imports: [...HlmTabsImports, ...HlmButtonImports],
250
+ template: `
251
+ <div hlmTabs [tab]="active()" (tabActivated)="active.set($event)">
252
+ <div hlmTabsList>
253
+ <button hlmTabsTrigger="one">One</button>
254
+ <button hlmTabsTrigger="two">Two</button>
255
+ <button hlmTabsTrigger="three">Three</button>
256
+ </div>
257
+ <div hlmTabsContent="one">Panel one.</div>
258
+ <div hlmTabsContent="two">Panel two.</div>
259
+ <div hlmTabsContent="three">Panel three.</div>
260
+ </div>
261
+ <div class="mt-2 flex gap-2">
262
+ <button hlmBtn size="sm" (click)="active.set('two')">Go to Two</button>
263
+ <button hlmBtn size="sm" variant="outline" (click)="active.set('three')">Go to Three</button>
264
+ </div>
265
+ `,
266
+ })
267
+ export class DemoControlled {
268
+ readonly active = signal('one');
269
+ }
270
+ ```
271
+
272
+ ### 4. Forms inside tab panels
273
+
274
+ Each panel is a plain container — put full forms inside:
275
+
276
+ ```ts
277
+ // demo-forms.component.ts
278
+ import { Component } from '@angular/core';
279
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
280
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
281
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
282
+ import { HlmLabel } from '@egose/shadcn-theme-ng/label';
283
+
284
+ @Component({
285
+ selector: 'demo-forms',
286
+ standalone: true,
287
+ imports: [...HlmTabsImports, ReactiveFormsModule, HlmInput, HlmLabel],
288
+ template: `
289
+ <div hlmTabs tab="profile">
290
+ <div hlmTabsList>
291
+ <button hlmTabsTrigger="profile">Profile</button>
292
+ <button hlmTabsTrigger="security">Security</button>
293
+ </div>
294
+ <form [formGroup]="form">
295
+ <div hlmTabsContent="profile" class="space-y-2">
296
+ <label hlmLabel for="name">Name</label>
297
+ <input hlmInput id="name" formControlName="name" />
298
+ </div>
299
+ <div hlmTabsContent="security" class="space-y-2">
300
+ <label hlmLabel for="pw">New password</label>
301
+ <input hlmInput id="pw" type="password" formControlName="password" />
302
+ </div>
303
+ </form>
304
+ </div>
305
+ `,
306
+ })
307
+ export class DemoForms {
308
+ readonly form = new FormGroup({ name: new FormControl('Ada'), password: new FormControl('') });
309
+ }
310
+ ```
311
+
312
+ ### 5. Paginated tab bar (many tabs)
313
+
314
+ ```ts
315
+ // demo-paginated.component.ts
316
+ import { Component } from '@angular/core';
317
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
318
+
319
+ @Component({
320
+ selector: 'demo-paginated',
321
+ standalone: true,
322
+ imports: [...HlmTabsImports],
323
+ template: `
324
+ <div hlmTabs tab="tab1">
325
+ <hlm-paginated-tabs-list tabListClass="max-w-xl">
326
+ @for (t of tabs; track t) {
327
+ <button [hlmTabsTrigger]="t">{{ t }}</button>
328
+ }
329
+ </hlm-paginated-tabs-list>
330
+ @for (t of tabs; track t) {
331
+ <div [hlmTabsContent]="t">Content for {{ t }}</div>
332
+ }
333
+ </div>
334
+ `,
335
+ })
336
+ export class DemoPaginated {
337
+ readonly tabs = Array.from({ length: 20 }, (_, i) => `tab${i + 1}`);
338
+ }
339
+ ```
340
+
341
+ Paginator buttons hide automatically when everything fits (`showPaginationControls()`), and accept `paginationButtonClass` overrides.
342
+
343
+ ### 6. Lazy + async panel content
344
+
345
+ ```ts
346
+ // demo-lazy.component.ts
347
+ import { Component, inject, signal } from '@angular/core';
348
+ import { HttpClient } from '@angular/common/http';
349
+ import { HlmTabsImports } from '@egose/shadcn-theme-ng/tabs';
350
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
351
+
352
+ @Component({
353
+ selector: 'demo-lazy',
354
+ standalone: true,
355
+ imports: [...HlmTabsImports, ...HlmSpinnerImports],
356
+ template: `
357
+ <div hlmTabs tab="summary">
358
+ <div hlmTabsList>
359
+ <button hlmTabsTrigger="summary">Summary</button>
360
+ <button hlmTabsTrigger="reports">Reports</button>
361
+ </div>
362
+ <div hlmTabsContent="summary">Instant summary.</div>
363
+ <ng-template hlmTabsContentLazy>
364
+ <div hlmTabsContent="reports">
365
+ @if (loading()) {
366
+ <hlm-spinner size="1.25rem" />
367
+ } @else {
368
+ <p class="text-sm">{{ report() }}</p>
369
+ }
370
+ </div>
371
+ </ng-template>
372
+ </div>
373
+ `,
374
+ })
375
+ export class DemoLazy {
376
+ private readonly http = inject(HttpClient);
377
+ readonly loading = signal(true);
378
+ readonly report = signal<string | null>(null);
379
+
380
+ constructor() {
381
+ this.http.get('/api/report', { responseType: 'text' }).subscribe({
382
+ next: (v) => {
383
+ this.report.set(v);
384
+ this.loading.set(false);
385
+ },
386
+ error: () => {
387
+ this.report.set('Failed to load.');
388
+ this.loading.set(false);
389
+ },
390
+ });
391
+ }
392
+ }
393
+ ```
394
+
395
+ ## Accessibility notes
396
+
397
+ - Triggers are real `<button>` elements wired to `role="tab"` semantics by `BrnTabsTrigger`; panels get `role="tabpanel"`. Keep that mapping 1:1 (matching keys).
398
+ - Keyboard: Arrow keys move between tabs (orientation-aware), Home/End jump to first/last. With `activationMode="manual"`, arrows move focus but Enter/Space activates — use it when panels are expensive.
399
+ - Disabled triggers (`disabled`) are skipped in keyboard navigation — use sparingly and explain why a tab is unavailable.
400
+ - Do not remove panels from the DOM conditionally with `@if` in ways that orphan the active tab; prefer the lazy directive or keep all panels mounted.
401
+ - Label the list when its purpose is ambiguous (`aria-label` on the `hlmTabsList` element).
402
+
403
+ ## Theming / CSS variables
404
+
405
+ The list bar follows `listVariants({ variant })` (`bg-muted` pill vs. transparent `line`), while triggers/panels use foreground/muted tokens with `data-active` selectors. Follows your shadcn theme automatically. Extend via `class` (merged through `classes()`) or `tabListClass`/`paginationButtonClass` on the paginated list.
406
+
407
+ ## Related subpaths
408
+
409
+ - `@egose/shadcn-theme-ng/button` — `buttonVariants` powers the paginator chevrons.
410
+ - `@egose/shadcn-theme-ng/card` — panels containing card-styled settings forms.
411
+ - `@egose/shadcn-theme-ng/input` / `@egose/shadcn-theme-ng/label` — form controls inside panels.
412
+ - `@egose/shadcn-theme-ng/spinner` — loading state for lazy/async panels.
@@ -145,7 +145,12 @@ class HlmTabsPaginatedList extends BrnTabsPaginatedList {
145
145
  </button>
146
146
 
147
147
  <div #tabListContainer class="tw:z-[1] tw:flex tw:grow tw:overflow-hidden" (keydown)="_handleKeydown($event)">
148
- <div class="tw:relative tw:grow tw:transition-transform" #tabList role="tablist" (cdkObserveContent)="_onContentChanges()">
148
+ <div
149
+ class="tw:relative tw:grow tw:transition-transform"
150
+ #tabList
151
+ role="tablist"
152
+ (cdkObserveContent)="_onContentChanges()"
153
+ >
149
154
  <div #tabListInner [class]="_tabListClass()">
150
155
  <ng-content />
151
156
  </div>
@@ -195,7 +200,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
195
200
  </button>
196
201
 
197
202
  <div #tabListContainer class="tw:z-[1] tw:flex tw:grow tw:overflow-hidden" (keydown)="_handleKeydown($event)">
198
- <div class="tw:relative tw:grow tw:transition-transform" #tabList role="tablist" (cdkObserveContent)="_onContentChanges()">
203
+ <div
204
+ class="tw:relative tw:grow tw:transition-transform"
205
+ #tabList
206
+ role="tablist"
207
+ (cdkObserveContent)="_onContentChanges()"
208
+ >
199
209
  <div #tabListInner [class]="_tabListClass()">
200
210
  <ng-content />
201
211
  </div>