@egose/shadcn-theme-ng-tw 0.1.0 → 0.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.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/avatar/README.md +357 -5
  8. package/badge/README.md +318 -2
  9. package/basic-alert/README.md +353 -2
  10. package/breadcrumb/README.md +406 -5
  11. package/button/README.md +482 -2
  12. package/button/fesm2022/button.mjs +85 -107
  13. package/button/types/button.d.ts +5 -8
  14. package/button-group/README.md +318 -5
  15. package/button-group/fesm2022/button-group.mjs +1 -1
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/collapsible/README.md +332 -5
  22. package/combobox/README.md +507 -5
  23. package/combobox/fesm2022/combobox.mjs +4 -1
  24. package/command/README.md +435 -5
  25. package/confirmation-dialog/README.md +301 -2
  26. package/context-menu/README.md +366 -5
  27. package/date-picker/README.md +465 -2
  28. package/date-picker/fesm2022/date-picker.mjs +2 -2
  29. package/dialog/README.md +448 -2
  30. package/drawer/README.md +395 -5
  31. package/dropdown-menu/README.md +417 -5
  32. package/empty/README.md +329 -5
  33. package/field/README.md +385 -5
  34. package/form-checkbox/README.md +312 -2
  35. package/form-date-picker/README.md +322 -2
  36. package/form-field/README.md +356 -2
  37. package/form-field-simple/README.md +340 -2
  38. package/form-searchable-multiselect/README.md +361 -2
  39. package/form-select/README.md +350 -2
  40. package/form-text-input/README.md +371 -2
  41. package/form-textarea/README.md +347 -2
  42. package/hover-card/README.md +256 -5
  43. package/icon/README.md +239 -2
  44. package/input/README.md +269 -2
  45. package/input-group/README.md +335 -5
  46. package/input-group/fesm2022/input-group.mjs +3 -3
  47. package/input-otp/README.md +375 -5
  48. package/item/README.md +385 -5
  49. package/item/fesm2022/item.mjs +3 -3
  50. package/kbd/README.md +291 -5
  51. package/label/README.md +272 -2
  52. package/layout-simple/README.md +193 -2
  53. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  54. package/layout-simple/types/layout-simple.d.ts +174 -137
  55. package/menu/README.md +417 -2
  56. package/menubar/README.md +343 -5
  57. package/native-select/README.md +323 -5
  58. package/navigation-menu/README.md +369 -5
  59. package/package.json +1 -1
  60. package/pagination/README.md +388 -5
  61. package/popover/README.md +331 -2
  62. package/progress/README.md +311 -5
  63. package/radio-group/README.md +364 -2
  64. package/radio-group/fesm2022/radio-group.mjs +5 -1
  65. package/resizable/README.md +269 -5
  66. package/scroll-area/README.md +233 -5
  67. package/searchable-multiselect/README.md +323 -2
  68. package/select/README.md +437 -2
  69. package/separator/README.md +222 -2
  70. package/sheet/README.md +311 -2
  71. package/sidebar/README.md +457 -5
  72. package/skeleton/README.md +217 -5
  73. package/slider/README.md +273 -5
  74. package/slider/fesm2022/slider.mjs +17 -13
  75. package/sonner/README.md +346 -2
  76. package/spinner/README.md +284 -2
  77. package/switch/README.md +310 -2
  78. package/table/README.md +423 -5
  79. package/tabs/README.md +411 -2
  80. package/tabs/fesm2022/tabs.mjs +12 -2
  81. package/textarea/README.md +282 -5
  82. package/toggle/README.md +270 -5
  83. package/toggle-group/README.md +340 -5
  84. package/tooltip/README.md +269 -2
  85. package/typography/README.md +271 -5
  86. package/utils/README.md +303 -2
@@ -1,11 +1,423 @@
1
- # DropdownMenu
1
+ # Dropdown Menu (`@egose/shadcn-theme-ng/dropdown-menu`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
3
+ A dropdown action menu in the shadcn/ui DropdownMenu style: trigger button + floating panel with items, labels, separators, shortcuts, checkbox/radio items, groups, and nested sub-menus. Equivalent to shadcn/ui `DropdownMenu` (Radix).
4
4
 
