@ktortu/aaa 0.9.2 → 0.9.3

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/dialog/dialog.css CHANGED
@@ -347,4 +347,10 @@
347
347
  translate: 0 0;
348
348
  }
349
349
  }
350
+
351
+ /* Actions en colonne sur mobile / bottom-sheet */
352
+ .cdk-overlay-pane.kt-dialog--sheet [ktDialogActions] {
353
+ flex-direction: column-reverse;
354
+ align-items: stretch;
355
+ }
350
356
  }
@@ -1,8 +1,103 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Directive, inject, ElementRef, afterNextRender, InjectionToken, PLATFORM_ID, contentChild, input, booleanAttribute, DestroyRef, effect } from '@angular/core';
2
+ import { InjectionToken, inject, ElementRef, PLATFORM_ID, contentChild, forwardRef, input, booleanAttribute, signal, DestroyRef, effect, Directive } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
4
  import { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';
5
5
 
6
+ const KT_CARD_CONFIG = new InjectionToken('KT_CARD_CONFIG');
7
+ /**
8
+ * Fournit des défauts de carte (apparence de surface) pour un sous-arbre ou l'application entière.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * providers: [provideKtCard({ variant: 'outlined' })]
13
+ * ```
14
+ */
15
+ function provideKtCard(config) {
16
+ return { provide: KT_CARD_CONFIG, useValue: config };
17
+ }
18
+ /**
19
+ * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi
20
+ * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de
21
+ * wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).
22
+ *
23
+ * Trois axes pilotés en `data-*` (même contrat que ktButton) :
24
+ * - variant : data-variant = elevated | outlined | filled (apparence)
25
+ * - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable
26
+ * est un [ktCardLink], lien/bouton primaire au lien étiré)
27
+ * - disabled : data-disabled (surface inerte)
28
+ *
29
+ * La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs
30
+ * et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.
31
+ *
32
+ * @example
33
+ * ```html
34
+ * <article ktCard variant="outlined" interactive>
35
+ * <div ktCardContent>
36
+ * <h3 id="t1">Titre</h3>
37
+ * <a ktCardLink routerLink="/detail" aria-labelledby="t1">Voir le détail</a>
38
+ * </div>
39
+ * </article>
40
+ * ```
41
+ */
42
+ class KtCard {
43
+ config = inject(KT_CARD_CONFIG, { optional: true });
44
+ host = inject(ElementRef).nativeElement;
45
+ platformId = inject(PLATFORM_ID);
46
+ auditEnabled = inject(KT_AUDIT_ENABLED);
47
+ /** Référence réactive sur le lien primaire de la carte */
48
+ cardLink = contentChild(forwardRef(() => KtCardLink), { ...(ngDevMode ? { debugName: "cardLink" } : /* istanbul ignore next */ {}), descendants: true });
49
+ /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */
50
+ variant = input(this.config?.variant ?? 'elevated', /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
52
+ /**
53
+ * Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton
54
+ * primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false
55
+ */
56
+ interactive = input(false, { ...(ngDevMode ? { debugName: "interactive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
57
+ /** Rend la surface inerte (état `data-disabled`). @default false */
58
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
59
+ isDestroyed = false;
60
+ contentInitialized = signal(false, /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "contentInitialized" }] : /* istanbul ignore next */ []));
62
+ constructor() {
63
+ inject(DestroyRef).onDestroy(() => {
64
+ this.isDestroyed = true;
65
+ });
66
+ effect(() => {
67
+ if (this.isDestroyed)
68
+ return;
69
+ if (!isPlatformBrowser(this.platformId))
70
+ return;
71
+ if (!this.contentInitialized())
72
+ return;
73
+ if (!this.auditEnabled || !this.interactive())
74
+ return;
75
+ // Garde-fou a11y : une carte interactive sans cible cliquable affiche une
76
+ // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).
77
+ const hasTarget = this.host.matches('a, button') || !!this.cardLink();
78
+ if (!hasTarget) {
79
+ console.warn('[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +
80
+ 'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.');
81
+ }
82
+ });
83
+ }
84
+ ngAfterContentInit() {
85
+ this.contentInitialized.set(true);
86
+ }
87
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, deps: [], target: i0.ɵɵFactoryTarget.Directive });
88
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.1", type: KtCard, isStandalone: true, selector: "[ktCard]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variant": "variant()", "attr.data-interactive": "interactive() ? \"\" : null", "attr.data-disabled": "disabled() ? \"\" : null" } }, queries: [{ propertyName: "cardLink", first: true, predicate: i0.forwardRef(() => KtCardLink), descendants: true, isSignal: true }], ngImport: i0 });
89
+ }
90
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, decorators: [{
91
+ type: Directive,
92
+ args: [{
93
+ selector: '[ktCard]',
94
+ host: {
95
+ '[attr.data-variant]': 'variant()',
96
+ '[attr.data-interactive]': 'interactive() ? "" : null',
97
+ '[attr.data-disabled]': 'disabled() ? "" : null',
98
+ },
99
+ }]
100
+ }], ctorParameters: () => [], propDecorators: { cardLink: [{ type: i0.ContentChild, args: [forwardRef(() => KtCardLink), { ...{ descendants: true }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
6
101
  /**
7
102
  * En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur
8
103
  * structurel sans logique — la mise en forme vit dans `card.css` via `[ktCardHeader]`.
@@ -76,7 +171,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
76
171
  }] });
77
172
  /**
78
173
  * Lien (ou bouton) PRIMAIRE d'une carte interactive : pattern « lien étiré » (Inclusive
79
- * Components). Un pseudo-élément `::after` couvre toute la carte (cf. `card.css`) → toute la
174
+ * Components). Un pseudo-élément `::after` covers toute la carte (cf. `card.css`) → toute la
80
175
  * surface est cliquable, SANS imbriquer de contrôles interactifs (anti-pattern WCAG 4.1.2). Les
81
176
  * actions secondaires de la carte repassent au-dessus du lien (z-index dans `card.css`).
82
177
  *
@@ -104,26 +199,24 @@ class KtCardLink {
104
199
  * hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer
105
200
  * l'état `disabled` de la carte (aria-disabled / tabindex) sans planter hors contexte.
106
201
  */
107
- card = inject(KtCard, { optional: true });
202
+ card = inject(forwardRef(() => KtCard), { optional: true });
108
203
  handleClick(event) {
109
204
  if (this.card?.disabled()) {
110
205
  event.preventDefault();
111
206
  event.stopPropagation();
112
207
  }
113
208
  }
114
- constructor() {
209
+ ngAfterViewInit() {
115
210
  // Garde-fou a11y : un lien étiré sans nom accessible n'est pas annonçable.
116
- afterNextRender(() => {
117
- if (!this.auditEnabled)
118
- return;
119
- const hasName = !!this.host.textContent?.trim() ||
120
- !!this.host.getAttribute('aria-label')?.trim() ||
121
- this.host.hasAttribute('aria-labelledby');
122
- if (!hasName) {
123
- console.warn('[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +
124
- 'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).');
125
- }
126
- });
211
+ if (!this.auditEnabled)
212
+ return;
213
+ const hasName = !!this.host.textContent?.trim() ||
214
+ !!this.host.getAttribute('aria-label')?.trim() ||
215
+ this.host.hasAttribute('aria-labelledby');
216
+ if (!hasName) {
217
+ console.warn('[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +
218
+ 'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).');
219
+ }
127
220
  }
128
221
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
129
222
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardLink, isStandalone: true, selector: "a[ktCardLink], button[ktCardLink]", host: { listeners: { "click": "handleClick($event)" }, properties: { "attr.aria-disabled": "card?.disabled() ? \"true\" : null", "attr.tabindex": "card?.disabled() ? \"-1\" : null" } }, ngImport: i0 });
@@ -138,96 +231,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
138
231
  '(click)': 'handleClick($event)',
139
232
  },
140
233
  }]
141
- }], ctorParameters: () => [] });
142
-
143
- const KT_CARD_CONFIG = new InjectionToken('KT_CARD_CONFIG');
144
- /**
145
- * Fournit des défauts de carte (apparence de surface) pour un sous-arbre ou l'application entière.
146
- *
147
- * @example
148
- * ```ts
149
- * providers: [provideKtCard({ variant: 'outlined' })]
150
- * ```
151
- */
152
- function provideKtCard(config) {
153
- return { provide: KT_CARD_CONFIG, useValue: config };
154
- }
155
- /**
156
- * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi
157
- * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de
158
- * wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).
159
- *
160
- * Trois axes pilotés en `data-*` (même contrat que ktButton) :
161
- * - variant : data-variant = elevated | outlined | filled (apparence)
162
- * - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable
163
- * est un [ktCardLink], lien/bouton primaire au lien étiré)
164
- * - disabled : data-disabled (surface inerte)
165
- *
166
- * La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs
167
- * et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.
168
- *
169
- * @example
170
- * ```html
171
- * <article ktCard variant="outlined" interactive>
172
- * <div ktCardContent>
173
- * <h3 id="t1">Titre</h3>
174
- * <a ktCardLink routerLink="/detail" aria-labelledby="t1">Voir le détail</a>
175
- * </div>
176
- * </article>
177
- * ```
178
- */
179
- class KtCard {
180
- config = inject(KT_CARD_CONFIG, { optional: true });
181
- host = inject(ElementRef).nativeElement;
182
- platformId = inject(PLATFORM_ID);
183
- auditEnabled = inject(KT_AUDIT_ENABLED);
184
- /** Référence réactive sur le lien primaire de la carte */
185
- cardLink = contentChild(KtCardLink, { ...(ngDevMode ? { debugName: "cardLink" } : /* istanbul ignore next */ {}), descendants: true });
186
- /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */
187
- variant = input(this.config?.variant ?? 'elevated', /* @ts-ignore */
188
- ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
189
- /**
190
- * Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton
191
- * primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false
192
- */
193
- interactive = input(false, { ...(ngDevMode ? { debugName: "interactive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
194
- /** Rend la surface inerte (état `data-disabled`). @default false */
195
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
196
- isDestroyed = false;
197
- constructor() {
198
- inject(DestroyRef).onDestroy(() => {
199
- this.isDestroyed = true;
200
- });
201
- // Garde-fou a11y : une carte interactive sans cible cliquable affiche une
202
- // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).
203
- effect(() => {
204
- if (this.isDestroyed)
205
- return;
206
- if (!isPlatformBrowser(this.platformId))
207
- return;
208
- if (!this.auditEnabled || !this.interactive())
209
- return;
210
- const hasTarget = this.host.matches('a, button') || !!this.cardLink();
211
- if (!hasTarget) {
212
- console.warn('[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +
213
- 'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.');
214
- }
215
- });
216
- }
217
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, deps: [], target: i0.ɵɵFactoryTarget.Directive });
218
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.1", type: KtCard, isStandalone: true, selector: "[ktCard]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variant": "variant()", "attr.data-interactive": "interactive() ? \"\" : null", "attr.data-disabled": "disabled() ? \"\" : null" } }, queries: [{ propertyName: "cardLink", first: true, predicate: KtCardLink, descendants: true, isSignal: true }], ngImport: i0 });
219
- }
220
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, decorators: [{
221
- type: Directive,
222
- args: [{
223
- selector: '[ktCard]',
224
- host: {
225
- '[attr.data-variant]': 'variant()',
226
- '[attr.data-interactive]': 'interactive() ? "" : null',
227
- '[attr.data-disabled]': 'disabled() ? "" : null',
228
- },
229
- }]
230
- }], ctorParameters: () => [], propDecorators: { cardLink: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KtCardLink), { ...{ descendants: true }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
234
+ }] });
231
235
 
