@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,3 +1,302 @@
1
- # Confirmation Dialog Subpath
1
+ # Confirmation Dialog (`@egose/shadcn-theme-ng/confirmation-dialog`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/confirmation-dialog` or `@egose/shadcn-theme-ng-tw/confirmation-dialog`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A promise-based "Are you sure?" modal — the Angular equivalent of a shadcn/ui `AlertDialog` confirmation shortcut. Instead of composing dialog primitives by hand, you call `EgConfirmationDialogService.showConfirmationDialog({ title, description })` and `await` a `Promise<boolean>` (`true` = Confirm, `false` = Cancel/dismiss). The rendered `EgConfirmationDialog` component itself (warning icon + title + description + Cancel/Confirm footer) is opened through `HlmDialogService` from `@egose/shadcn-theme-ng/dialog` with a `BrnDialogRef<boolean>` result. There is no `*Imports` array and no `*Module` — the public surface is the component, the service, and a deprecated misspelled alias.
4
+
5
+ Ships as `@egose/shadcn-theme-ng/confirmation-dialog` and `@egose/shadcn-theme-ng-tw/confirmation-dialog` (the `tw:`-prefixed variant). See the [package README](../../README.md) for install, peer dependencies, and Tailwind setup. Do not publish this project directory independently — it is consumed via the published subpath only.
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
+ Peers are listed in the [package README](../../README.md). This subpath declares `@angular/common` and `@angular/core` as peers (see `projects/confirmation-dialog/package.json`); at runtime it uses `@egose/shadcn-theme-ng/dialog`, `button`, `alert`, and `icon`, so install the whole package.
18
+
19
+ ## Imports
20
+
21
+ The public API (`projects/confirmation-dialog/src/public-api.ts`) exports exactly:
22
+
23
+ ```ts
24
+ import {
25
+ EgConfirmationDialog,
26
+ EgConfirmationDialogService,
27
+ // Deprecated misspelled alias (type + const) — do not use in new code:
28
+ EgConfirmationDiaglog,
29
+ } from '@egose/shadcn-theme-ng/confirmation-dialog';
30
+ // tw variant:
31
+ // import { ... } from '@egose/shadcn-theme-ng-tw/confirmation-dialog';
32
+ ```
33
+
34
+ There is no `EgConfirmationDialogImports` and no `EgConfirmationDialogModule`. The service is `@Injectable({ providedIn: 'root' })` — just inject it:
35
+
36
+ ```ts
37
+ import { Component, inject } from '@angular/core';
38
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
39
+
40
+ @Component({ selector: 'app-demo', standalone: true, template: `...` })
41
+ export class DemoComponent {
42
+ private readonly confirm = inject(EgConfirmationDialogService);
43
+ }
44
+ ```
45
+
46
+ > `EgConfirmationDiaglog` (missing the second `a`) is a deprecated type+const alias for `EgConfirmationDialog` kept for back-compat. It will be removed in the next major release — use the correctly spelled name.
47
+
48
+ ## Anatomy / Structure
49
+
50
+ You never write the dialog markup yourself — the service opens this fixed template:
51
+
52
+ ```html
53
+ <!-- Rendered by EgConfirmationDialog (fixed internal template) -->
54
+ <hlm-dialog-header>
55
+ <div class="tw:flex tw:items-center tw:gap-1">
56
+ <ng-icon hlm hlmAlertIcon name="lucideTriangleAlert" size="base" />
57
+ <h3 hlmDialogTitle>{{ title }}</h3>
58
+ </div>
59
+ <p hlmDialogDescription>{{ description }}</p>
60
+ </hlm-dialog-header>
61
+
62
+ <hlm-dialog-footer>
63
+ <button hlmButton variant="secondary" appearance="outline">Cancel</button>
64
+ <button hlmButton variant="danger">Confirm</button>
65
+ </hlm-dialog-footer>
66
+ ```
67
+
68
+ - `EgConfirmationDialog` has **no selector** — it is instantiated by `HlmDialogService.open<boolean>(EgConfirmationDialog, { context: { title, description } })`.
69
+ - Title/description fall back to `'Are you absolutely sure?'` / `'This action cannot be undone.'` when the context omits them.
70
+ - `close(confirm: boolean)` closes the `BrnDialogRef<boolean>` with the result; Cancel resolves `false`, Confirm resolves `true`, and dismissing (X / backdrop / Escape) resolves `false` via `closed$ ?? false`.
71
+
72
+ ## API reference
73
+
74
+ ### `EgConfirmationDialogService`
75
+
76
+ `@Injectable({ providedIn: 'root' })`, constructor-injects `HlmDialogService`.
77
+
78
+ | Method | Signature | Description |
79
+ | ------------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
80
+ | `showConfirmationDialog` | `({ title, description }: { title: string; description: string }) => Promise<boolean>` | Opens the modal with the given copy; resolves `true` on Confirm, `false` on Cancel or any dismiss. |
81
+
82
+ Implementation detail: subscribes to `dialogRef.closed$` once per call and resolves `confirm ?? false`.
83
+
84
+ ### `EgConfirmationDialog`
85
+
86
+ Standalone component (no selector), opened via the dialog service.
87
+
88
+ | Member | Type | Description |
89
+ | ------- | ---------------------------- | ------------------------------------------------------------------------------------------- |
90
+ | `close` | `(confirm: boolean) => void` | Closes the dialog with the boolean result. Wired to the two footer buttons in the template. |
91
+
92
+ Reads `{ title, description }` from `injectBrnDialogContext()`.
93
+
94
+ ## Examples
95
+
96
+ ### 1. Basic delete confirmation (`async/await`)
97
+
98
+ ```ts
99
+ import { Component, inject } from '@angular/core';
100
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
101
+
102
+ @Component({
103
+ selector: 'app-delete-button',
104
+ standalone: true,
105
+ template: `<button type="button" (click)="onDelete()">Delete project</button>`,
106
+ })
107
+ export class DeleteButtonComponent {
108
+ private readonly confirm = inject(EgConfirmationDialogService);
109
+
110
+ async onDelete() {
111
+ const ok = await this.confirm.showConfirmationDialog({
112
+ title: 'Delete project?',
113
+ description: 'This will permanently delete the project and all of its data.',
114
+ });
115
+ if (ok) {
116
+ console.log('deleting…');
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ ### 2. Promise-chain style (no `async`)
123
+
124
+ ```ts
125
+ import { Component, inject } from '@angular/core';
126
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
127
+
128
+ @Component({
129
+ selector: 'app-leave-page',
130
+ standalone: true,
131
+ template: `<button type="button" (click)="leave()">Discard changes</button>`,
132
+ })
133
+ export class LeavePageComponent {
134
+ private readonly confirm = inject(EgConfirmationDialogService);
135
+
136
+ leave() {
137
+ this.confirm
138
+ .showConfirmationDialog({
139
+ title: 'Discard unsaved changes?',
140
+ description: 'Your edits will be lost if you leave this page.',
141
+ })
142
+ .then((ok) => {
143
+ if (ok) console.log('navigating away…');
144
+ });
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### 3. Destructive row action in a table
150
+
151
+ Each row awaits its own confirmation before mutating the list.
152
+
153
+ ```ts
154
+ import { Component, inject, signal } from '@angular/core';
155
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
156
+
157
+ interface Member {
158
+ id: number;
159
+ name: string;
160
+ }
161
+
162
+ @Component({
163
+ selector: 'app-members-table',
164
+ standalone: true,
165
+ template: `
166
+ <ul>
167
+ @for (m of members(); track m.id) {
168
+ <li class="flex items-center justify-between py-1">
169
+ <span>{{ m.name }}</span>
170
+ <button type="button" (click)="remove(m)">Remove</button>
171
+ </li>
172
+ }
173
+ </ul>
174
+ `,
175
+ })
176
+ export class MembersTableComponent {
177
+ private readonly confirm = inject(EgConfirmationDialogService);
178
+ readonly members = signal<Member[]>([
179
+ { id: 1, name: 'Ada' },
180
+ { id: 2, name: 'Grace' },
181
+ ]);
182
+
183
+ async remove(m: Member) {
184
+ const ok = await this.confirm.showConfirmationDialog({
185
+ title: `Remove ${m.name}?`,
186
+ description: `${m.name} will lose access immediately. You can re-invite them later.`,
187
+ });
188
+ if (ok) this.members.update((ms) => ms.filter((x) => x.id !== m.id));
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### 4. Guarding a reactive form reset
194
+
195
+ ```ts
196
+ import { Component, inject } from '@angular/core';
197
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
198
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
199
+
200
+ @Component({
201
+ selector: 'app-settings-form',
202
+ standalone: true,
203
+ imports: [ReactiveFormsModule],
204
+ template: `
205
+ <form [formGroup]="form">
206
+ <input formControlName="name" placeholder="Workspace name" />
207
+ <button type="button" (click)="reset()">Reset to defaults</button>
208
+ </form>
209
+ `,
210
+ })
211
+ export class SettingsFormComponent {
212
+ private readonly confirm = inject(EgConfirmationDialogService);
213
+ readonly form = new FormGroup({ name: new FormControl('acme') });
214
+
215
+ async reset() {
216
+ if (this.form.pristine) return;
217
+ const ok = await this.confirm.showConfirmationDialog({
218
+ title: 'Reset settings?',
219
+ description: 'All fields return to their default values.',
220
+ });
221
+ if (ok) this.form.reset({ name: 'acme' });
222
+ }
223
+ }
224
+ ```
225
+
226
+ ### 5. Sequential confirmations (two-step destructive flow)
227
+
228
+ Await twice — e.g. confirm scope, then confirm irreversibility.
229
+
230
+ ```ts
231
+ import { Component, inject } from '@angular/core';
232
+ import { EgConfirmationDialogService } from '@egose/shadcn-theme-ng/confirmation-dialog';
233
+
234
+ @Component({
235
+ selector: 'app-purge-flow',
236
+ standalone: true,
237
+ template: `<button type="button" (click)="purge()">Purge organization</button>`,
238
+ })
239
+ export class PurgeFlowComponent {
240
+ private readonly confirm = inject(EgConfirmationDialogService);
241
+
242
+ async purge() {
243
+ const step1 = await this.confirm.showConfirmationDialog({
244
+ title: 'Purge all data?',
245
+ description: 'This affects every project in the organization.',
246
+ });
247
+ if (!step1) return;
248
+
249
+ const step2 = await this.confirm.showConfirmationDialog({
250
+ title: 'Are you absolutely sure?',
251
+ description: 'This action cannot be undone. Backups are deleted too.',
252
+ });
253
+ if (step2) console.log('purging…');
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### 6. Opening the component directly (custom dialog options)
259
+
260
+ If you need non-default dialog chrome (different `contentClass`, close behavior), bypass the service and use `HlmDialogService` with `EgConfirmationDialog` directly.
261
+
262
+ ```ts
263
+ import { Component, inject } from '@angular/core';
264
+ import { EgConfirmationDialog } from '@egose/shadcn-theme-ng/confirmation-dialog';
265
+ import { HlmDialogService } from '@egose/shadcn-theme-ng/dialog';
266
+
267
+ @Component({
268
+ selector: 'app-custom-open',
269
+ standalone: true,
270
+ template: `<button type="button" (click)="open()">Review defaults</button>`,
271
+ })
272
+ export class CustomOpenComponent {
273
+ private readonly dialogs = inject(HlmDialogService);
274
+
275
+ open() {
276
+ // No title/description → component falls back to its default copy.
277
+ const ref = this.dialogs.open<boolean>(EgConfirmationDialog, {
278
+ context: { title: '', description: '' },
279
+ contentClass: 'sm:max-w-md',
280
+ });
281
+ ref.closed$.subscribe((ok) => console.log('result:', ok ?? false));
282
+ }
283
+ }
284
+ ```
285
+
286
+ ## Accessibility notes
287
+
288
+ - The dialog uses the standard `hlm-dialog` primitives (`hlmDialogTitle` + `hlmDialogDescription`), so screen readers announce title/description on open and focus is trapped/moved per the dialog service.
289
+ - Confirm is styled `variant="danger"` and Cancel `variant="secondary" appearance="outline"` — the safe action is visually distinct; keyboard order is Cancel → Confirm.
290
+ - Dismiss (Escape/backdrop/X) resolves `false` — the destructive path always requires an explicit Confirm click.
291
+ - Keep `title`/`description` specific ("Delete 'Q3 report'?") rather than generic so SR users hear the consequence without surrounding context.
292
+
293
+ ## Theming / CSS variables
294
+
295
+ No component-specific CSS variables. The icon uses `hlmAlertIcon` (warning treatment), the title is `text-xl`, the description `text-base text-gray-700`, and the footer is `mt-4`. Buttons follow the shared `hlmButton` `secondary`/`danger` variants in both themes.
296
+
297
+ ## Related subpaths
298
+
299
+ - `@egose/shadcn-theme-ng/dialog` — `HlmDialogService.open`, title/description/footer primitives behind this shortcut.
300
+ - `@egose/shadcn-theme-ng/alert-dialog` — fully custom alert dialogs when you need your own buttons/copy/layout.
301
+ - `@egose/shadcn-theme-ng/button` — `secondary`/`danger` variants used by the footer.
302
+ - `@egose/shadcn-theme-ng/alert` — `HlmAlertIcon` warning treatment for the header icon.
@@ -1,11 +1,372 @@
1
- # ContextMenu
1
+ # Context Menu (`@egose/shadcn-theme-ng/context-menu`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
3
+ A right-click menu trigger — the Angular port of shadcn/ui `Context Menu`. The entire subpath is one thin directive, `HlmContextMenuTrigger` (`[hlmContextMenuTrigger]`), which wraps the CDK `CdkContextMenuTrigger` and positions a shared menu panel via `@spartan-ng/brain/core` (`createMenuPosition`, `MENU_SIDE`). The visible menu content itself is **not** in this package — you render it with `@egose/shadcn-theme-ng/dropdown-menu` (or `menu`) primitives; this directive only owns the right-click anchoring, `align`/`side` placement, `disabled` locking, and open/close outputs.
4
4
 
5
- ## Building
5
+ Ships as `@egose/shadcn-theme-ng/context-menu` and `@egose/shadcn-theme-ng-tw/context-menu` (the `tw:`-prefixed variant). See the [package README](../../README.md) for install, peer dependencies, and Tailwind setup. Do not publish this project directory independently — it is consumed via the published subpath only.
6
6
 
7
- To build the library, run:
7
+ ## Installation
8
8
 
9
9
  ```bash
10
- ng build context-menu
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
+ Peers are listed in the [package README](../../README.md). This subpath declares `@angular/common`, `@angular/core`, and `@spartan-ng/brain` as peers (see `projects/context-menu/package.json`); at runtime it also needs `@angular/cdk/menu` and a menu-content package (`dropdown-menu`).
18
+
19
+ ## Imports
20
+
21
+ ```ts
22
+ import {
23
+ HlmContextMenuTrigger,
24
+ HlmContextMenuImports,
25
+ HlmContextMenuModule,
26
+ HlmContextMenuConfig,
27
+ provideHlmContextMenuConfig,
28
+ injectHlmContextMenuConfig,
29
+ } from '@egose/shadcn-theme-ng/context-menu';
30
+ // tw variant:
31
+ // import { ... } from '@egose/shadcn-theme-ng-tw/context-menu';
32
+ ```
33
+
34
+ Standalone:
35
+
36
+ ```ts
37
+ import { Component } from '@angular/core';
38
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
39
+
40
+ @Component({
41
+ selector: 'app-demo',
42
+ standalone: true,
43
+ imports: [...HlmContextMenuImports],
44
+ template: `...`,
45
+ })
46
+ export class DemoComponent {}
47
+ ```
48
+
49
+ NgModule:
50
+
51
+ ```ts
52
+ import { NgModule } from '@angular/core';
53
+ import { HlmContextMenuModule } from '@egose/shadcn-theme-ng/context-menu';
54
+
55
+ @NgModule({ imports: [HlmContextMenuModule] })
56
+ export class DemoModule {}
57
+ ```
58
+
59
+ ## Anatomy / Structure
60
+
61
+ ```html
62
+ <!-- Trigger area: right-click anywhere inside the div -->
63
+ <div
64
+ [hlmContextMenuTrigger]="menu"
65
+ [hlmContextMenuTriggerData]="{ id: file.id }"
66
+ (hlmContextMenuOpened)="onOpen()"
67
+ (hlmContextMenuClosed)="onClose()"
68
+ >
69
+ Right-click me
70
+ </div>
71
+
72
+ <!-- Shared menu content (dropdown-menu subpath) -->
73
+ <ng-template #menu let-id="id">
74
+ <hlm-dropdown-menu>
75
+ <hlm-dropdown-menu-group>
76
+ <button hlmDropdownMenuItem (click)="open(id)">Open</button>
77
+ <button hlmDropdownMenuItem (click)="rename(id)">Rename</button>
78
+ <hlm-dropdown-menu-separator></hlm-dropdown-menu-separator>
79
+ <button hlmDropdownMenuItem variant="destructive" (click)="remove(id)">Delete</button>
80
+ </hlm-dropdown-menu-group>
81
+ </hlm-dropdown-menu>
82
+ </ng-template>
83
+ ```
84
+
85
+ > Exact `hlm-dropdown-menu` selectors/composition live in that subpath's README — the sketch above shows intent. What this package guarantees: `[hlmContextMenuTrigger]` anchors whatever `Menu` template you pass, sets `transformOriginSelector` so the panel animates from the click corner, and derives `data-side` from the resolved position.
86
+
87
+ Real selector: `[hlmContextMenuTrigger]` (attribute only, on any element). Host carries `data-slot="context-menu-trigger"`, `data-disabled` when locked, and `tw:select-none`.
88
+
89
+ ## API reference
90
+
91
+ ### `HlmContextMenuTrigger` (`[hlmContextMenuTrigger]`)
92
+
93
+ Host-directive mapping to `CdkContextMenuTrigger`:
94
+
95
+ | CDK input (aliased) | Directive-facing name | Description |
96
+ | --------------------------- | --------------------------- | ----------------------------------------------------- |
97
+ | `cdkContextMenuTriggerFor` | `hlmContextMenuTrigger` | The `<ng-template>` menu panel to open. **Required.** |
98
+ | `cdkContextMenuTriggerData` | `hlmContextMenuTriggerData` | Data passed to the menu template (`let-*` bindings). |
99
+ | `cdkContextMenuDisabled` | `disabled` | Locks right-click opening. |
100
+
101
+ | CDK output (aliased) | Payload | Description |
102
+ | ---------------------- | ------- | ------------------------------------------------------- |
103
+ | `cdkContextMenuOpened` | — | Emitted as `hlmContextMenuOpened` when the menu opens. |
104
+ | `cdkContextMenuClosed` | — | Emitted as `hlmContextMenuClosed` when the menu closes. |
105
+
106
+ Own inputs (placement, defaulting from injected config):
107
+
108
+ | Input | Type | Default | Description |
109
+ | ---------- | ----------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
110
+ | `disabled` | `boolean` | CDK trigger's current value | Locks the menu (boolean-coerced). Reflected as `data-disabled`. |
111
+ | `align` | `MenuAlign` | `'start'` (or configured) | Cross-axis alignment of the panel. |
112
+ | `side` | `MenuSide` | `'bottom'` (or configured) | Preferred side relative to the click point. Combined via `createMenuPosition(align, side)` and assigned to `cdkTrigger.menuPosition` in an `effect`. |
113
+
114
+ ### Config token (`hlm-context-menu-token.ts`)
115
+
116
+ | Member | Type | Description |
117
+ | ----------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
118
+ | `HlmContextMenuConfig` | `{ align: MenuAlign; side: MenuSide }` | Shape of the global default. |
119
+ | `provideHlmContextMenuConfig` | `(config: Partial<HlmContextMenuConfig>) => ValueProvider` | Provide app- or route-level defaults (`{ side: 'right' }`, …). Defaults: `{ align: 'start', side: 'bottom' }`. |
120
+ | `injectHlmContextMenuConfig` | `() => HlmContextMenuConfig` | Reads the configured (or default) config — used internally by the trigger. |
121
+
122
+ ## Examples
123
+
124
+ ### 1. Basic right-click area + dropdown menu
125
+
126
+ ```ts
127
+ import { Component } from '@angular/core';
128
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
129
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
130
+
131
+ @Component({
132
+ selector: 'app-basic-context-menu',
133
+ standalone: true,
134
+ imports: [...HlmContextMenuImports, ...HlmDropdownMenuImports],
135
+ template: `
136
+ <div
137
+ [hlmContextMenuTrigger]="menu"
138
+ class="border-border flex h-40 items-center justify-center rounded-lg border border-dashed text-sm"
139
+ >
140
+ Right-click here
141
+ </div>
142
+
143
+ <ng-template #menu>
144
+ <hlm-dropdown-menu>
145
+ <hlm-dropdown-menu-group>
146
+ <button hlmDropdownMenuItem (click)="action('cut')">Cut</button>
147
+ <button hlmDropdownMenuItem (click)="action('copy')">Copy</button>
148
+ <button hlmDropdownMenuItem (click)="action('paste')">Paste</button>
149
+ </hlm-dropdown-menu-group>
150
+ </hlm-dropdown-menu>
151
+ </ng-template>
152
+ `,
153
+ })
154
+ export class BasicContextMenuComponent {
155
+ action(a: string) {
156
+ console.log(a);
157
+ }
158
+ }
11
159
  ```
160
+
161
+ ### 2. Per-row data (`hlmContextMenuTriggerData`)
162
+
163
+ Pass the row into the template with `let-*` bindings.
164
+
165
+ ```ts
166
+ import { Component, signal } from '@angular/core';
167
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
168
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
169
+
170
+ interface File {
171
+ id: string;
172
+ name: string;
173
+ }
174
+
175
+ @Component({
176
+ selector: 'app-row-context-menu',
177
+ standalone: true,
178
+ imports: [...HlmContextMenuImports, ...HlmDropdownMenuImports],
179
+ template: `
180
+ @for (f of files(); track f.id) {
181
+ <div
182
+ [hlmContextMenuTrigger]="menu"
183
+ [hlmContextMenuTriggerData]="{ file: f }"
184
+ class="rounded px-3 py-2 text-sm hover:bg-muted"
185
+ >
186
+ {{ f.name }}
187
+ </div>
188
+ }
189
+
190
+ <ng-template #menu let-file="file">
191
+ <hlm-dropdown-menu>
192
+ <button hlmDropdownMenuItem (click)="open(file)">Open "{{ file.name }}"</button>
193
+ <button hlmDropdownMenuItem (click)="rename(file)">Rename</button>
194
+ <hlm-dropdown-menu-separator></hlm-dropdown-menu-separator>
195
+ <button hlmDropdownMenuItem variant="destructive" (click)="remove(file)">Delete</button>
196
+ </hlm-dropdown-menu>
197
+ </ng-template>
198
+ `,
199
+ })
200
+ export class RowContextMenuComponent {
201
+ readonly files = signal<File[]>([
202
+ { id: '1', name: 'report.pdf' },
203
+ { id: '2', name: 'photo.png' },
204
+ ]);
205
+ open(f: File) {
206
+ console.log('open', f);
207
+ }
208
+ rename(f: File) {
209
+ console.log('rename', f);
210
+ }
211
+ remove(f: File) {
212
+ this.files.update((fs) => fs.filter((x) => x.id !== f.id));
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### 3. Placement (`align` / `side`) + global config
218
+
219
+ ```ts
220
+ import { Component } from '@angular/core';
221
+ import { HlmContextMenuImports, provideHlmContextMenuConfig } from '@egose/shadcn-theme-ng/context-menu';
222
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
223
+
224
+ @Component({
225
+ selector: 'app-placement-context-menu',
226
+ standalone: true,
227
+ imports: [...HlmContextMenuImports, ...HlmDropdownMenuImports],
228
+ // Open to the right of the click everywhere in this subtree by default:
229
+ providers: [provideHlmContextMenuConfig({ side: 'right', align: 'start' })],
230
+ template: `
231
+ <!-- Override per trigger when needed: -->
232
+ <div [hlmContextMenuTrigger]="menu" side="bottom" align="center" class="rounded border p-8 text-sm">
233
+ Right-click (bottom-center panel)
234
+ </div>
235
+
236
+ <ng-template #menu>
237
+ <hlm-dropdown-menu>
238
+ <button hlmDropdownMenuItem>Item one</button>
239
+ <button hlmDropdownMenuItem>Item two</button>
240
+ </hlm-dropdown-menu>
241
+ </ng-template>
242
+ `,
243
+ })
244
+ export class PlacementContextMenuComponent {}
245
+ ```
246
+
247
+ ### 4. Disabled state + open/close tracking
248
+
249
+ ```ts
250
+ import { Component, signal } from '@angular/core';
251
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
252
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
253
+
254
+ @Component({
255
+ selector: 'app-disabled-context-menu',
256
+ standalone: true,
257
+ imports: [...HlmContextMenuImports, ...HlmDropdownMenuImports],
258
+ template: `
259
+ <label class="mb-2 flex items-center gap-2 text-sm">
260
+ <input type="checkbox" [checked]="locked()" (change)="locked.set(!locked())" /> Disable menu
261
+ </label>
262
+ <div
263
+ [hlmContextMenuTrigger]="menu"
264
+ [disabled]="locked()"
265
+ (hlmContextMenuOpened)="status.set('open')"
266
+ (hlmContextMenuClosed)="status.set('closed')"
267
+ class="rounded border p-8 text-sm"
268
+ >
269
+ Right-click (status: {{ status() }})
270
+ </div>
271
+
272
+ <ng-template #menu>
273
+ <hlm-dropdown-menu>
274
+ <button hlmDropdownMenuItem>Action</button>
275
+ </hlm-dropdown-menu>
276
+ </ng-template>
277
+ `,
278
+ })
279
+ export class DisabledContextMenuComponent {
280
+ readonly locked = signal(false);
281
+ readonly status = signal('closed');
282
+ }
283
+ ```
284
+
285
+ ### 5. Card / list with per-item menus (composition)
286
+
287
+ ```ts
288
+ import { Component } from '@angular/core';
289
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
290
+ import { HlmCardImports } from '@egose/shadcn-theme-ng/card';
291
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
292
+
293
+ @Component({
294
+ selector: 'app-card-context-menu',
295
+ standalone: true,
296
+ imports: [...HlmContextMenuImports, ...HlmCardImports, ...HlmDropdownMenuImports],
297
+ template: `
298
+ <div hlmCard class="w-80" [hlmContextMenuTrigger]="menu">
299
+ <div hlmCardHeader>
300
+ <h3 hlmCardTitle>Q3 report.pdf</h3>
301
+ <p hlmCardDescription>Right-click for file actions.</p>
302
+ </div>
303
+ <div hlmCardContent class="text-sm">2.4 MB · Modified yesterday</div>
304
+ </div>
305
+
306
+ <ng-template #menu>
307
+ <hlm-dropdown-menu>
308
+ <button hlmDropdownMenuItem>Preview</button>
309
+ <button hlmDropdownMenuItem>Download</button>
310
+ <button hlmDropdownMenuItem>Share…</button>
311
+ </hlm-dropdown-menu>
312
+ </ng-template>
313
+ `,
314
+ })
315
+ export class CardContextMenuComponent {}
316
+ ```
317
+
318
+ ### 6. Full custom menu content (icons + shortcuts + submenu hints)
319
+
320
+ ```ts
321
+ import { Component } from '@angular/core';
322
+ import { HlmContextMenuImports } from '@egose/shadcn-theme-ng/context-menu';
323
+ import { HlmDropdownMenuImports } from '@egose/shadcn-theme-ng/dropdown-menu';
324
+ import { NgIcon, provideIcons } from '@ng-icons/core';
325
+ import { lucideCopy, lucideScissors, lucideClipboardPaste } from '@ng-icons/lucide';
326
+
327
+ @Component({
328
+ selector: 'app-rich-context-menu',
329
+ standalone: true,
330
+ imports: [...HlmContextMenuImports, ...HlmDropdownMenuImports, NgIcon],
331
+ providers: [provideIcons({ lucideCopy, lucideScissors, lucideClipboardPaste })],
332
+ template: `
333
+ <div [hlmContextMenuTrigger]="menu" class="bg-muted rounded-lg p-10 text-sm">Right-click for a rich menu</div>
334
+
335
+ <ng-template #menu>
336
+ <hlm-dropdown-menu class="w-56">
337
+ <hlm-dropdown-menu-label>Clipboard</hlm-dropdown-menu-label>
338
+ <hlm-dropdown-menu-group>
339
+ <button hlmDropdownMenuItem>
340
+ <ng-icon name="lucideScissors" /> Cut <span hlmDropdownMenuShortcut>⌘X</span>
341
+ </button>
342
+ <button hlmDropdownMenuItem>
343
+ <ng-icon name="lucideCopy" /> Copy <span hlmDropdownMenuShortcut>⌘C</span>
344
+ </button>
345
+ <button hlmDropdownMenuItem>
346
+ <ng-icon name="lucideClipboardPaste" /> Paste <span hlmDropdownMenuShortcut>⌘V</span>
347
+ </button>
348
+ </hlm-dropdown-menu-group>
349
+ </hlm-dropdown-menu>
350
+ </ng-template>
351
+ `,
352
+ })
353
+ export class RichContextMenuComponent {}
354
+ ```
355
+
356
+ ## Accessibility notes
357
+
358
+ - Right-click is pointer-only: always provide a keyboard-accessible equivalent (a `…` button opening the same menu template via `hlm-dropdown-menu-trigger`, or visible action buttons) — otherwise keyboard/SR users lose the actions entirely.
359
+ - The trigger area keeps `select-none` but remains focusable content; do not set `disabled` without explaining why (adjacent hint or tooltip).
360
+ - Menu content comes from `dropdown-menu`/`menu` primitives with roving-focus, Escape-to-close, and arrow-key navigation — keep those behaviors; do not trap focus in the trigger.
361
+ - `hlmContextMenuOpened/Closed` let you mirror state (e.g. highlight the trigger area while open) so sighted keyboard users get the same context.
362
+
363
+ ## Theming / CSS variables
364
+
365
+ No component-specific CSS variables in this subpath. The trigger only adds `select-none`; all panel styling (surface, animations from the click corner via `transformOriginSelector`, `data-side` variants) lives in the `dropdown-menu`/`menu` content components.
366
+
367
+ ## Related subpaths
368
+
369
+ - `@egose/shadcn-theme-ng/dropdown-menu` — the menu panel content (`hlm-dropdown-menu`, items, separators, shortcuts).
370
+ - `@egose/shadcn-theme-ng/menu` — lower-level menu primitives if you build custom panels.
371
+ - `@egose/shadcn-theme-ng/popover` — click-triggered floating panels (vs right-click here).
372
+ - `@egose/shadcn-theme-ng/tooltip` — hints for the keyboard-accessible equivalents of context actions.