@molecule/app-danger-zone-angular 0.0.0-stage → 0.0.1-stage
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/LICENSE +202 -0
- package/README.md +521 -2
- package/dist/confirm-dialog.d.ts +23 -0
- package/dist/confirm-dialog.d.ts.map +1 -0
- package/dist/confirm-dialog.js +23 -0
- package/dist/confirm-dialog.js.map +1 -0
- package/dist/danger-zone-section.component.d.ts +139 -0
- package/dist/danger-zone-section.component.d.ts.map +1 -0
- package/dist/danger-zone-section.component.js +299 -0
- package/dist/danger-zone-section.component.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/slot-dom.d.ts +45 -0
- package/dist/slot-dom.d.ts.map +1 -0
- package/dist/slot-dom.js +80 -0
- package/dist/slot-dom.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +69 -4
package/README.md
CHANGED
|
@@ -1,3 +1,522 @@
|
|
|
1
|
-
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-11-15T09:11:28.356Z
|
|
7
|
+
-->
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
# @molecule/app-danger-zone-angular
|
|
10
|
+
|
|
11
|
+
Angular destructive-action primitives — the counterpart of
|
|
12
|
+
`@molecule/app-danger-zone-react`: the same props and the same DOM for the
|
|
13
|
+
same input.
|
|
14
|
+
|
|
15
|
+
Exports `<DangerZoneSection>` — Card with title/description/button for
|
|
16
|
+
delete/reset/revoke — and `<ConfirmDialog>`, the standalone "Are you
|
|
17
|
+
sure?" modal.
|
|
18
|
+
|
|
19
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
20
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
21
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Component } from '@angular/core'
|
|
27
|
+
import { DangerZoneSection } from '@molecule/app-danger-zone-angular'
|
|
28
|
+
|
|
29
|
+
@Component({
|
|
30
|
+
selector: 'app-account',
|
|
31
|
+
standalone: true,
|
|
32
|
+
imports: [DangerZoneSection],
|
|
33
|
+
template: `
|
|
34
|
+
<div
|
|
35
|
+
mol-danger-zone-section
|
|
36
|
+
title="Delete account"
|
|
37
|
+
description="This permanently removes your account and all data."
|
|
38
|
+
actionLabel="Delete account"
|
|
39
|
+
(click)="openConfirm()"
|
|
40
|
+
></div>
|
|
41
|
+
`,
|
|
42
|
+
})
|
|
43
|
+
export class Account {}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Type
|
|
47
|
+
|
|
48
|
+
`feature`
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install @molecule/app-danger-zone-angular @angular/common @angular/core @angular/platform-browser @molecule/app-angular @molecule/app-ui @molecule/app-ui-angular
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
### Interfaces
|
|
59
|
+
|
|
60
|
+
#### `ConfirmDialogProps`
|
|
61
|
+
|
|
62
|
+
Props accepted by {@link ConfirmDialog} — the same props
|
|
63
|
+
`@molecule/app-danger-zone-react` accepts. Node-valued props (`title`,
|
|
64
|
+
`description`) are projected content in Angular (an input cannot carry a
|
|
65
|
+
node), so they are typed `unknown`; `children` is the element's default
|
|
66
|
+
projection. Callbacks use React's names: the component's `close`/`confirm`
|
|
67
|
+
outputs are the same contract.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface ConfirmDialogProps {
|
|
71
|
+
/** Whether the dialog is open. */
|
|
72
|
+
open: boolean
|
|
73
|
+
/** Called when the dialog should close (escape, backdrop, cancel). */
|
|
74
|
+
onClose?: (event?: unknown) => void
|
|
75
|
+
/** Title (nodes are `[slot=title]` content). */
|
|
76
|
+
title?: unknown
|
|
77
|
+
/** Body / warning text (nodes are `[slot=description]` content). */
|
|
78
|
+
description?: unknown
|
|
79
|
+
/** Confirm-button label (e.g. "Delete"). */
|
|
80
|
+
confirmLabel?: string
|
|
81
|
+
/** Cancel-button label. */
|
|
82
|
+
cancelLabel?: string
|
|
83
|
+
/** Called when the user confirms. */
|
|
84
|
+
onConfirm?: () => void | Promise<void>
|
|
85
|
+
/** Is the confirm action destructive? Defaults to true. */
|
|
86
|
+
destructive?: boolean
|
|
87
|
+
/** Extra body content between description and footer (default projection). */
|
|
88
|
+
children?: unknown
|
|
89
|
+
/** Disables both buttons; the caller owns the pending state. */
|
|
90
|
+
loading?: boolean
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `DangerZoneSectionProps`
|
|
95
|
+
|
|
96
|
+
Props accepted by {@link DangerZoneSection} — the same props
|
|
97
|
+
`@molecule/app-danger-zone-react` accepts. Node props (`title`,
|
|
98
|
+
`description`, `actionLabel`, `children`) are projected content in Angular
|
|
99
|
+
(an input cannot carry a node), so they are typed `unknown`: pass them in
|
|
100
|
+
the host template (`[slot=title]`, `[slot=description]`,
|
|
101
|
+
`[slot=actionLabel]` and default content), not as values.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface DangerZoneSectionProps {
|
|
105
|
+
/** Section title (string/number renders as text; nodes are `[slot=title]` content). */
|
|
106
|
+
title?: unknown
|
|
107
|
+
/** Explanation of the destructive action (nodes are `[slot=description]` content). */
|
|
108
|
+
description?: unknown
|
|
109
|
+
/** Label on the action button (nodes are `[slot=actionLabel]` content). */
|
|
110
|
+
actionLabel?: unknown
|
|
111
|
+
/** Called when the button is clicked; receives the DOM click event. */
|
|
112
|
+
onAction?: (event: MouseEvent) => void
|
|
113
|
+
/** When true, the button disables and shows a loading label. */
|
|
114
|
+
loading?: boolean
|
|
115
|
+
/** When true, the button is disabled without loading. */
|
|
116
|
+
disabled?: boolean
|
|
117
|
+
/** Additional content between description and button (e.g. a confirm-text input), projected as the element's default content. */
|
|
118
|
+
children?: unknown
|
|
119
|
+
/** Extra classes on the Card (the host element). */
|
|
120
|
+
className?: string
|
|
121
|
+
/** `data-mol-id` for AI-agent selectors. */
|
|
122
|
+
dataMolId?: string
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Classes
|
|
127
|
+
|
|
128
|
+
#### `DangerZoneSection`
|
|
129
|
+
|
|
130
|
+
DangerZoneSection component.
|
|
131
|
+
|
|
132
|
+
Card-wrapped section for destructive actions — delete account, reset data,
|
|
133
|
+
revoke access. Uses the error/danger accent color and puts the action on
|
|
134
|
+
the right. The host IS the Card's `<div>`
|
|
135
|
+
(`div[mol-danger-zone-section]`), matching React's root exactly; the Card
|
|
136
|
+
classes (`cm.card({ variant: 'elevated' })` + `cm.cardPadding('md')`, the
|
|
137
|
+
UI Card's defaults) are applied through the host class binding.
|
|
138
|
+
|
|
139
|
+
The node props are projected slots: `title` renders inside the `<h3>`,
|
|
140
|
+
`description` inside the `<p>`, `actionLabel` inside the action `<button>`,
|
|
141
|
+
and `children` bare between the header and the button row (React renders
|
|
142
|
+
`{children}` with no wrapper). String inputs render as text instead — the
|
|
143
|
+
same values React renders. The action label's capture wrapper detaches
|
|
144
|
+
while `loading` is true: React shows the `…` glyph instead of the label.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
@Component({
|
|
148
|
+
selector: 'div[mol-danger-zone-section]',
|
|
149
|
+
standalone: true,
|
|
150
|
+
imports: [Button],
|
|
151
|
+
inputs: [
|
|
152
|
+
'title',
|
|
153
|
+
'description',
|
|
154
|
+
'actionLabel',
|
|
155
|
+
'loading',
|
|
156
|
+
'disabled',
|
|
157
|
+
'className',
|
|
158
|
+
'dataMolId',
|
|
159
|
+
'onAction',
|
|
160
|
+
],
|
|
161
|
+
host: {
|
|
162
|
+
'[class]': 'classes()',
|
|
163
|
+
'[attr.data-mol-id]': 'dataMolId ?? null',
|
|
164
|
+
},
|
|
165
|
+
template: `<div [class]="cm().stack(3)">
|
|
166
|
+
<header [class]="cm().stack(1)">
|
|
167
|
+
<h3 [class]="titleClasses()">
|
|
168
|
+
{{ titleText() }}<ng-content select="[slot=title]"></ng-content>
|
|
169
|
+
</h3>
|
|
170
|
+
<p [class]="cm().textSize('sm')">
|
|
171
|
+
{{ descriptionText() }}<ng-content select="[slot=description]"></ng-content>
|
|
172
|
+
</p>
|
|
173
|
+
</header>
|
|
174
|
+
<ng-content></ng-content>
|
|
175
|
+
<div [class]="cm().flex({ justify: 'end' })">
|
|
176
|
+
<button
|
|
177
|
+
mol-button
|
|
178
|
+
[variant]="'solid'"
|
|
179
|
+
[color]="'error'"
|
|
180
|
+
[disabled]="isDisabled()"
|
|
181
|
+
(click)="handleAction($event)"
|
|
182
|
+
>
|
|
183
|
+
{{ loading ? '…' : actionLabelText()
|
|
184
|
+
}}<span #labelSlot><ng-content select="[slot=actionLabel]"></ng-content></span>
|
|
185
|
+
</button>
|
|
186
|
+
</div>
|
|
187
|
+
</div>`,
|
|
188
|
+
})
|
|
189
|
+
export class DangerZoneSection implements AfterViewInit, DoCheck {
|
|
190
|
+
/** Section title (string/number renders as text; nodes are `[slot=title]` content). */
|
|
191
|
+
title?: unknown
|
|
192
|
+
/** Explanation of the destructive action (string/number as text; nodes are `[slot=description]` content). */
|
|
193
|
+
description?: unknown
|
|
194
|
+
/** Label on the action button (string/number as text; nodes are `[slot=actionLabel]` content). */
|
|
195
|
+
actionLabel?: unknown
|
|
196
|
+
/** When true, the button disables and shows a loading label. */
|
|
197
|
+
loading?: boolean
|
|
198
|
+
/** When true, the button is disabled without loading. */
|
|
199
|
+
disabled?: boolean
|
|
200
|
+
/** Extra classes on the Card. */
|
|
201
|
+
className?: string
|
|
202
|
+
/** `data-mol-id` for AI-agent selectors. */
|
|
203
|
+
dataMolId?: string
|
|
204
|
+
/** Called when the button is clicked; receives the DOM click event (React's `onAction`). */
|
|
205
|
+
onAction?: (event: MouseEvent) => void
|
|
206
|
+
|
|
207
|
+
/** The action label's capture wrapper (moved out of the DOM once read). */
|
|
208
|
+
@ViewChild('labelSlot') labelSlot?: ElementRef<HTMLSpanElement>
|
|
209
|
+
|
|
210
|
+
/** Whether the action label slot holds projected content (read once). */
|
|
211
|
+
private hasLabelSlot = false
|
|
212
|
+
|
|
213
|
+
/** The capture wrapper's parent (the action `<button>`), remembered across detaches. */
|
|
214
|
+
private labelParent?: Node
|
|
215
|
+
|
|
216
|
+
/** The projected label nodes while they render bare (React's `{actionLabel}`). */
|
|
217
|
+
private labelNodes: Node[] = []
|
|
218
|
+
|
|
219
|
+
/** Marks the wrapper's position in the button while the projected nodes render bare. */
|
|
220
|
+
private labelAnchor?: Comment
|
|
221
|
+
|
|
222
|
+
/** The wrapper sync for the action label's capture wrapper. */
|
|
223
|
+
private readonly sync = new SlotWrapper(inject(ElementRef), inject(Renderer2))
|
|
224
|
+
|
|
225
|
+
/** Creates the position anchor comment the DOM normalizer strips. */
|
|
226
|
+
private readonly renderer = inject(Renderer2)
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The current class map.
|
|
230
|
+
* @returns The UIClassMap.
|
|
231
|
+
*/
|
|
232
|
+
cm(): UIClassMap {
|
|
233
|
+
return getClassMap()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Computes the Card's class list (`cm.card({ variant: 'elevated' })` +
|
|
238
|
+
* `cm.cardPadding('md')` — the UI Card's defaults — plus `className`).
|
|
239
|
+
* @returns The class string.
|
|
240
|
+
*/
|
|
241
|
+
classes(): string {
|
|
242
|
+
return this.cm().cn(
|
|
243
|
+
this.cm().card({ variant: 'elevated' }),
|
|
244
|
+
this.cm().cardPadding('md'),
|
|
245
|
+
this.className,
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Computes the title's class (`cm.textSize('base')` + `cm.fontWeight('semibold')`).
|
|
251
|
+
* @returns The class string.
|
|
252
|
+
*/
|
|
253
|
+
titleClasses(): string {
|
|
254
|
+
return this.cm().cn(this.cm().textSize('base'), this.cm().fontWeight('semibold'))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The title input's text (React's `{title}` for a text child: strings and
|
|
259
|
+
* numbers render; a node arrives as `[slot=title]` content instead).
|
|
260
|
+
* @returns The text, or `''` for a node title.
|
|
261
|
+
*/
|
|
262
|
+
titleText(): string {
|
|
263
|
+
return typeof this.title === 'string' || typeof this.title === 'number'
|
|
264
|
+
? String(this.title)
|
|
265
|
+
: ''
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The description input's text (React's `{description}` for a text child).
|
|
270
|
+
* @returns The text, or `''` for a node description.
|
|
271
|
+
*/
|
|
272
|
+
descriptionText(): string {
|
|
273
|
+
return typeof this.description === 'string' || typeof this.description === 'number'
|
|
274
|
+
? String(this.description)
|
|
275
|
+
: ''
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* The action label input's text (React's `{actionLabel}` for a text child).
|
|
280
|
+
* @returns The text, or `''` for a node label.
|
|
281
|
+
*/
|
|
282
|
+
actionLabelText(): string {
|
|
283
|
+
return typeof this.actionLabel === 'string' || typeof this.actionLabel === 'number'
|
|
284
|
+
? String(this.actionLabel)
|
|
285
|
+
: ''
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The button's disabled state (React's `disabled || loading`; an absent
|
|
290
|
+
* boolean input must not show a disabled button).
|
|
291
|
+
* @returns Whether the button is disabled.
|
|
292
|
+
*/
|
|
293
|
+
isDisabled(): boolean {
|
|
294
|
+
return this.disabled === true || this.loading === true
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Runs the click handler the way React forwards the DOM event. */
|
|
298
|
+
handleAction(event: MouseEvent): void {
|
|
299
|
+
this.onAction?.(event)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Reads the label slot's presence (view queries resolve only at view init)
|
|
304
|
+
* and syncs the capture wrapper against React's DOM.
|
|
305
|
+
*/
|
|
306
|
+
ngAfterViewInit(): void {
|
|
307
|
+
const label = this.labelSlot?.nativeElement
|
|
308
|
+
if (label) {
|
|
309
|
+
this.labelParent = label.parentElement ?? undefined
|
|
310
|
+
this.hasLabelSlot = hasNodes(label)
|
|
311
|
+
}
|
|
312
|
+
this.syncLabels()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Re-syncs the wrapper when `loading` changes.
|
|
317
|
+
*/
|
|
318
|
+
ngDoCheck(): void {
|
|
319
|
+
this.syncLabels()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Applies React's label branch: the projected label renders only while
|
|
324
|
+
* there is one and the button is not loading (`{loading ? '…' : actionLabel}`),
|
|
325
|
+
* and it renders BARE — the capture wrapper never shows in the DOM.
|
|
326
|
+
*/
|
|
327
|
+
private syncLabels(): void {
|
|
328
|
+
const span = this.labelSlot?.nativeElement
|
|
329
|
+
const parent = this.labelParent
|
|
330
|
+
if (!span || !parent) return
|
|
331
|
+
if (this.hasLabelSlot && this.loading !== true) {
|
|
332
|
+
this.unwrapLabel(span, parent)
|
|
333
|
+
} else {
|
|
334
|
+
this.wrapLabel(span, parent)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Moves the projected nodes out of the capture wrapper to its position and
|
|
340
|
+
* detaches the empty wrapper (React renders the label with no element).
|
|
341
|
+
*
|
|
342
|
+
* @param span - The capture wrapper.
|
|
343
|
+
* @param parent - The wrapper's parent (the action `<button>`).
|
|
344
|
+
*/
|
|
345
|
+
private unwrapLabel(span: HTMLSpanElement, parent: Node): void {
|
|
346
|
+
const anchor = this.labelAnchor ?? this.createLabelAnchor(parent, span)
|
|
347
|
+
this.labelNodes = Array.from(span.childNodes)
|
|
348
|
+
for (const node of this.labelNodes) parent.insertBefore(node, anchor)
|
|
349
|
+
if (span.isConnected) parent.removeChild(span)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Creates the position anchor comment and inserts it where the wrapper sits.
|
|
354
|
+
*
|
|
355
|
+
* @param parent - The wrapper's parent (the action `<button>`).
|
|
356
|
+
* @param span - The capture wrapper the anchor replaces.
|
|
357
|
+
* @returns The created anchor comment.
|
|
358
|
+
*/
|
|
359
|
+
private createLabelAnchor(parent: Node, span: Node): Comment {
|
|
360
|
+
const anchor: Comment = this.renderer.createComment('slot')
|
|
361
|
+
this.labelAnchor = anchor
|
|
362
|
+
parent.insertBefore(anchor, span)
|
|
363
|
+
return anchor
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Puts the projected nodes back inside the capture wrapper (hidden, as
|
|
368
|
+
* React hides the label while loading) and keeps the wrapper out of the DOM.
|
|
369
|
+
*
|
|
370
|
+
* @param span - The capture wrapper.
|
|
371
|
+
* @param parent - The wrapper's parent (the action `<button>`).
|
|
372
|
+
*/
|
|
373
|
+
private wrapLabel(span: HTMLSpanElement, parent: Node): void {
|
|
374
|
+
for (const node of this.labelNodes) span.appendChild(node)
|
|
375
|
+
this.labelNodes = []
|
|
376
|
+
if (!span.isConnected && this.labelAnchor?.isConnected) {
|
|
377
|
+
parent.insertBefore(span, this.labelAnchor)
|
|
378
|
+
}
|
|
379
|
+
this.sync.sync(span, false, parent)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### `ConfirmDialog`
|
|
385
|
+
|
|
386
|
+
ConfirmDialog component.
|
|
387
|
+
|
|
388
|
+
Composed from `Modal` and `Button` exactly as React composes them; the
|
|
389
|
+
dialog's own title/description/children are projected content (`[slot=title]`,
|
|
390
|
+
`[slot=description]`, default). The host is unwrapped (`data-parity-unwrap`,
|
|
391
|
+
React's `Modal` root is a fragment/portal).
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
@Component({
|
|
395
|
+
selector: 'mol-confirm-dialog',
|
|
396
|
+
standalone: true,
|
|
397
|
+
imports: [Button, Modal],
|
|
398
|
+
inputs: ['open', 'title', 'description', 'confirmLabel', 'cancelLabel', 'destructive', 'loading'],
|
|
399
|
+
outputs: ['close', 'confirm'],
|
|
400
|
+
host: {
|
|
401
|
+
'data-parity-unwrap': '',
|
|
402
|
+
style: 'display: contents',
|
|
403
|
+
},
|
|
404
|
+
template: `<mol-modal [open]="open" (close)="close.emit($event)"
|
|
405
|
+
><div [class]="cm().stack(3)">
|
|
406
|
+
<h2 [class]="cm().cn(cm().textSize('lg'), cm().fontWeight('bold'))">
|
|
407
|
+
<ng-content select="[slot=title]"></ng-content>
|
|
408
|
+
</h2>
|
|
409
|
+
<p [class]="cm().textSize('sm')"><ng-content select="[slot=description]"></ng-content></p>
|
|
410
|
+
<ng-content></ng-content>
|
|
411
|
+
<div [class]="cm().flex({ justify: 'end', gap: 'sm' })">
|
|
412
|
+
<button mol-button [variant]="'ghost'" [disabled]="!!loading" (click)="close.emit($event)">
|
|
413
|
+
{{ cancelText() }}</button
|
|
414
|
+
><button
|
|
415
|
+
mol-button
|
|
416
|
+
[variant]="'solid'"
|
|
417
|
+
[color]="destructive === false ? 'primary' : 'error'"
|
|
418
|
+
[disabled]="!!loading"
|
|
419
|
+
(click)="confirm.emit()"
|
|
420
|
+
>
|
|
421
|
+
{{ confirmText() }}
|
|
422
|
+
</button>
|
|
423
|
+
</div>
|
|
424
|
+
</div></mol-modal
|
|
425
|
+
>`,
|
|
426
|
+
})
|
|
427
|
+
export class ConfirmDialog {
|
|
428
|
+
/** Whether the dialog is open. */
|
|
429
|
+
open = false
|
|
430
|
+
/** Title (node-valued prop). */
|
|
431
|
+
title?: unknown
|
|
432
|
+
/** Body / warning text (node-valued prop). */
|
|
433
|
+
description?: unknown
|
|
434
|
+
/** Confirm-button label. */
|
|
435
|
+
confirmLabel?: string
|
|
436
|
+
/** Cancel-button label. */
|
|
437
|
+
cancelLabel?: string
|
|
438
|
+
/** Whether the confirm action is destructive (default true). */
|
|
439
|
+
destructive = true
|
|
440
|
+
/** Disables both buttons (the caller owns the pending state). */
|
|
441
|
+
loading?: boolean
|
|
442
|
+
|
|
443
|
+
/** Fires when the dialog should close (escape, backdrop, cancel). */
|
|
444
|
+
@Output() readonly close = new EventEmitter<unknown>()
|
|
445
|
+
/** Fires when the user confirms. */
|
|
446
|
+
@Output() readonly confirm = new EventEmitter<void>()
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Reactive translation, read in the injection context. Its `t` reads the
|
|
450
|
+
* locale, so the view re-renders the text on a locale switch (the static `t`
|
|
451
|
+
* does not).
|
|
452
|
+
*/
|
|
453
|
+
private readonly i18n = useTranslation()
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* The current class map.
|
|
457
|
+
* @returns The UIClassMap.
|
|
458
|
+
*/
|
|
459
|
+
cm(): UIClassMap {
|
|
460
|
+
return getClassMap()
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* The cancel button's label.
|
|
465
|
+
* @returns The label text.
|
|
466
|
+
*/
|
|
467
|
+
cancelText(): string {
|
|
468
|
+
return this.cancelLabel ?? this.i18n.t('confirm.cancel', {}, { defaultValue: 'Cancel' })
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* The confirm button's label (React shows an ellipsis while loading).
|
|
473
|
+
* @returns The label text.
|
|
474
|
+
*/
|
|
475
|
+
confirmText(): string {
|
|
476
|
+
return this.loading
|
|
477
|
+
? '…'
|
|
478
|
+
: (this.confirmLabel ?? this.i18n.t('confirm.confirm', {}, { defaultValue: 'Confirm' }))
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Injection Notes
|
|
484
|
+
|
|
485
|
+
### Requirements
|
|
486
|
+
|
|
487
|
+
Peer dependencies:
|
|
488
|
+
|
|
489
|
+
- `@angular/common` ^22.0.0
|
|
490
|
+
- `@angular/core` ^22.0.0
|
|
491
|
+
- `@angular/platform-browser` ^22.0.0
|
|
492
|
+
- `@molecule/app-angular` ^3.5.0
|
|
493
|
+
- `@molecule/app-ui` ^1.0.1
|
|
494
|
+
- `@molecule/app-ui-angular` ^1.1.0
|
|
495
|
+
|
|
496
|
+
### Runtime Dependencies
|
|
497
|
+
|
|
498
|
+
- `@angular/common`
|
|
499
|
+
- `@angular/core`
|
|
500
|
+
- `@angular/platform-browser`
|
|
501
|
+
- `@molecule/app-angular`
|
|
502
|
+
- `@molecule/app-ui`
|
|
503
|
+
- `@molecule/app-ui-angular`
|
|
504
|
+
|
|
505
|
+
- Requires a bonded ClassMap (the Card classes, `cm.card` +
|
|
506
|
+
`cm.cardPadding('md')`) — rendering throws otherwise.
|
|
507
|
+
- Node props are projected content: `title`, `description` and
|
|
508
|
+
`actionLabel` are selected by `[slot=title]` / `[slot=description]` /
|
|
509
|
+
`[slot=actionLabel]`, everything else lands bare between the header and
|
|
510
|
+
the button row as the default projection. String inputs render as text.
|
|
511
|
+
- `<ConfirmDialog>` does NOT track `onConfirm`'s promise and does NOT
|
|
512
|
+
close itself on confirm: set `loading` yourself while the action runs,
|
|
513
|
+
then call `onClose()` when it settles — otherwise users can double-fire
|
|
514
|
+
the destructive action or be left staring at an open dialog.
|
|
515
|
+
Cancel/Confirm fall back to the `confirm.*` i18n keys (companion bond:
|
|
516
|
+
`@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
|
|
517
|
+
for action-specific wording ("Delete", "Revoke"). `destructive`
|
|
518
|
+
(default true) only switches the confirm button color.
|
|
519
|
+
|
|
520
|
+
## Translations
|
|
521
|
+
|
|
522
|
+
Translation strings are provided by `@molecule/app-locales-danger-zone`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfirmDialog — re-exported from the framework bundle.
|
|
3
|
+
*
|
|
4
|
+
* The Angular port lives in `@molecule/app-ui-angular` (like React's, which
|
|
5
|
+
* lives in `@molecule/app-ui-react` after its 2026-09-24 promotion), so
|
|
6
|
+
* every Angular app can confirm destructive actions without installing this
|
|
7
|
+
* feature package. This re-export keeps
|
|
8
|
+
* `@molecule/app-danger-zone-angular`'s public API aligned with
|
|
9
|
+
* `@molecule/app-danger-zone-react`; prefer importing it from
|
|
10
|
+
* `@molecule/app-ui-angular` directly.
|
|
11
|
+
*
|
|
12
|
+
* Contract (unchanged): the dialog does NOT track `onConfirm`'s promise and
|
|
13
|
+
* does NOT close itself on confirm — set `loading` yourself while the action
|
|
14
|
+
* runs, then call `onClose()` when it settles. Cancel/Confirm fall back to
|
|
15
|
+
* the `confirm.*` i18n keys (companion bond:
|
|
16
|
+
* `@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
|
|
17
|
+
* for action-specific wording. `destructive` (default true) only switches
|
|
18
|
+
* the confirm button color.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export { ConfirmDialog } from '@molecule/app-ui-angular';
|
|
23
|
+
//# sourceMappingURL=confirm-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-dialog.d.ts","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfirmDialog — re-exported from the framework bundle.
|
|
3
|
+
*
|
|
4
|
+
* The Angular port lives in `@molecule/app-ui-angular` (like React's, which
|
|
5
|
+
* lives in `@molecule/app-ui-react` after its 2026-09-24 promotion), so
|
|
6
|
+
* every Angular app can confirm destructive actions without installing this
|
|
7
|
+
* feature package. This re-export keeps
|
|
8
|
+
* `@molecule/app-danger-zone-angular`'s public API aligned with
|
|
9
|
+
* `@molecule/app-danger-zone-react`; prefer importing it from
|
|
10
|
+
* `@molecule/app-ui-angular` directly.
|
|
11
|
+
*
|
|
12
|
+
* Contract (unchanged): the dialog does NOT track `onConfirm`'s promise and
|
|
13
|
+
* does NOT close itself on confirm — set `loading` yourself while the action
|
|
14
|
+
* runs, then call `onClose()` when it settles. Cancel/Confirm fall back to
|
|
15
|
+
* the `confirm.*` i18n keys (companion bond:
|
|
16
|
+
* `@molecule/app-locales-danger-zone`); pass `confirmLabel`/`cancelLabel`
|
|
17
|
+
* for action-specific wording. `destructive` (default true) only switches
|
|
18
|
+
* the confirm button color.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export { ConfirmDialog } from '@molecule/app-ui-angular';
|
|
23
|
+
//# sourceMappingURL=confirm-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-dialog.js","sourceRoot":"","sources":["../src/confirm-dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA"}
|