232
236
  /**
233
237
  * Import ergonomique de toute la famille card en une fois :
@@ -1 +1 @@
1
- {"version":3,"file":"ktortu-aaa-card.mjs","sources":["../../../../projects/ktortu/aaa/card/card-structure.ts","../../../../projects/ktortu/aaa/card/card.ts","../../../../projects/ktortu/aaa/card/public-api.ts","../../../../projects/ktortu/aaa/card/ktortu-aaa-card.ts"],"sourcesContent":["import { Directive, ElementRef, afterNextRender, inject } from '@angular/core';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\n\nimport { KtCard } from './card';\n\n/**\n * En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur\n * structurel sans logique — la mise en forme vit dans `card.css` via `[ktCardHeader]`.\n * L'ÉTIQUETTE accessible reste le titre fourni par le consommateur (`<h3 id>` + `aria-labelledby`\n * sur l'hôte), comme [ktDialogTitle] pour le dialog : on sépare layout et sémantique.\n *\n * @example\n * ```html\n * <header ktCardHeader><h3 id=\"t1\">Titre</h3></header>\n * ```\n */\n@Directive({ selector: '[ktCardHeader]' })\nexport class KtCardHeader {}\n\n/**\n * Média pleine largeur (image/vidéo). Marqueur structurel sans logique : `card.css` lui donne le\n * full-bleed (marge négative = padding de la carte) et `[ktCard]` clippe au rayon quand un média\n * est présent. À envelopper autour d'un `<img ngSrc>` (NgOptimizedImage).\n *\n * @example\n * ```html\n * <div ktCardMedia><img ngSrc=\"cover.jpg\" width=\"400\" height=\"200\" alt=\"\" /></div>\n * ```\n */\n@Directive({ selector: '[ktCardMedia]' })\nexport class KtCardMedia {}\n\n/**\n * Corps de la carte. Marqueur structurel sans logique : la mise en forme (rythme vertical) vit\n * dans `card.css` via `[ktCardContent]`.\n *\n * @example\n * ```html\n * <div ktCardContent>Texte de la carte.</div>\n * ```\n */\n@Directive({ selector: '[ktCardContent]' })\nexport class KtCardContent {}\n\n/**\n * Barre d'actions de la carte (rangée de boutons/liens). Marqueur structurel sans logique :\n * la mise en forme (flex, gap, épinglée en pied) vit dans `card.css` via `[ktCardActions]`.\n *\n * @example\n * ```html\n * <footer ktCardActions><button ktButton>Action</button></footer>\n * ```\n */\n@Directive({ selector: '[ktCardActions]' })\nexport class KtCardActions {}\n\n/**\n * Lien (ou bouton) PRIMAIRE d'une carte interactive : pattern « lien étiré » (Inclusive\n * Components). Un pseudo-élément `::after` couvre toute la carte (cf. `card.css`) → toute la\n * surface est cliquable, SANS imbriquer de contrôles interactifs (anti-pattern WCAG 4.1.2). Les\n * actions secondaires de la carte repassent au-dessus du lien (z-index dans `card.css`).\n *\n * UN SEUL [ktCardLink] par carte. Le focus clavier est porté sur ce lien ; l'anneau de focus est\n * relayé sur toute la carte (`[ktCard]:has([ktCardLink]:focus-visible)`).\n *\n * Quand la carte ancêtre est `disabled`, le lien sort de l'ordre de tabulation (`tabindex=\"-1\"`)\n * et est annoncé `aria-disabled` : une carte inerte ne piège pas le focus clavier (WCAG 2.4.3).\n *\n * @example\n * ```html\n * <article ktCard interactive>\n * <div ktCardContent>\n * <h3 id=\"t1\">Titre</h3>\n * <a ktCardLink routerLink=\"/detail\" aria-labelledby=\"t1\">Voir le détail</a>\n * </div>\n * </article>\n * ```\n */\n@Directive({\n selector: 'a[ktCardLink], button[ktCardLink]',\n host: {\n '[attr.aria-disabled]': 'card?.disabled() ? \"true\" : null',\n '[attr.tabindex]': 'card?.disabled() ? \"-1\" : null',\n '(click)': 'handleClick($event)',\n },\n})\nexport class KtCardLink {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\n /**\n * Carte ancêtre, injectée optionnellement via `inject(Card, { optional: true })` (DI par\n * hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer\n * l'état `disabled` de la carte (aria-disabled / tabindex) sans planter hors contexte.\n */\n protected readonly card = inject(KtCard, { optional: true });\n\n handleClick(event: Event): void {\n if (this.card?.disabled()) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n constructor() {\n // Garde-fou a11y : un lien étiré sans nom accessible n'est pas annonçable.\n afterNextRender(() => {\n if (!this.auditEnabled) return;\n\n const hasName =\n !!this.host.textContent?.trim() ||\n !!this.host.getAttribute('aria-label')?.trim() ||\n this.host.hasAttribute('aria-labelledby');\n if (!hasName) {\n console.warn(\n '[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +\n 'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).',\n );\n }\n });\n }\n}\n","import {\n DestroyRef,\n Directive,\n ElementRef,\n InjectionToken,\n PLATFORM_ID,\n Provider,\n booleanAttribute,\n contentChild,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\nimport { KtCardLink } from './card-structure';\n\n/** Axe \"apparence\" de la surface. */\nexport type KtCardVariant = 'elevated' | 'outlined' | 'filled';\n\n/** Défauts applicables à toutes les `[ktCard]` (surchargeables par carte via les inputs). */\nexport interface KtCardConfig {\n /** Apparence par défaut de la surface. */\n variant: KtCardVariant;\n}\n\nexport const KT_CARD_CONFIG = new InjectionToken<Partial<KtCardConfig>>('KT_CARD_CONFIG');\n\n/**\n * Fournit des défauts de carte (apparence de surface) pour un sous-arbre ou l'application entière.\n *\n * @example\n * ```ts\n * providers: [provideKtCard({ variant: 'outlined' })]\n * ```\n */\nexport function provideKtCard(config: Partial<KtCardConfig>): Provider {\n return { provide: KT_CARD_CONFIG, useValue: config };\n}\n\n/**\n * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi\n * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de\n * wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).\n *\n * Trois axes pilotés en `data-*` (même contrat que ktButton) :\n * - variant : data-variant = elevated | outlined | filled (apparence)\n * - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable\n * est un [ktCardLink], lien/bouton primaire au lien étiré)\n * - disabled : data-disabled (surface inerte)\n *\n * La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs\n * et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.\n *\n * @example\n * ```html\n * <article ktCard variant=\"outlined\" interactive>\n * <div ktCardContent>\n * <h3 id=\"t1\">Titre</h3>\n * <a ktCardLink routerLink=\"/detail\" aria-labelledby=\"t1\">Voir le détail</a>\n * </div>\n * </article>\n * ```\n */\n@Directive({\n selector: '[ktCard]',\n host: {\n '[attr.data-variant]': 'variant()',\n '[attr.data-interactive]': 'interactive() ? \"\" : null',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n },\n})\nexport class KtCard {\n private readonly config = inject(KT_CARD_CONFIG, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly platformId = inject(PLATFORM_ID);\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\n\n /** Référence réactive sur le lien primaire de la carte */\n readonly cardLink = contentChild(KtCardLink, { descendants: true });\n\n /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */\n readonly variant = input<KtCardVariant>(this.config?.variant ?? 'elevated');\n\n /**\n * Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton\n * primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false\n */\n readonly interactive = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Rend la surface inerte (état `data-disabled`). @default false */\n readonly disabled = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n private isDestroyed = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n this.isDestroyed = true;\n });\n\n // Garde-fou a11y : une carte interactive sans cible cliquable affiche une\n // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).\n effect(() => {\n if (this.isDestroyed) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (!this.auditEnabled || !this.interactive()) return;\n\n const hasTarget = this.host.matches('a, button') || !!this.cardLink();\n if (!hasTarget) {\n console.warn(\n '[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +\n 'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.',\n );\n }\n });\n }\n}\n","import { KtCard } from './card';\nimport { KtCardActions, KtCardContent, KtCardHeader, KtCardLink, KtCardMedia } from './card-structure';\n\nexport * from './card';\nexport * from './card-structure';\n\n/**\n * Import ergonomique de toute la famille card en une fois :\n * `imports: [KtCardImports]` au lieu d'énumérer chaque directive.\n */\nexport const KtCardImports = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ktortu-aaa-card.mjs","sources":["../../../../projects/ktortu/aaa/card/card.ts","../../../../projects/ktortu/aaa/card/public-api.ts","../../../../projects/ktortu/aaa/card/ktortu-aaa-card.ts"],"sourcesContent":["import {\n AfterContentInit,\n AfterViewInit,\n DestroyRef,\n Directive,\n ElementRef,\n InjectionToken,\n PLATFORM_ID,\n Provider,\n booleanAttribute,\n contentChild,\n effect,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\n\n/** Axe \"apparence\" de la surface. */\nexport type KtCardVariant = 'elevated' | 'outlined' | 'filled';\n\n/** Défauts applicables à toutes les `[ktCard]` (surchargeables par carte via les inputs). */\nexport interface KtCardConfig {\n /** Apparence par défaut de la surface. */\n variant: KtCardVariant;\n}\n\nexport const KT_CARD_CONFIG = new InjectionToken<Partial<KtCardConfig>>('KT_CARD_CONFIG');\n\n/**\n * Fournit des défauts de carte (apparence de surface) pour un sous-arbre ou l'application entière.\n *\n * @example\n * ```ts\n * providers: [provideKtCard({ variant: 'outlined' })]\n * ```\n */\nexport function provideKtCard(config: Partial<KtCardConfig>): Provider {\n return { provide: KT_CARD_CONFIG, useValue: config };\n}\n\n/**\n * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi\n * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de\n * wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).\n *\n * Trois axes pilotés en `data-*` (même contrat que ktButton) :\n * - variant : data-variant = elevated | outlined | filled (apparence)\n * - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable\n * est un [ktCardLink], lien/bouton primaire au lien étiré)\n * - disabled : data-disabled (surface inerte)\n *\n * La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs\n * et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.\n *\n * @example\n * ```html\n * <article ktCard variant=\"outlined\" interactive>\n * <div ktCardContent>\n * <h3 id=\"t1\">Titre</h3>\n * <a ktCardLink routerLink=\"/detail\" aria-labelledby=\"t1\">Voir le détail</a>\n * </div>\n * </article>\n * ```\n */\n@Directive({\n selector: '[ktCard]',\n host: {\n '[attr.data-variant]': 'variant()',\n '[attr.data-interactive]': 'interactive() ? \"\" : null',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n },\n})\nexport class KtCard implements AfterContentInit {\n private readonly config = inject(KT_CARD_CONFIG, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly platformId = inject(PLATFORM_ID);\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\n\n /** Référence réactive sur le lien primaire de la carte */\n readonly cardLink = contentChild<KtCardLink>(\n forwardRef(() => KtCardLink),\n { descendants: true },\n );\n\n /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */\n readonly variant = input<KtCardVariant>(this.config?.variant ?? 'elevated');\n\n /**\n * Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton\n * primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false\n */\n readonly interactive = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Rend la surface inerte (état `data-disabled`). @default false */\n readonly disabled = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n private isDestroyed = false;\n private readonly contentInitialized = signal(false);\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n this.isDestroyed = true;\n });\n\n effect(() => {\n if (this.isDestroyed) return;\n if (!isPlatformBrowser(this.platformId)) return;\n if (!this.contentInitialized()) return;\n if (!this.auditEnabled || !this.interactive()) return;\n\n // Garde-fou a11y : une carte interactive sans cible cliquable affiche une\n // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).\n const hasTarget = this.host.matches('a, button') || !!this.cardLink();\n if (!hasTarget) {\n console.warn(\n '[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +\n 'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.',\n );\n }\n });\n }\n\n ngAfterContentInit(): void {\n this.contentInitialized.set(true);\n }\n}\n\n/**\n * En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur\n * structurel sans logique — la mise en forme vit dans `card.css` via `[ktCardHeader]`.\n * L'ÉTIQUETTE accessible reste le titre fourni par le consommateur (`<h3 id>` + `aria-labelledby`\n * sur l'hôte), comme [ktDialogTitle] pour le dialog : on sépare layout et sémantique.\n *\n * @example\n * ```html\n * <header ktCardHeader><h3 id=\"t1\">Titre</h3></header>\n * ```\n */\n@Directive({ selector: '[ktCardHeader]' })\nexport class KtCardHeader {}\n\n/**\n * Média pleine largeur (image/vidéo). Marqueur structurel sans logique : `card.css` lui donne le\n * full-bleed (marge négative = padding de la carte) et `[ktCard]` clippe au rayon quand un média\n * est présent. À envelopper autour d'un `<img ngSrc>` (NgOptimizedImage).\n *\n * @example\n * ```html\n * <div ktCardMedia><img ngSrc=\"cover.jpg\" width=\"400\" height=\"200\" alt=\"\" /></div>\n * ```\n */\n@Directive({ selector: '[ktCardMedia]' })\nexport class KtCardMedia {}\n\n/**\n * Corps de la carte. Marqueur structurel sans logique : la mise en forme (rythme vertical) vit\n * dans `card.css` via `[ktCardContent]`.\n *\n * @example\n * ```html\n * <div ktCardContent>Texte de la carte.</div>\n * ```\n */\n@Directive({ selector: '[ktCardContent]' })\nexport class KtCardContent {}\n\n/**\n * Barre d'actions de la carte (rangée de boutons/liens). Marqueur structurel sans logique :\n * la mise en forme (flex, gap, épinglée en pied) vit dans `card.css` via `[ktCardActions]`.\n *\n * @example\n * ```html\n * <footer ktCardActions><button ktButton>Action</button></footer>\n * ```\n */\n@Directive({ selector: '[ktCardActions]' })\nexport class KtCardActions {}\n\n/**\n * Lien (ou bouton) PRIMAIRE d'une carte interactive : pattern « lien étiré » (Inclusive\n * Components). Un pseudo-élément `::after` covers toute la carte (cf. `card.css`) → toute la\n * surface est cliquable, SANS imbriquer de contrôles interactifs (anti-pattern WCAG 4.1.2). Les\n * actions secondaires de la carte repassent au-dessus du lien (z-index dans `card.css`).\n *\n * UN SEUL [ktCardLink] par carte. Le focus clavier est porté sur ce lien ; l'anneau de focus est\n * relayé sur toute la carte (`[ktCard]:has([ktCardLink]:focus-visible)`).\n *\n * Quand la carte ancêtre est `disabled`, le lien sort de l'ordre de tabulation (`tabindex=\"-1\"`)\n * et est annoncé `aria-disabled` : une carte inerte ne piège pas le focus clavier (WCAG 2.4.3).\n *\n * @example\n * ```html\n * <article ktCard interactive>\n * <div ktCardContent>\n * <h3 id=\"t1\">Titre</h3>\n * <a ktCardLink routerLink=\"/detail\" aria-labelledby=\"t1\">Voir le détail</a>\n * </div>\n * </article>\n * ```\n */\n@Directive({\n selector: 'a[ktCardLink], button[ktCardLink]',\n host: {\n '[attr.aria-disabled]': 'card?.disabled() ? \"true\" : null',\n '[attr.tabindex]': 'card?.disabled() ? \"-1\" : null',\n '(click)': 'handleClick($event)',\n },\n})\nexport class KtCardLink implements AfterViewInit {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\n /**\n * Carte ancêtre, injectée optionnellement via `inject(Card, { optional: true })` (DI par\n * hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer\n * l'état `disabled` de la carte (aria-disabled / tabindex) sans planter hors contexte.\n */\n protected readonly card = inject<KtCard>(\n forwardRef(() => KtCard),\n { optional: true },\n );\n\n handleClick(event: Event): void {\n if (this.card?.disabled()) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n ngAfterViewInit(): void {\n // Garde-fou a11y : un lien étiré sans nom accessible n'est pas annonçable.\n if (!this.auditEnabled) return;\n\n const hasName =\n !!this.host.textContent?.trim() ||\n !!this.host.getAttribute('aria-label')?.trim() ||\n this.host.hasAttribute('aria-labelledby');\n if (!hasName) {\n console.warn(\n '[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +\n 'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).',\n );\n }\n }\n}\n","import { KtCard, KtCardActions, KtCardContent, KtCardHeader, KtCardLink, KtCardMedia } from './card';\n\nexport * from './card';\n\n/**\n * Import ergonomique de toute la famille card en une fois :\n * `imports: [KtCardImports]` au lieu d'énumérer chaque directive.\n */\nexport const KtCardImports = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  import { DialogRef, DEFAULT_DIALOG_CONFIG, CdkDialogContainer, Dialog, DIALOG_DATA } from '@angular/cdk/dialog';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, ElementRef, Renderer2, afterNextRender, Directive, input, isDevMode, DestroyRef, PLATFORM_ID, signal, ChangeDetectionStrategy, Component, ViewContainerRef } from '@angular/core';
3
+ import { inject, ElementRef, Renderer2, afterNextRender, Directive, input, isDevMode, DestroyRef, PLATFORM_ID, signal, ChangeDetectionStrategy, Component, ViewContainerRef, Injectable } from '@angular/core';
4
4
  import { KtIdGenerator, createKtSheetDrag, KtViewport } from '@ktortu/aaa/cdk';
5
5
  import { isPlatformBrowser } from '@angular/common';
6
6
  import * as i1 from '@angular/cdk/portal';
7
7
  import { PortalModule } from '@angular/cdk/portal';
8
+ import { map } from 'rxjs/operators';
9
+ import { KtButton } from '@ktortu/aaa/button';
8
10
 
9
11
  /**
10
12
  * À poser sur le titre VISIBLE (idéalement un `<h2>`) du contenu d'un CDK Dialog.
@@ -587,6 +589,224 @@ function defineKtDialog() {
587
589
  };
588
590
  }
589
591
 
592
+ const alertDialog = defineKtDialog();
593
+ /**
594
+ * Composant interne d'alerte générique.
595
+ * Affiche un titre, un message (simple ou multiligne HTML) et un bouton de fermeture.
596
+ */
597
+ class KtAlertDialog {
598
+ data = alertDialog.injectData();
599
+ isMessageArray() {
600
+ return Array.isArray(this.data.message);
601
+ }
602
+ messageArray() {
603
+ return Array.isArray(this.data.message) ? this.data.message : [];
604
+ }
605
+ messageString() {
606
+ return typeof this.data.message === 'string' ? this.data.message : '';
607
+ }
608
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtAlertDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
609
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: KtAlertDialog, isStandalone: true, selector: "kt-alert-dialog", ngImport: i0, template: `
610
+ <h2 ktDialogTitle>{{ data.title }}</h2>
611
+ <div ktDialogDescription>
612
+ @if (isMessageArray()) {
613
+ @for (line of messageArray(); track line) {
614
+ <p [innerHTML]="line"></p>
615
+ }
616
+ } @else {
617
+ <p [innerHTML]="messageString()"></p>
618
+ }
619
+ </div>
620
+ <footer ktDialogActions>
621
+ <button ktButton ktDialogFocusInitial ktDialogClose>
622
+ {{ data.closeLabel || 'Fermer' }}
623
+ </button>
624
+ </footer>
625
+ `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "ariaLabel", "type", "loading", "icon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogClose, selector: "[ktDialogClose]", inputs: ["ktDialogClose"] }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
626
+ }
627
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtAlertDialog, decorators: [{
628
+ type: Component,
629
+ args: [{
630
+ selector: 'kt-alert-dialog',
631
+ changeDetection: ChangeDetectionStrategy.OnPush,
632
+ imports: [KtButton, KtDialogImports],
633
+ template: `
634
+ <h2 ktDialogTitle>{{ data.title }}</h2>
635
+ <div ktDialogDescription>
636
+ @if (isMessageArray()) {
637
+ @for (line of messageArray(); track line) {
638
+ <p [innerHTML]="line"></p>
639
+ }
640
+ } @else {
641
+ <p [innerHTML]="messageString()"></p>
642
+ }
643
+ </div>
644
+ <footer ktDialogActions>
645
+ <button ktButton ktDialogFocusInitial ktDialogClose>
646
+ {{ data.closeLabel || 'Fermer' }}
647
+ </button>
648
+ </footer>
649
+ `,
650
+ }]
651
+ }] });
652
+ /** Ouvreur co-localisé officiel pour l'alerte. Utilise la présentation responsive par défaut. */
653
+ const injectAlertDialog = () => alertDialog.injectOpener(KtAlertDialog, { presentation: 'centered-sheet' });
654
+ const confirmDialog = defineKtDialog();
655
+ /**
656
+ * Composant interne de confirmation/décision générique.
657
+ * Supporte le mode binaire (Oui/Non) et le mode ternaire (Oui/Non/Annuler).
658
+ * Gère l'empilement vertical des boutons et les messages HTML multi-lignes.
659
+ */
660
+ class KtConfirmDialog {
661
+ data = confirmDialog.injectData();
662
+ ref = confirmDialog.injectRef();
663
+ isMessageArray() {
664
+ return Array.isArray(this.data.message);
665
+ }
666
+ messageArray() {
667
+ return Array.isArray(this.data.message) ? this.data.message : [];
668
+ }
669
+ messageString() {
670
+ return typeof this.data.message === 'string' ? this.data.message : '';
671
+ }
672
+ confirm() {
673
+ this.ref.close('confirm');
674
+ }
675
+ reject() {
676
+ this.ref.close('reject');
677
+ }
678
+ cancel() {
679
+ this.ref.close('cancel');
680
+ }
681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtConfirmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
682
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: KtConfirmDialog, isStandalone: true, selector: "kt-confirm-dialog", ngImport: i0, template: `
683
+ <h2 ktDialogTitle>{{ data.title }}</h2>
684
+ <div ktDialogDescription>
685
+ @if (isMessageArray()) {
686
+ @for (line of messageArray(); track line) {
687
+ <p [innerHTML]="line"></p>
688
+ }
689
+ } @else {
690
+ <p [innerHTML]="messageString()"></p>
691
+ }
692
+ </div>
693
+ <footer ktDialogActions>
694
+ @if (data.cancelLabel) {
695
+ <button ktButton mode="text" ktDialogFocusInitial (click)="cancel()">
696
+ {{ data.cancelLabel }}
697
+ </button>
698
+ <button ktButton mode="text" (click)="reject()">
699
+ {{ data.rejectLabel || 'Non' }}
700
+ </button>
701
+ } @else {
702
+ <button ktButton mode="text" ktDialogFocusInitial (click)="reject()">
703
+ {{ data.rejectLabel || 'Non' }}
704
+ </button>
705
+ }
706
+ <button ktButton [color]="data.color || 'primary'" (click)="confirm()">
707
+ {{ data.confirmLabel || 'Oui' }}
708
+ </button>
709
+ </footer>
710
+ `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "ariaLabel", "type", "loading", "icon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
711
+ }
712
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtConfirmDialog, decorators: [{
713
+ type: Component,
714
+ args: [{
715
+ selector: 'kt-confirm-dialog',
716
+ changeDetection: ChangeDetectionStrategy.OnPush,
717
+ imports: [KtButton, KtDialogImports],
718
+ template: `
719
+ <h2 ktDialogTitle>{{ data.title }}</h2>
720
+ <div ktDialogDescription>
721
+ @if (isMessageArray()) {
722
+ @for (line of messageArray(); track line) {
723
+ <p [innerHTML]="line"></p>
724
+ }
725
+ } @else {
726
+ <p [innerHTML]="messageString()"></p>
727
+ }
728
+ </div>
729
+ <footer ktDialogActions>
730
+ @if (data.cancelLabel) {
731
+ <button ktButton mode="text" ktDialogFocusInitial (click)="cancel()">
732
+ {{ data.cancelLabel }}
733
+ </button>
734
+ <button ktButton mode="text" (click)="reject()">
735
+ {{ data.rejectLabel || 'Non' }}
736
+ </button>
737
+ } @else {
738
+ <button ktButton mode="text" ktDialogFocusInitial (click)="reject()">
739
+ {{ data.rejectLabel || 'Non' }}
740
+ </button>
741
+ }
742
+ <button ktButton [color]="data.color || 'primary'" (click)="confirm()">
743
+ {{ data.confirmLabel || 'Oui' }}
744
+ </button>
745
+ </footer>
746
+ `,
747
+ }]
748
+ }] });
749
+ /** Ouvreur co-localisé officiel pour la confirmation/décision. Utilise la présentation responsive par défaut. */
750
+ const injectConfirmDialog = () => confirmDialog.injectOpener(KtConfirmDialog, { presentation: 'centered-sheet' });
751
+ /**
752
+ * Service d'aide global pour l'ouverture simplifiée de boîtes de dialogue d'alerte et de confirmation.
753
+ * S'appuie sur les ouvreurs de dialogues officiels co-localisés.
754
+ * Propose une présentation adaptative (centrée sur grand écran, bottom-sheet sur mobile).
755
+ */
756
+ class KtQuickDialog {
757
+ openAlert = injectAlertDialog();
758
+ openConfirm = injectConfirmDialog();
759
+ /**
760
+ * Ouvre une boîte de dialogue d'alerte simple.
761
+ *
762
+ * @param title Titre de l'alerte.
763
+ * @param message Message d'explication (string simple ou tableau de strings pour du multi-lignes HTML).
764
+ * @param closeLabel Libellé du bouton de fermeture.
765
+ * @returns La référence DialogRef de la modale ouverte.
766
+ */
767
+ alert(title, message, closeLabel) {
768
+ return this.openAlert({ title, message, closeLabel });
769
+ }
770
+ /**
771
+ * Ouvre une boîte de dialogue de confirmation binaire (Oui/Non).
772
+ *
773
+ * @param config Options de configuration (titre, message, libellés de boutons, couleur).
774
+ * @returns Un Observable émettant true si l'utilisateur valide (Oui),
775
+ * false s'il rejette (Non), et undefined si la boîte est fermée en cliquant en dehors ou via Échap.
776
+ */
777
+ confirm(config) {
778
+ const dialogRef = this.openConfirm({
779
+ ...config,
780
+ cancelLabel: '', // Pas de bouton d'annulation en mode binaire
781
+ });
782
+ return dialogRef.closed.pipe(map((result) => {
783
+ if (result === 'confirm')
784
+ return true;
785
+ if (result === 'reject')
786
+ return false;
787
+ return undefined;
788
+ }));
789
+ }
790
+ /**
791
+ * Ouvre une boîte de dialogue de décision ternaire (Oui/Non/Annuler).
792
+ *
793
+ * @param config Options de configuration (titre, message, libellés de boutons dont cancelLabel obligatoire).
794
+ * @returns Un Observable émettant 'confirm', 'reject', 'cancel' ou undefined si fermeture externe.
795
+ */
796
+ decide(config) {
797
+ const dialogRef = this.openConfirm(config);
798
+ return dialogRef.closed;
799
+ }
800
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtQuickDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
801
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtQuickDialog, providedIn: 'root' });
802
+ }
803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtQuickDialog, decorators: [{
804
+ type: Injectable,
805
+ args: [{
806
+ providedIn: 'root',
807
+ }]
808
+ }] });
809
+
590
810
  /**
591
811
  * Import ergonomique de toute la famille dialog en une fois :
592
812
  * `imports: [KtDialogImports]` au lieu d'énumérer chaque directive structurelle.
@@ -606,5 +826,5 @@ const KtDialogImports = [
606
826
  * Generated bundle index. Do not edit.
607
827
  */
608
828
 
609
- export { KT_DIALOG_AAA_DEFAULTS, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, defineKtDialog, injectKtDialogOpener, provideKtDialogDefaults, resolveKtDialogPanelClass };
829
+ export { KT_DIALOG_AAA_DEFAULTS, KtAlertDialog, KtConfirmDialog, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, KtQuickDialog, defineKtDialog, injectAlertDialog, injectConfirmDialog, injectKtDialogOpener, provideKtDialogDefaults, resolveKtDialogPanelClass };
610
830
  //# sourceMappingURL=ktortu-aaa-dialog.mjs.map