@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 +6 -0
- package/fesm2022/ktortu-aaa-card.mjs +109 -105
- package/fesm2022/ktortu-aaa-card.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-dialog.mjs +222 -2
- package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-disclosure.mjs +47 -45
- package/fesm2022/ktortu-aaa-disclosure.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-forms.mjs +10 -13
- package/fesm2022/ktortu-aaa-forms.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ktortu-aaa-card.d.ts +68 -67
- package/types/ktortu-aaa-dialog.d.ts +128 -2
- package/types/ktortu-aaa-disclosure.d.ts +18 -9
- package/types/ktortu-aaa-forms.d.ts +3 -2
package/dialog/dialog.css
CHANGED
|
@@ -1,8 +1,103 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
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`
|
|
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
|
-
|
|
209
|
+
ngAfterViewInit() {
|
|
115
210
|
// Garde-fou a11y : un lien étiré sans nom accessible n'est pas annonçable.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
}]
|
|
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
|