@ktortu/aaa 0.9.2 → 0.9.4
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/disclosure/disclosure-tokens.css +18 -0
- package/disclosure/disclosure.css +84 -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/fesm2022/ktortu-aaa.mjs +1 -0
- package/fesm2022/ktortu-aaa.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/types/ktortu-aaa.d.ts +1 -0
package/dialog/dialog.css
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Design tokens du disclosure (famille ktDisclosure) — contrat public de theming.
|
|
2
|
+
|
|
3
|
+
Même architecture que --card-* / --snackbar-* : tout DÉRIVE du socle --kt-*.
|
|
4
|
+
Chargé APRÈS foundation.css (qui pose les --kt-*). */
|
|
5
|
+
@layer kt-aaa.tokens {
|
|
6
|
+
:root {
|
|
7
|
+
/* Durée de l'animation d'ouverture/fermeture (grid-template-rows). Neutralisée sous
|
|
8
|
+
prefers-reduced-motion. */
|
|
9
|
+
--disclosure-anim-duration: 0.2s;
|
|
10
|
+
|
|
11
|
+
/* Cible tactile minimale du déclencheur (WCAG 2.5.5, AAA). */
|
|
12
|
+
--disclosure-toggle-min-size: 44px;
|
|
13
|
+
|
|
14
|
+
/* Chevron (input `chevron` de [ktDisclosureToggle], affiché par défaut). */
|
|
15
|
+
--disclosure-chevron-size: 1em;
|
|
16
|
+
--disclosure-chevron-gap: 0.5rem;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
/* Styles de la famille Disclosure ([ktDisclosure] + [ktDisclosureToggle] + kt-disclosure-content).
|
|
3
|
+
Global : enregistré comme card.css / button.css (les directives se posent sur des éléments
|
|
4
|
+
arbitraires, hors de toute vue de composant). Tokens (--disclosure-*) dans disclosure-tokens.css.
|
|
5
|
+
|
|
6
|
+
Animation = grid-template-rows 0fr ↔ 1fr : anime la hauteur AUTO réelle, sans magic number ni
|
|
7
|
+
mesure JS (cf. ADR-0004). Le panneau passe `inert` quand il est fermé (géré côté composant).
|
|
8
|
+
NB perf : animer grid-template-rows est layout-bound (reflow du sous-arbre par frame, non
|
|
9
|
+
composité). OK pour un geste ponctuel ; éviter sur des panneaux très lourds en contexte mobile. */
|
|
10
|
+
|
|
11
|
+
/* ============ PANNEAU (kt-disclosure-content) ============ */
|
|
12
|
+
kt-disclosure-content {
|
|
13
|
+
display: grid;
|
|
14
|
+
/* Replié par défaut : la rangée fait 0fr (clippée par le wrapper interne). */
|
|
15
|
+
grid-template-rows: 0fr;
|
|
16
|
+
}
|
|
17
|
+
kt-disclosure-content[data-expanded='true'] {
|
|
18
|
+
grid-template-rows: 1fr;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Wrapper interne fourni par le template : c'est lui qui clippe le contenu. `min-block-size: 0`
|
|
22
|
+
est indispensable pour qu'une rangée de grille 0fr puisse réellement se réduire sous la taille
|
|
23
|
+
min-content du contenu. Mettez vos paddings ICI (sur le contenu projeté), pas en marge directe
|
|
24
|
+
du panneau, pour éviter une fuite de marge pendant l'animation. */
|
|
25
|
+
.kt-disclosure-content__inner {
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
min-block-size: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ============ DÉCLENCHEUR ([ktDisclosureToggle]) ============
|
|
31
|
+
Le bouton appartient au consommateur (peut être un ktButton). On reste discret (curseur). Le
|
|
32
|
+
focus visible est assuré par le bouton lui-même (UA ou ktButton). On garantit en revanche la
|
|
33
|
+
cible tactile AAA (WCAG 2.5.5) même sur un <button> nu — surchargeable par token ; un ktButton
|
|
34
|
+
dépasse déjà ce minimum. L'inline-size reste piloté par le contenu (un bouton icône-seule doit
|
|
35
|
+
fournir sa largeur ET un nom accessible). */
|
|
36
|
+
[ktDisclosureToggle] {
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
min-block-size: var(--disclosure-toggle-min-size, 44px);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ============ CHEVRON (input `chevron` de [ktDisclosureToggle], affiché par défaut) ============ */
|
|
42
|
+
[ktDisclosureToggle].kt-disclosure-toggle--chevron {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: var(--disclosure-chevron-gap, 0.5rem);
|
|
46
|
+
}
|
|
47
|
+
/* Icône chevron MASQUÉE, centrée dans son carré : reste optiquement centrée en replié comme en
|
|
48
|
+
déplié (simple rotation 180°, centre inchangé). Un carré bordé tourné de 45°/225° décalerait le
|
|
49
|
+
tracé verticalement entre les deux états. Teintée via currentColor. Décoratif : le pseudo-élément
|
|
50
|
+
est hors de l'arbre d'accessibilité (l'état réel est porté par aria-expanded). */
|
|
51
|
+
[ktDisclosureToggle].kt-disclosure-toggle--chevron::after {
|
|
52
|
+
content: '';
|
|
53
|
+
inline-size: var(--disclosure-chevron-size, 1em);
|
|
54
|
+
block-size: var(--disclosure-chevron-size, 1em);
|
|
55
|
+
background-color: currentColor;
|
|
56
|
+
-webkit-mask: center / contain no-repeat
|
|
57
|
+
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
|
|
58
|
+
mask: center / contain no-repeat
|
|
59
|
+
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
|
|
60
|
+
}
|
|
61
|
+
[ktDisclosureToggle][aria-expanded='true'].kt-disclosure-toggle--chevron::after {
|
|
62
|
+
/* Pointe vers le haut une fois déplié (rotation 180°, centre inchangé). */
|
|
63
|
+
rotate: 180deg;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* ============ Mouvement réduit (WCAG 2.3.3) ============ */
|
|
67
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
68
|
+
kt-disclosure-content {
|
|
69
|
+
transition: grid-template-rows var(--disclosure-anim-duration, 0.2s) ease;
|
|
70
|
+
}
|
|
71
|
+
[ktDisclosureToggle].kt-disclosure-toggle--chevron::after {
|
|
72
|
+
transition: rotate var(--disclosure-anim-duration, 0.2s) ease;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ============ Contraste élevé Windows / forced-colors ============
|
|
77
|
+
Un élément masqué teinté via background-color peut s'effacer en forced-colors : on force une
|
|
78
|
+
couleur système pour que le chevron (seul indice visuel d'état avec aria-expanded) reste visible. */
|
|
79
|
+
@media (forced-colors: active) {
|
|
80
|
+
[ktDisclosureToggle].kt-disclosure-toggle--chevron::after {
|
|
81
|
+
background-color: CanvasText;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -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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"}
|