5
- ## Building
5
+ The Angular implementation is a styling layer over the Angular CDK menu primitives (`CdkMenu`, `CdkMenuTrigger`, `CdkMenuItem`, `CdkMenuItemCheckbox`, `CdkMenuItemRadio`, `CdkMenuGroup`) plus position helpers (`createMenuPosition`, `MENU_SIDE`) and focus handling from `@spartan-ng/brain/core`. An internal `HlmDropdownMenuFocusOnHover` directive moves DOM focus on hover (Radix/shadcn behavior) and is applied automatically to every item type.
6
6
 
7
- To build the library, run:
7
+ > **Ships as:** `@egose/shadcn-theme-ng/dropdown-menu` and `@egose/shadcn-theme-ng-tw/dropdown-menu`
8
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
9
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
10
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
11
+
12
+ ## Installation
8
13
 
9
14
  ```bash
10
- ng build dropdown-menu
15
+ # Plain Tailwind (no prefix)
16
+ npm install @egose/shadcn-theme-ng
17
+
18
+ # tw:-prefixed Tailwind variant
19
+ npm install @egose/shadcn-theme-ng-tw
20
+ ```
21
+
22
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
23
+ [package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
24
+ `@angular/cdk/menu`, `@egose/shadcn-theme-ng/utils`, and `@ng-icons/lucide` (check / chevron icons).
25
+
26
+ ## Imports
27
+
28
+ All symbols are exported from the subpath root (`projects/dropdown-menu/src/public-api.ts`):
29
+
30
+ ```ts
31
+ import {
32
+ HlmDropdownMenu,
33
+ HlmDropdownMenuTrigger,
34
+ HlmDropdownMenuItem,
35
+ HlmDropdownMenuCheckbox,
36
+ HlmDropdownMenuCheckboxIndicator,
37
+ HlmDropdownMenuRadio,
38
+ HlmDropdownMenuRadioIndicator,
39
+ HlmDropdownMenuLabel,
40
+ HlmDropdownMenuSeparator,
41
+ HlmDropdownMenuShortcut,
42
+ HlmDropdownMenuGroup,
43
+ HlmDropdownMenuSub,
44
+ HlmDropdownMenuSubTrigger,
45
+ HlmDropdownMenuItemSubIndicator,
46
+ provideHlmDropdownMenuConfig,
47
+ injectHlmDropdownMenuConfig,
48
+ HlmDropdownMenuImports,
49
+ HlmDropdownMenuModule,
50
+ } from '@egose/shadcn-theme-ng/dropdown-menu';
51
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/dropdown-menu'
52
+ ```
53
+
54
+ Standalone component — spread the `*Imports` array:
55
+
56
+ ```ts
57
+ import { Component } from '@angular/core';
58
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
59
+
60
+ @Component({
61
+ selector: 'app-demo',
62
+ standalone: true,
63
+ imports: [...HlmDropdownMenuImports],
64
+ template: `...`,
65
+ })
66
+ export class DemoComponent {}
67
+ ```
68
+
69
+ NgModule-based consumer — import the module:
70
+
71
+ ```ts
72
+ import { NgModule } from '@angular/core';
73
+ import { HlmDropdownMenuModule } from '@egose/shadcn-theme-ng/dropdown-menu';
74
+
75
+ @NgModule({ imports: [HlmDropdownMenuModule] })
76
+ export class DemoModule {}
77
+ ```
78
+
79
+ ## Anatomy / Structure
80
+
81
+ ```html
82
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Open</button>
83
+
84
+ <ng-template #menu>
85
+ <div hlmDropdownMenu>
86
+ <div hlmDropdownMenuLabel>My Account</div>
87
+ <div hlmDropdownMenuSeparator></div>
88
+ <div hlmDropdownMenuGroup>
89
+ <button hlmDropdownMenuItem (triggered)="profile()">
90
+ Profile
91
+ <span hlmDropdownMenuShortcut>⇧⌘P</span>
92
+ </button>
93
+ <button hlmDropdownMenuItem variant="destructive" (triggered)="logout()">Log out</button>
94
+ </div>
95
+ </div>
96
+ </ng-template>
11
97
  ```
98
+
99
+ Real selectors (from source):
100
+
101
+ | Class | Selector(s) | Kind |
102
+ | ---------------------------------- | ---------------------------------------------------------- | ----------------------------------- |
103
+ | `HlmDropdownMenu` | `[hlmDropdownMenu], hlm-dropdown-menu` | Directive (hosts `CdkMenu`) |
104
+ | `HlmDropdownMenuTrigger` | `[hlmDropdownMenuTrigger]` | Directive (hosts `CdkMenuTrigger`) |
105
+ | `HlmDropdownMenuItem` | `[hlmDropdownMenuItem], hlm-dropdown-menu-item` | Directive (hosts `CdkMenuItem`) |
106
+ | `HlmDropdownMenuCheckbox` | `[hlmDropdownMenuCheckbox], [hlmDropdownMenuCheckboxItem]` | Directive (hosts checkbox CDK item) |
107
+ | `HlmDropdownMenuCheckboxIndicator` | `hlm-dropdown-menu-checkbox-indicator` | Component (`lucideCheck`) |
108
+ | `HlmDropdownMenuRadio` | `[hlmDropdownMenuRadio]` | Directive (hosts radio CDK item) |
109
+ | `HlmDropdownMenuRadioIndicator` | `hlm-dropdown-menu-radio-indicator` | Component (`lucideCheck`) |
110
+ | `HlmDropdownMenuLabel` | `[hlmDropdownMenuLabel], hlm-dropdown-menu-label` | Directive |
111
+ | `HlmDropdownMenuSeparator` | `[hlmDropdownMenuSeparator], hlm-dropdown-menu-separator` | Directive |
112
+ | `HlmDropdownMenuShortcut` | `[hlmDropdownMenuShortcut], hlm-dropdown-menu-shortcut` | Directive |
113
+ | `HlmDropdownMenuGroup` | `[hlmDropdownMenuGroup], hlm-dropdown-menu-group` | Directive (hosts `CdkMenuGroup`) |
114
+ | `HlmDropdownMenuSub` | `[hlmDropdownMenuSub], hlm-dropdown-menu-sub` | Directive (nested `CdkMenu`) |
115
+ | `HlmDropdownMenuSubTrigger` | `[hlmDropdownMenuSubTrigger]` | Directive (hosts `CdkMenuTrigger`) |
116
+ | `HlmDropdownMenuItemSubIndicator` | `hlm-dropdown-menu-item-sub-indicator` | Component (`lucideChevronRight`) |
117
+
118
+ ## API reference
119
+
120
+ ### `HlmDropdownMenu` — `[hlmDropdownMenu], hlm-dropdown-menu`
121
+
122
+ Menu panel. Hosts `CdkMenu`. Place inside an `ng-template` referenced by the trigger.
123
+
124
+ | Input | Type | Default | Description |
125
+ | ------------ | -------- | ------- | ------------------------------------------------ |
126
+ | `sideOffset` | `number` | `1` | Gap (`--side-offset`) between trigger and panel. |
127
+
128
+ Host exposes `data-state`, `data-side`, and animation classes derived from the trigger's resolved position.
129
+
130
+ ### `HlmDropdownMenuTrigger` — `[hlmDropdownMenuTrigger]`
131
+
132
+ | Input | Type | Default | Description |
133
+ | ------- | ----------- | ------------------- | --------------------- |
134
+ | `align` | `MenuAlign` | config (`'start'`) | Horizontal alignment. |
135
+ | `side` | `MenuSide` | config (`'bottom'`) | Preferred side. |
136
+
137
+ CDK bindings (via `hostDirectives`): `cdkMenuTriggerFor: hlmDropdownMenuTrigger` (the `ng-template`), `cdkMenuTriggerData: hlmDropdownMenuTriggerData`; outputs `cdkMenuOpened: hlmDropdownMenuOpened`, `cdkMenuClosed: hlmDropdownMenuClosed`.
138
+
139
+ ### `HlmDropdownMenuItem` — `[hlmDropdownMenuItem], hlm-dropdown-menu-item`
140
+
141
+ | Input | Type | Default | Description |
142
+ | ---------- | ---------------------------- | ----------- | ------------------------------------------------------------ |
143
+ | `disabled` | `boolean` | `false` | Disables the item (forwarded to `cdkMenuItemDisabled`). |
144
+ | `variant` | `'default' \| 'destructive'` | `'default'` | Destructive red styling. |
145
+ | `inset` | `boolean` | `false` | Indents content (`ps-8`), e.g. to align with checkable rows. |
146
+
147
+ Output `triggered` (forwarded from `cdkMenuItemTriggered`) fires on select.
148
+
149
+ ### `HlmDropdownMenuCheckbox` — `[hlmDropdownMenuCheckbox], [hlmDropdownMenuCheckboxItem]`
150
+
151
+ Checkable row. Hosts an internal `HlmDropdownMenuCheckboxCdk extends CdkMenuItemCheckbox` (selector `[hlmDropdownMenuCheckboxCdk]`, internal — use the public selector instead).
152
+
153
+ | Input | Type | Default | Description |
154
+ | ---------- | --------- | ------- | ------------------------------------------------------------------------- |
155
+ | `disabled` | `boolean` | — | Forwarded to `cdkMenuItemDisabled`. |
156
+ | `checked` | `boolean` | — | Forwarded to `cdkMenuItemChecked`. Bind with `[checked]` + `(triggered)`. |
157
+ | `keepOpen` | `boolean` | `true` | Keep the menu open after toggling (CDK trigger override). |
158
+ | `inset` | `boolean` | `false` | Indentation. |
159
+
160
+ Pair with `<hlm-dropdown-menu-checkbox-indicator />` (check icon, shown when `data-checked`).
161
+
162
+ ### `HlmDropdownMenuRadio` — `[hlmDropdownMenuRadio]`
163
+
164
+ Same contract as checkbox but single-select within its group (hosts `HlmDropdownMenuRadioCdk`, selector `[hlmDropdownMenuRadioCdk]`, internal). Inputs: `disabled`, `checked`, `keepOpen` (default `true`). Pair with `<hlm-dropdown-menu-radio-indicator />`.
165
+
166
+ ### Label / separator / shortcut / group
167
+
168
+ | Class | Inputs | Notes |
169
+ | -------------------------- | ------------------------------------ | ----------------------------------------------------------- |
170
+ | `HlmDropdownMenuLabel` | `inset` (`boolean`, default `false`) | Muted section heading. |
171
+ | `HlmDropdownMenuSeparator` | — | Hairline divider. |
172
+ | `HlmDropdownMenuShortcut` | — | Right-aligned kbd hint; place inside an item. |
173
+ | `HlmDropdownMenuGroup` | — | Hosts `CdkMenuGroup` for labelled sections / radio scoping. |
174
+
175
+ ### Sub-menus
176
+
177
+ | Class | Inputs | Notes |
178
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
179
+ | `HlmDropdownMenuSubTrigger` | `align`, `side` (config defaults); CDK `hlmDropdownMenuSubTrigger` (template), `hlmDropdownMenuTriggerData`; outputs `hlmDropdownMenuSubOpened/Closed` | Row that opens a nested menu. Include `<hlm-dropdown-menu-item-sub-indicator />` chevron. |
180
+ | `HlmDropdownMenuSub` | — | Nested panel inside the sub-template. |
181
+
182
+ ### Config
183
+
184
+ `HlmDropdownMenuConfig = { align: MenuAlign; side: MenuSide }`, defaults `{ align: 'start', side: 'bottom' }`. Override globally with `provideHlmDropdownMenuConfig({ align, side })`, read with `injectHlmDropdownMenuConfig()`.
185
+
186
+ ## Examples
187
+
188
+ ### 1. Basic account menu
189
+
190
+ ```ts
191
+ import { Component } from '@angular/core';
192
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
193
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
194
+
195
+ @Component({
196
+ selector: 'app-account-menu',
197
+ standalone: true,
198
+ imports: [...HlmDropdownMenuImports, HlmButton],
199
+ template: `
200
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Open</button>
201
+ <ng-template #menu>
202
+ <div hlmDropdownMenu>
203
+ <div hlmDropdownMenuLabel>My Account</div>
204
+ <div hlmDropdownMenuSeparator></div>
205
+ <button hlmDropdownMenuItem (triggered)="go('profile')">Profile</button>
206
+ <button hlmDropdownMenuItem (triggered)="go('billing')">Billing</button>
207
+ <button hlmDropdownMenuItem variant="destructive" (triggered)="go('logout')">Log out</button>
208
+ </div>
209
+ </ng-template>
210
+ `,
211
+ })
212
+ export class AccountMenuComponent {
213
+ go(where: string) {
214
+ console.log(where);
215
+ }
216
+ }
217
+ ```
218
+
219
+ ### 2. Shortcuts, inset, and disabled items
220
+
221
+ ```ts
222
+ import { Component } from '@angular/core';
223
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
224
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
225
+
226
+ @Component({
227
+ selector: 'app-shortcuts',
228
+ standalone: true,
229
+ imports: [...HlmDropdownMenuImports, HlmButton],
230
+ template: `
231
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Edit</button>
232
+ <ng-template #menu>
233
+ <div hlmDropdownMenu>
234
+ <div hlmDropdownMenuLabel inset>Editor</div>
235
+ <button hlmDropdownMenuItem inset (triggered)="noop('undo')">
236
+ Undo <span hlmDropdownMenuShortcut>⌘Z</span>
237
+ </button>
238
+ <button hlmDropdownMenuItem inset (triggered)="noop('redo')">
239
+ Redo <span hlmDropdownMenuShortcut>⇧⌘Z</span>
240
+ </button>
241
+ <div hlmDropdownMenuSeparator></div>
242
+ <button hlmDropdownMenuItem inset disabled>Cut (disabled)</button>
243
+ </div>
244
+ </ng-template>
245
+ `,
246
+ })
247
+ export class ShortcutsComponent {
248
+ noop(what: string) {
249
+ console.log(what);
250
+ }
251
+ }
252
+ ```
253
+
254
+ ### 3. Checkbox filters (keep-open toggles)
255
+
256
+ ```ts
257
+ import { Component, signal } from '@angular/core';
258
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
259
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
260
+
261
+ @Component({
262
+ selector: 'app-filters',
263
+ standalone: true,
264
+ imports: [...HlmDropdownMenuImports, HlmButton],
265
+ template: `
266
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Filter</button>
267
+ <ng-template #menu>
268
+ <div hlmDropdownMenu>
269
+ <div hlmDropdownMenuLabel>Show</div>
270
+ <button hlmDropdownMenuCheckbox [checked]="open()" (triggered)="open.set(!open())">
271
+ Open issues
272
+ <hlm-dropdown-menu-checkbox-indicator />
273
+ </button>
274
+ <button hlmDropdownMenuCheckbox [checked]="closed()" (triggered)="closed.set(!closed())">
275
+ Closed issues
276
+ <hlm-dropdown-menu-checkbox-indicator />
277
+ </button>
278
+ </div>
279
+ </ng-template>
280
+ <p class="tw:text-sm">open={{ open() }} closed={{ closed() }}</p>
281
+ `,
282
+ })
283
+ export class FiltersComponent {
284
+ readonly open = signal(true);
285
+ readonly closed = signal(false);
286
+ }
287
+ ```
288
+
289
+ ### 4. Radio sort order
290
+
291
+ ```ts
292
+ import { Component, signal } from '@angular/core';
293
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
294
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
295
+
296
+ @Component({
297
+ selector: 'app-sort',
298
+ standalone: true,
299
+ imports: [...HlmDropdownMenuImports, HlmButton],
300
+ template: `
301
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Sort: {{ order() }}</button>
302
+ <ng-template #menu>
303
+ <div hlmDropdownMenu>
304
+ <div hlmDropdownMenuGroup>
305
+ <div hlmDropdownMenuLabel>Sort by</div>
306
+ @for (opt of options; track opt) {
307
+ <button hlmDropdownMenuRadio [checked]="order() === opt" (triggered)="order.set(opt)">
308
+ {{ opt }}
309
+ <hlm-dropdown-menu-radio-indicator />
310
+ </button>
311
+ }
312
+ </div>
313
+ </div>
314
+ </ng-template>
315
+ `,
316
+ })
317
+ export class SortComponent {
318
+ readonly options = ['Newest', 'Oldest', 'Most starred'] as const;
319
+ readonly order = signal<(typeof this.options)[number]>('Newest');
320
+ }
321
+ ```
322
+
323
+ ### 5. Nested sub-menu
324
+
325
+ ```ts
326
+ import { Component } from '@angular/core';
327
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
328
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
329
+
330
+ @Component({
331
+ selector: 'app-submenu',
332
+ standalone: true,
333
+ imports: [...HlmDropdownMenuImports, HlmButton],
334
+ template: `
335
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu">Share</button>
336
+ <ng-template #menu>
337
+ <div hlmDropdownMenu>
338
+ <button hlmDropdownMenuItem (triggered)="copy()">Copy link</button>
339
+ <button hlmDropdownMenuSubTrigger [hlmDropdownMenuSubTrigger]="invite">
340
+ Invite users
341
+ <hlm-dropdown-menu-item-sub-indicator />
342
+ </button>
343
+ </div>
344
+ </ng-template>
345
+ <ng-template #invite>
346
+ <div hlmDropdownMenuSub>
347
+ <button hlmDropdownMenuItem (triggered)="inviteBy('email')">Email</button>
348
+ <button hlmDropdownMenuItem (triggered)="inviteBy('sms')">SMS</button>
349
+ </div>
350
+ </ng-template>
351
+ `,
352
+ })
353
+ export class SubmenuComponent {
354
+ copy() {
355
+ console.log('copied');
356
+ }
357
+ inviteBy(channel: string) {
358
+ console.log(channel);
359
+ }
360
+ }
361
+ ```
362
+
363
+ ### 6. Async data menu + global position config
364
+
365
+ ```ts
366
+ import { Component, inject, signal } from '@angular/core';
367
+ import { HlmDropdownMenuImports, provideHlmDropdownMenuConfig } from '@egose/shadcn-theme-ng/dropdown-menu';
368
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
369
+
370
+ @Component({
371
+ selector: 'app-async-menu',
372
+ standalone: true,
373
+ imports: [...HlmDropdownMenuImports, HlmButton],
374
+ providers: [provideHlmDropdownMenuConfig({ align: 'end', side: 'bottom' })],
375
+ template: `
376
+ <button hlmBtn variant="outline" [hlmDropdownMenuTrigger]="menu" (click)="load()">Projects</button>
377
+ <ng-template #menu>
378
+ <div hlmDropdownMenu>
379
+ <div hlmDropdownMenuLabel>Recent</div>
380
+ @if (loading()) {
381
+ <button hlmDropdownMenuItem disabled>Loading…</button>
382
+ }
383
+ @for (p of projects(); track p) {
384
+ <button hlmDropdownMenuItem (triggered)="open(p)">{{ p }}</button>
385
+ }
386
+ </div>
387
+ </ng-template>
388
+ `,
389
+ })
390
+ export class AsyncMenuComponent {
391
+ readonly projects = signal<string[]>([]);
392
+ readonly loading = signal(false);
393
+ load() {
394
+ if (this.projects().length || this.loading()) return;
395
+ this.loading.set(true);
396
+ setTimeout(() => {
397
+ this.projects.set(['website', 'mobile', 'docs']);
398
+ this.loading.set(false);
399
+ }, 600);
400
+ }
401
+ open(p: string) {
402
+ console.log('open', p);
403
+ }
404
+ }
405
+ ```
406
+
407
+ ## Accessibility notes
408
+
409
+ - Triggers are native buttons hosting `CdkMenuTrigger`: enter/space/arrows open the menu, escape closes, and focus returns to the trigger.
410
+ - Items are `CdkMenuItem`s with roving focus: arrows move, typeahead jumps, enter/space activates. `disabled` items are skipped.
411
+ - Checkbox/radio rows expose checked state via `data-checked` + icon indicators; screen readers announce them as menu item checkboxes/radios through the CDK roles.
412
+ - Hover moves focus (`HlmDropdownMenuFocusOnHover`) so pointer and keyboard highlight stay in sync — do not add competing `mouseenter` focus handlers.
413
+
414
+ ## Theming / CSS variables
415
+
416
+ No component-specific CSS variables. Panel, item, and indicator styling derives from global tokens (`--popover`, `--accent`, `--muted`, `--destructive`, …) with `data-state` / `data-side` / `data-variant` / `data-checked` hooks for custom CSS. Tune placement globally via `provideHlmDropdownMenuConfig`.
417
+
418
+ ## Related subpaths
419
+
420
+ - `@egose/shadcn-theme-ng/context-menu` — right-click variant of the same CDK menu system.
421
+ - `@egose/shadcn-theme-ng/menubar` — menubar with the same item/label/separator vocabulary.
422
+ - `@egose/shadcn-theme-ng/popover` — generic floating panel alternative for non-menu content.
423
+ - `@egose/shadcn-theme-ng/button` — `hlmBtn` trigger styling.