@ktortu/aaa 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +5 -3
  2. package/fesm2022/ktortu-aaa-button.mjs +10 -2
  3. package/fesm2022/ktortu-aaa-button.mjs.map +1 -1
  4. package/fesm2022/ktortu-aaa-card.mjs +14 -5
  5. package/fesm2022/ktortu-aaa-card.mjs.map +1 -1
  6. package/fesm2022/ktortu-aaa-cdk.mjs +50 -2
  7. package/fesm2022/ktortu-aaa-cdk.mjs.map +1 -1
  8. package/fesm2022/ktortu-aaa-dialog.mjs +80 -39
  9. package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -1
  10. package/fesm2022/ktortu-aaa-disclosure.mjs +201 -0
  11. package/fesm2022/ktortu-aaa-disclosure.mjs.map +1 -0
  12. package/fesm2022/ktortu-aaa-forms.mjs +525 -149
  13. package/fesm2022/ktortu-aaa-forms.mjs.map +1 -1
  14. package/fesm2022/ktortu-aaa-i18n.mjs +27 -2
  15. package/fesm2022/ktortu-aaa-i18n.mjs.map +1 -1
  16. package/fesm2022/ktortu-aaa-menu.mjs.map +1 -1
  17. package/fesm2022/ktortu-aaa-progress-bar.mjs +91 -0
  18. package/fesm2022/ktortu-aaa-progress-bar.mjs.map +1 -0
  19. package/fesm2022/ktortu-aaa-snackbar.mjs +478 -0
  20. package/fesm2022/ktortu-aaa-snackbar.mjs.map +1 -0
  21. package/fesm2022/ktortu-aaa-tabs.mjs.map +1 -1
  22. package/fesm2022/ktortu-aaa-tooltip.mjs +20 -30
  23. package/fesm2022/ktortu-aaa-tooltip.mjs.map +1 -1
  24. package/fesm2022/ktortu-aaa.mjs +2 -0
  25. package/fesm2022/ktortu-aaa.mjs.map +1 -1
  26. package/forms/checkbox/checkbox-group.css +0 -8
  27. package/forms/chips/chip-list.css +5 -0
  28. package/forms/field/field.css +16 -0
  29. package/forms/radio/radio-group.css +1 -9
  30. package/forms/styles/field-box.css +14 -6
  31. package/forms/styles/field-pending.css +46 -0
  32. package/forms/styles/select-panel.css +4 -0
  33. package/package.json +13 -1
  34. package/progress-bar/progress-bar.css +127 -0
  35. package/snackbar/snackbar-tokens.css +53 -0
  36. package/snackbar/snackbar.css +175 -0
  37. package/styles/disclosure.css +6 -0
  38. package/styles/forms.css +1 -0
  39. package/styles/progress-bar.css +5 -0
  40. package/styles/snackbar.css +9 -0
  41. package/styles/styles.css +3 -0
  42. package/themes/theme-cyberpunk.css +11 -0
  43. package/tooltip/tooltip.css +1 -1
  44. package/types/ktortu-aaa-button.d.ts +2 -0
  45. package/types/ktortu-aaa-card.d.ts +3 -0
  46. package/types/ktortu-aaa-cdk.d.ts +27 -1
  47. package/types/ktortu-aaa-dialog.d.ts +89 -34
  48. package/types/ktortu-aaa-disclosure.d.ts +112 -0
  49. package/types/ktortu-aaa-forms.d.ts +238 -30
  50. package/types/ktortu-aaa-i18n.d.ts +3 -0
  51. package/types/ktortu-aaa-progress-bar.d.ts +27 -0
  52. package/types/ktortu-aaa-snackbar.d.ts +218 -0
  53. package/types/ktortu-aaa-tooltip.d.ts +0 -1
  54. package/types/ktortu-aaa.d.ts +2 -0
@@ -0,0 +1,127 @@
1
+ :host {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ gap: var(--kt-progress-bar-gap, 8px);
6
+ }
7
+
8
+ .kt-progress-bar-label {
9
+ font-family: var(--kt-font-family, sans-serif);
10
+ font-size: var(--kt-font-size-sm, 0.875rem);
11
+ color: var(--kt-text-color-secondary, var(--kt-neutral-dark, #555));
12
+ text-align: var(--kt-progress-bar-label-align, start);
13
+ }
14
+
15
+ /* Si le label est vide (pas d'input et ng-content vide), on le masque pour éviter le gap */
16
+ .kt-progress-bar-label:empty {
17
+ display: none;
18
+ }
19
+
20
+ .kt-progress-bar-track {
21
+ width: 100%;
22
+ height: var(--kt-progress-bar-height, 4px);
23
+ overflow: hidden;
24
+ background-color: var(--kt-progress-bar-track-color, var(--kt-neutral-light, #e0e0e0));
25
+ border-radius: var(--kt-progress-bar-border-radius, 2px);
26
+ position: relative;
27
+ }
28
+
29
+ .kt-progress-bar-indicator {
30
+ position: absolute;
31
+ top: 0;
32
+ bottom: 0;
33
+ left: 0;
34
+ right: 0;
35
+ background-color: var(--kt-progress-bar-indicator-color, var(--kt-primary, #0056b3));
36
+ transform-origin: top left;
37
+ transition: transform 250ms ease;
38
+ will-change: transform;
39
+ }
40
+
41
+ /* Mode indéterminé : triple animation parallèle dissociée */
42
+ :host([data-mode='indeterminate']) .kt-progress-bar-indicator {
43
+ animation:
44
+ kt-progress-bar-indeterminate-translate 2.2s infinite cubic-bezier(0.4, 0, 0.2, 1),
45
+ kt-progress-bar-indeterminate-scale 2.2s infinite ease-in-out,
46
+ kt-progress-bar-indeterminate-fade 2.2s infinite linear;
47
+ transform-origin: left;
48
+ }
49
+
50
+ /* Masquage visuel accessible (sr-only) */
51
+ .kt-sr-only {
52
+ position: absolute;
53
+ width: 1px;
54
+ height: 1px;
55
+ padding: 0;
56
+ margin: -1px;
57
+ overflow: hidden;
58
+ clip: rect(0, 0, 0, 0);
59
+ white-space: nowrap;
60
+ border: 0;
61
+ }
62
+
63
+ @keyframes kt-progress-bar-indeterminate-translate {
64
+ 0% {
65
+ translate: -100% 0;
66
+ }
67
+ 100% {
68
+ translate: 100% 0;
69
+ }
70
+ }
71
+
72
+ @keyframes kt-progress-bar-indeterminate-scale {
73
+ 0% {
74
+ scale: 0.15 1;
75
+ }
76
+ 35% {
77
+ scale: 0.7 1;
78
+ }
79
+ 70% {
80
+ scale: 0.4 1;
81
+ }
82
+ 100% {
83
+ scale: 0.15 1;
84
+ }
85
+ }
86
+
87
+ @keyframes kt-progress-bar-indeterminate-fade {
88
+ 0% {
89
+ opacity: 0;
90
+ }
91
+ 15% {
92
+ opacity: 1;
93
+ }
94
+ 85% {
95
+ opacity: 1;
96
+ }
97
+ 100% {
98
+ opacity: 0;
99
+ }
100
+ }
101
+
102
+ /* Support pour prefers-reduced-motion ou bypass réduit manuel */
103
+ @media (prefers-reduced-motion: reduce) {
104
+ :host([data-mode='indeterminate']) .kt-progress-bar-indicator {
105
+ animation: kt-progress-bar-pulsate 2s infinite ease-in-out;
106
+ transform: none;
107
+ transform-origin: center;
108
+ }
109
+ }
110
+
111
+ .kt-progress-bar-indicator.kt-reduced-motion {
112
+ animation: kt-progress-bar-pulsate 2s infinite ease-in-out !important;
113
+ transform: none !important;
114
+ transform-origin: center !important;
115
+ }
116
+
117
+ @keyframes kt-progress-bar-pulsate {
118
+ 0% {
119
+ opacity: 0.3;
120
+ }
121
+ 50% {
122
+ opacity: 0.8;
123
+ }
124
+ 100% {
125
+ opacity: 0.3;
126
+ }
127
+ }
@@ -0,0 +1,53 @@
1
+ /* Design tokens de la snackbar (service KtSnackbar) — contrat public de theming.
2
+
3
+ PRINCIPE (identique au reste de la lib) : tout DÉRIVE du socle --kt-*. Surcharger un --kt-*
4
+ rethématise la snackbar en même temps que les champs, boutons et dialog. Chaque valeur reste
5
+ surchargeable en redéclarant son token --snackbar-* (sur :root ou un conteneur de spécificité ≥).
6
+
7
+ La snackbar est posée sur une « surface inversée » (fond sombre, texte clair) façon Material —
8
+ d'où des on-* dérivés de --kt-on-surface / --kt-surface, qui suivent automatiquement un thème.
9
+ Les variantes ne recolorisent PAS la pastille (le texte reste à contraste AAA) : elles ne pilotent
10
+ que l'accent + l'icône. Un thème qui veut des pastilles pleinement colorées surcharge simplement
11
+ --snackbar-bg / --snackbar-fg sous [data-variant='…']. */
12
+ @layer kt-aaa.tokens {
13
+ :root {
14
+ /* Surface inversée : fond ~ on-surface, texte ~ surface (contraste ≈ 16:1 → AAA 1.4.6). */
15
+ --snackbar-bg: color-mix(in oklab, var(--kt-on-surface, #1f1f1f) 92%, var(--kt-surface, #ffffff));
16
+ --snackbar-fg: var(--kt-surface, #ffffff);
17
+
18
+ --snackbar-radius: var(--kt-control-radius, 8px);
19
+ --snackbar-shadow: 0 4px 12px rgb(0 0 0 / 24%);
20
+
21
+ /* Géométrie interne */
22
+ --snackbar-padding-block: 0.625rem;
23
+ --snackbar-padding-inline: 1rem;
24
+ --snackbar-gap: 0.75rem;
25
+ --snackbar-font: var(--kt-control-font, 1rem);
26
+
27
+ /* Placement : distance au bord du viewport, avec marge de sécurité mobile (encoches / barres). */
28
+ --snackbar-offset: 16px;
29
+ --snackbar-offset-bottom: calc(var(--snackbar-offset) + env(safe-area-inset-bottom, 0px));
30
+ --snackbar-offset-top: calc(var(--snackbar-offset) + env(safe-area-inset-top, 0px));
31
+ --snackbar-max-inline-size: 560px;
32
+
33
+ /* Bouton de fermeture : cible 44px (AAA 2.5.5). */
34
+ --snackbar-close-size: var(--kt-control-height, 44px);
35
+ --snackbar-close-icon-size: 1rem;
36
+
37
+ /* Variantes : accent (icône) clair pour contraster sur la pastille sombre (≥ 3:1), et glyphe
38
+ (ligature de la police d'icônes --kt-icon-font). Couleur + FORME : la forme distincte est
39
+ l'indice non coloré (WCAG 1.4.1). Surchargez accent/glyphe pour rebrander une variante. */
40
+ --snackbar-icon-size: 1.25rem;
41
+ --snackbar-accent-info: #8ab4f8;
42
+ --snackbar-accent-success: #81c995;
43
+ --snackbar-accent-warning: #fdd663;
44
+ --snackbar-accent-error: #f28b82;
45
+ --snackbar-glyph-info: 'info';
46
+ --snackbar-glyph-success: 'check_circle';
47
+ --snackbar-glyph-warning: 'warning';
48
+ --snackbar-glyph-error: 'error';
49
+
50
+ /* Animation d'entrée/sortie. Token de durée surchargeable ; neutralisée en prefers-reduced-motion. */
51
+ --snackbar-anim-duration: 150ms;
52
+ }
53
+ }
@@ -0,0 +1,175 @@
1
+ @layer kt-aaa.components {
2
+ /* Styles de la snackbar (conteneur monté dans un overlay CDK).
3
+ Global : l'élément est ajouté hors de toute vue (overlay top-layer).
4
+ Tokens (--snackbar-*) définis dans snackbar-tokens.css (chargé avant ce fichier).
5
+
6
+ Requiert côté hôte : '@angular/cdk/overlay-prebuilt.css' (positionnement de l'overlay) ET
7
+ '@angular/cdk/a11y-prebuilt.css' (masque l'élément du LiveAnnouncer qui porte l'annonce). */
8
+
9
+ /* Le pane d'overlay ne capte pas les clics hors de la snackbar (feedback NON bloquant). */
10
+ .kt-snackbar-pane {
11
+ pointer-events: none;
12
+ max-inline-size: 100vw;
13
+ }
14
+
15
+ .kt-snackbar {
16
+ pointer-events: auto; /* la snackbar elle-même reste survolable / cliquable */
17
+ display: flex;
18
+ align-items: center;
19
+ gap: var(--snackbar-gap);
20
+ box-sizing: border-box;
21
+
22
+ margin: var(--snackbar-offset);
23
+ inline-size: max-content;
24
+ max-inline-size: min(var(--snackbar-max-inline-size), 100vw - 2 * var(--snackbar-offset));
25
+
26
+ padding-block: var(--snackbar-padding-block);
27
+ padding-inline: var(--snackbar-padding-inline);
28
+
29
+ border-radius: var(--snackbar-radius);
30
+ background: var(--snackbar-bg);
31
+ color: var(--snackbar-fg);
32
+ box-shadow: var(--snackbar-shadow);
33
+
34
+ font-size: var(--snackbar-font);
35
+ line-height: 1.4;
36
+ }
37
+
38
+ /* Marge de sécurité mobile (encoches / barres système) selon le bord d'ancrage. */
39
+ .kt-snackbar-pane--bottom .kt-snackbar {
40
+ margin-block-end: var(--snackbar-offset-bottom);
41
+ }
42
+ .kt-snackbar-pane--top .kt-snackbar {
43
+ margin-block-start: var(--snackbar-offset-top);
44
+ }
45
+
46
+ .kt-snackbar__message {
47
+ flex: 1 1 auto;
48
+ min-inline-size: 0; /* autorise le retour à la ligne d'un message long */
49
+ }
50
+
51
+ /* ============ ICÔNE DE VARIANTE (décorative, aria-hidden) ============ */
52
+ /* Couleur = accent de la variante ; glyphe = ligature de la police d'icônes (comme [ktButton]).
53
+ Si la police d'icônes est absente, la ligature dégrade en texte (même comportement que le bouton). */
54
+ .kt-snackbar__icon {
55
+ flex: none;
56
+ display: inline-grid;
57
+ place-items: center;
58
+ color: var(--snackbar-accent, currentColor);
59
+ }
60
+ .kt-snackbar__icon::before {
61
+ content: var(--snackbar-glyph, '');
62
+ font-family: var(--kt-icon-font, 'Material Symbols Outlined');
63
+ font-weight: var(--kt-icon-font-weight, normal);
64
+ font-style: normal;
65
+ font-size: var(--snackbar-icon-size, 1.25rem);
66
+ line-height: 1;
67
+ font-feature-settings: 'liga';
68
+ -webkit-font-smoothing: antialiased;
69
+ }
70
+
71
+ /* Mapping variante → accent + glyphe (un thème surcharge accent/glyphe, ou bg/fg pour recolorer). */
72
+ .kt-snackbar[data-variant='info'] {
73
+ --snackbar-accent: var(--snackbar-accent-info);
74
+ --snackbar-glyph: var(--snackbar-glyph-info);
75
+ }
76
+ .kt-snackbar[data-variant='success'] {
77
+ --snackbar-accent: var(--snackbar-accent-success);
78
+ --snackbar-glyph: var(--snackbar-glyph-success);
79
+ }
80
+ .kt-snackbar[data-variant='warning'] {
81
+ --snackbar-accent: var(--snackbar-accent-warning);
82
+ --snackbar-glyph: var(--snackbar-glyph-warning);
83
+ }
84
+ .kt-snackbar[data-variant='error'] {
85
+ --snackbar-accent: var(--snackbar-accent-error);
86
+ --snackbar-glyph: var(--snackbar-glyph-error);
87
+ }
88
+
89
+ /* ============ BOUTON DE FERMETURE ============ */
90
+ /* Cible 44px, croix dessinée en CSS (zéro dépendance à une police d'icônes). */
91
+ .kt-snackbar__close {
92
+ position: relative;
93
+ flex: none;
94
+ inline-size: var(--snackbar-close-size);
95
+ block-size: var(--snackbar-close-size);
96
+ /* compense optiquement le padding inline du conteneur, sans réduire la cible de 44px */
97
+ margin-inline-end: calc(-0.5 * var(--snackbar-padding-inline));
98
+
99
+ display: inline-grid;
100
+ place-items: center;
101
+ padding: 0;
102
+ border: 0;
103
+ border-radius: 50%;
104
+ background: transparent;
105
+ color: inherit;
106
+ cursor: pointer;
107
+ }
108
+
109
+ .kt-snackbar__close::before,
110
+ .kt-snackbar__close::after {
111
+ content: '';
112
+ position: absolute;
113
+ inline-size: var(--snackbar-close-icon-size);
114
+ block-size: 2px;
115
+ border-radius: 2px;
116
+ background: currentColor;
117
+ translate: 0 0;
118
+ }
119
+ .kt-snackbar__close::before {
120
+ rotate: 45deg;
121
+ }
122
+ .kt-snackbar__close::after {
123
+ rotate: -45deg;
124
+ }
125
+
126
+ .kt-snackbar__close:hover {
127
+ background: color-mix(in srgb, currentColor 14%, transparent);
128
+ }
129
+ .kt-snackbar__close:focus-visible {
130
+ outline: var(--kt-focus-ring-width, 2px) solid currentColor;
131
+ outline-offset: 2px;
132
+ }
133
+
134
+ /* ============ ANIMATION D'ENTRÉE / SORTIE ============ */
135
+ /* Entrée par keyframe (glissement depuis le bord), sortie par transition (classe --leaving).
136
+ Toutes deux sont activées UNIQUEMENT hors prefers-reduced-motion → en reduced-motion la durée
137
+ de transition est nulle, donc playExit() démonte instantanément (WCAG 2.3.3). */
138
+ @media (prefers-reduced-motion: no-preference) {
139
+ .kt-snackbar {
140
+ transition:
141
+ opacity var(--snackbar-anim-duration) ease,
142
+ translate var(--snackbar-anim-duration) ease;
143
+ }
144
+ .kt-snackbar-pane--bottom .kt-snackbar {
145
+ animation: kt-snackbar-in-bottom var(--snackbar-anim-duration) ease-out;
146
+ }
147
+ .kt-snackbar-pane--top .kt-snackbar {
148
+ animation: kt-snackbar-in-top var(--snackbar-anim-duration) ease-out;
149
+ }
150
+
151
+ /* Sortie : on fond + on glisse vers le bord d'ancrage. */
152
+ .kt-snackbar--leaving {
153
+ opacity: 0;
154
+ }
155
+ .kt-snackbar-pane--bottom .kt-snackbar--leaving {
156
+ translate: 0 100%;
157
+ }
158
+ .kt-snackbar-pane--top .kt-snackbar--leaving {
159
+ translate: 0 -100%;
160
+ }
161
+ }
162
+
163
+ @keyframes kt-snackbar-in-bottom {
164
+ from {
165
+ opacity: 0;
166
+ translate: 0 100%;
167
+ }
168
+ }
169
+ @keyframes kt-snackbar-in-top {
170
+ from {
171
+ opacity: 0;
172
+ translate: 0 -100%;
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,6 @@
1
+ @layer kt-aaa.tokens, kt-aaa.base, kt-aaa.components;
2
+
3
+ /* Disclosure (ktDisclosure + déclencheur + panneau) : tokens + apparence. Nécessite le socle.
4
+ @import '@ktortu/aaa/disclosure.css'; */
5
+ @import '../disclosure/disclosure-tokens.css';
6
+ @import '../disclosure/disclosure.css';
package/styles/forms.css CHANGED
@@ -6,6 +6,7 @@
6
6
  @import '../forms/styles/tokens.css';
7
7
  @import '../forms/chips/tokens.css';
8
8
  @import '../forms/styles/field-box.css';
9
+ @import '../forms/styles/field-pending.css';
9
10
  @import '../forms/switch/switch.css';
10
11
  @import '../forms/checkbox/checkbox.css';
11
12
  @import '../forms/checkbox/checkbox-group.css';
@@ -0,0 +1,5 @@
1
+ @layer kt-aaa.tokens, kt-aaa.base, kt-aaa.components;
2
+
3
+ /* Barre de progression (kt-progress-bar) : apparence. Nécessite le socle (@ktortu/aaa/foundation.css).
4
+ @import '@ktortu/aaa/progress-bar.css'; */
5
+ @import '../progress-bar/progress-bar.css';
@@ -0,0 +1,9 @@
1
+ @layer kt-aaa.tokens, kt-aaa.base, kt-aaa.components;
2
+
3
+ /* Snackbar (service KtSnackbar, overlay CDK). Nécessite le socle (@ktortu/aaa/foundation.css),
4
+ ET côté hôte l'overlay CDK + le masquage du LiveAnnouncer :
5
+ @import '@angular/cdk/overlay-prebuilt.css';
6
+ @import '@angular/cdk/a11y-prebuilt.css';
7
+ @import '@ktortu/aaa/snackbar.css'; */
8
+ @import '../snackbar/snackbar-tokens.css';
9
+ @import '../snackbar/snackbar.css';
package/styles/styles.css CHANGED
@@ -22,3 +22,6 @@
22
22
  @import './tooltip.css';
23
23
  @import './dialog.css';
24
24
  @import './menu.css';
25
+ @import './snackbar.css';
26
+ @import './disclosure.css';
27
+ @import './progress-bar.css';
@@ -138,6 +138,10 @@
138
138
  --tab-radius: 6px 6px 0 0;
139
139
  --tab-border: none;
140
140
  --tab-margin: 0 4px 0 0;
141
+
142
+ /* ============ Progress Bar ============ */
143
+ --kt-progress-bar-track-color: #10182a;
144
+ --kt-progress-bar-indicator-color: transparent;
141
145
  }
142
146
 
143
147
  /* Fills DUOTONE cyan→magenta (ex-Synthwave) : niveau élément pour substituer var(--btn-fill) ;
@@ -158,6 +162,13 @@
158
162
  --btn-shadow-focus: 0 0 16px color-mix(in srgb, var(--btn-fg) 75%, transparent);
159
163
  }
160
164
 
165
+ [data-theme='cyberpunk'] .kt-progress-bar-indicator {
166
+ background-image: linear-gradient(90deg, #28d6ef, #ff3d8b);
167
+ box-shadow:
168
+ 0 0 8px rgb(40 214 239 / 60%),
169
+ 0 0 12px rgb(255 61 139 / 40%);
170
+ }
171
+
161
172
  /* LASER en bord HAUT des cartes : ligne néon qui balaie (signature « partout »). Le pseudo
162
173
  est libre (le lien étiré utilise ::after du lien, pas de la carte). z-index au-dessus du média. */
163
174
  [data-theme='cyberpunk'] [ktCard] {
@@ -28,7 +28,7 @@
28
28
  white-space: pre-line; /* Interprète les \n et les sauts de ligne dans les chaînes de texte */
29
29
  box-shadow: 0 4px 12px color-mix(in srgb, #000 25%, transparent);
30
30
  pointer-events: auto; /* requis pour le « hoverable » (WCAG 1.4.13) */
31
- user-select: text; /* Corrigé : Permet la sélection et traduction du texte */
31
+ user-select: text; /* Autorise la sélection et la traduction du texte du tooltip. */
32
32
  cursor: default;
33
33
  }
34
34
 
@@ -44,7 +44,9 @@ declare class KtButton implements AfterViewInit {
44
44
  private readonly config;
45
45
  private readonly host;
46
46
  private readonly platformId;
47
+ private isDestroyed;
47
48
  private readonly viewInitialized;
49
+ private readonly auditEnabled;
48
50
  protected readonly isLink: boolean;
49
51
  private readonly nativeAriaLabel;
50
52
  /** Emphase visuelle du bouton (apparence, ≠ `type`). @default 'filled' (ou KT_BUTTON_CONFIG.mode) */
@@ -80,6 +80,7 @@ declare class KtCardActions {
80
80
  */
81
81
  declare class KtCardLink {
82
82
  private readonly host;
83
+ private readonly auditEnabled;
83
84
  /**
84
85
  * Carte ancêtre, injectée optionnellement via `inject(Card, { optional: true })` (DI par
85
86
  * hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer
@@ -137,6 +138,7 @@ declare class KtCard {
137
138
  private readonly config;
138
139
  private readonly host;
139
140
  private readonly platformId;
141
+ private readonly auditEnabled;
140
142
  /** Référence réactive sur le lien primaire de la carte */
141
143
  readonly cardLink: i0.Signal<KtCardLink | undefined>;
142
144
  /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */
@@ -148,6 +150,7 @@ declare class KtCard {
148
150
  readonly interactive: i0.InputSignalWithTransform<boolean, unknown>;
149
151
  /** Rend la surface inerte (état `data-disabled`). @default false */
150
152
  readonly disabled: i0.InputSignalWithTransform<boolean, unknown>;
153
+ private isDestroyed;
151
154
  constructor();
152
155
  static ɵfac: i0.ɵɵFactoryDeclaration<KtCard, never>;
153
156
  static ɵdir: i0.ɵɵDirectiveDeclaration<KtCard, "[ktCard]", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, ["cardLink"], never, true, never>;
@@ -42,6 +42,26 @@ interface KtSheetDragOptions {
42
42
  */
43
43
  declare function createKtSheetDrag(opts: KtSheetDragOptions): KtSheetDrag;
44
44
 
45
+ /**
46
+ * Verrou de défilement du `<body>` à COMPTEUR DE RÉFÉRENCES, partagé (`providedIn: 'root'`).
47
+ *
48
+ * Plusieurs surfaces modales simultanées (sheets, popups) peuvent verrouiller le scroll du fond :
49
+ * `overflow: hidden` n'est posé qu'au premier `lock()` et restauré (à sa valeur d'origine) qu'au
50
+ * dernier `unlock()`. Sans ce compteur, deux consommateurs par-instance se désynchronisent — la
51
+ * fermeture du premier restaure le scroll alors que le second est encore ouvert.
52
+ */
53
+ declare class KtBodyScrollLock {
54
+ private readonly doc;
55
+ private count;
56
+ private previousOverflow;
57
+ /** Pose un verrou (premier appelant : mémorise puis force `overflow: hidden`). */
58
+ lock(): void;
59
+ /** Relâche un verrou ; restaure l'`overflow` d'origine quand le dernier consommateur relâche. */
60
+ unlock(): void;
61
+ static ɵfac: i0.ɵɵFactoryDeclaration<KtBodyScrollLock, never>;
62
+ static ɵprov: i0.ɵɵInjectableDeclaration<KtBodyScrollLock>;
63
+ }
64
+
45
65
  /**
46
66
  * Valeur d'un palier de breakpoint, CONFIGURABLE PAR APPLICATION :
47
67
  * - un **nombre** = largeur (px) à partir de laquelle le palier commence ; la lib en dérive une
@@ -144,5 +164,11 @@ declare class KtIdGenerator {
144
164
  static ɵprov: i0.ɵɵInjectableDeclaration<KtIdGenerator>;
145
165
  }
146
166
 
147
- export { KT_BREAKPOINTS, KT_DEFAULT_BREAKPOINTS, KtIdGenerator, KtViewport, createKtSheetDrag, provideKtBreakpoints, resolveKtBreakpointMedia };
167
+ /**
168
+ * InjectionToken permettant d'activer ou désactiver les audits d'accessibilité à l'exécution
169
+ * (gardes-fous console.warn en mode dev). Activé par défaut si `isDevMode()` est vrai.
170
+ */
171
+ declare const KT_AUDIT_ENABLED: InjectionToken<boolean>;
172
+
173
+ export { KT_AUDIT_ENABLED, KT_BREAKPOINTS, KT_DEFAULT_BREAKPOINTS, KtBodyScrollLock, KtIdGenerator, KtViewport, createKtSheetDrag, provideKtBreakpoints, resolveKtBreakpointMedia };
148
174
  export type { KtBreakpointMedia, KtBreakpointValue, KtBreakpoints, KtSheetDrag, KtSheetDragOptions };
@@ -249,22 +249,6 @@ type KtDialogPresentation = 'centered' | 'fullscreen' | 'sheet' | 'centered-full
249
249
  */
250
250
  declare function resolveKtDialogPanelClass(presentation?: KtDialogPresentation, compact?: boolean): string[];
251
251
 
252
- /**
253
- * Sucre typé pour récupérer les données injectées dans un composant de dialogue (`DIALOG_DATA`).
254
- * À appeler en contexte d'injection (ex. initialiseur de champ).
255
- *
256
- * @example
257
- * ```ts
258
- * interface RenameData { currentName: string; }
259
- *
260
- * @Component({ … })
261
- * export class RenameDialog {
262
- * protected readonly data = injectKtDialogData<RenameData>();
263
- * // this.data.currentName → string
264
- * }
265
- * ```
266
- */
267
- declare function injectKtDialogData<T>(): T;
268
252
  /** Config d'ouverture sans le champ `data` (fourni séparément, typé), enrichie de `presentation`. */
269
253
  type KtDialogOpenerConfig<D, R, C> = Omit<DialogConfig<D, DialogRef<R, C>>, 'data'> & {
270
254
  /** Présentation du dialog (cf. `KtDialogPresentation`) — choisie par le dev. Résolue à CHAQUE
@@ -273,39 +257,110 @@ type KtDialogOpenerConfig<D, R, C> = Omit<DialogConfig<D, DialogRef<R, C>>, 'dat
273
257
  presentation?: KtDialogPresentation;
274
258
  };
275
259
  /**
276
- * Crée un ouvreur de dialog **typé et centralisé**, en contexte d'injection.
277
- * Le contrat (composant + type de `data` + type de résultat + config par défaut) est défini
278
- * UNE fois ; l'ouvreur renvoyé est une closure appelable ensuite n'importe dans la classe
279
- * (handlers compris), 100 % typée, sans nouvel `inject`.
260
+ * ⚠️ API BAS-NIVEAU n'utilisez PAS ceci par défaut. Pour implémenter un dialog, passez par
261
+ * {@link defineKtDialog} puis son `injectOpener(...)` : il fixe données + résultat une seule fois et
262
+ * les partage avec `injectData()`/`injectRef()`. N'appelez `injectKtDialogOpener` directement que
263
+ * dans un cas avancé le contrat groupé de `defineKtDialog` ne convient pas.
264
+ *
265
+ * Crée un ouvreur de dialog typé en contexte d'injection : le contrat (composant + type de `data` +
266
+ * type de résultat + config par défaut) est figé via les génériques `<Composant, Data, Résultat>`
267
+ * (qu'il faut répéter — d'où la préférence pour `defineKtDialog`). L'ouvreur renvoyé est une closure
268
+ * appelable n'importe où dans la classe, 100 % typée, sans nouvel `inject`.
280
269
  *
281
270
  * Conserve le `ViewContainerRef` du contexte appelant (le dialog hérite donc de l'arbre logique
282
271
  * d'injection), reste testable (TestBed fournit `Dialog`) et sans état global (SSR-safe).
283
272
  *
284
- * @example
285
- * // co-localisé avec le composant
286
- * export const injectRenameDialog = () =>
287
- * injectKtDialogOpener<RenameDialog, RenameData, string>(RenameDialog, { autoFocus: '[ktDialogFocusInitial]' });
288
- *
289
- * // dans un composant/service consommateur
290
- * private readonly openRename = injectRenameDialog();
291
- * rename() { this.openRename({ currentName: 'X' }).closed.subscribe(n => { n: string | undefined }); }
292
- *
293
273
  * @param component Composant à monter dans le dialog.
294
274
  * @param baseConfig Config d'ouverture par défaut (sans `data`), enrichie de `presentation` ; fusionnée à chaque ouverture.
295
275
  * @returns Une closure d'ouverture typée : `() => DialogRef` (sans data) ou `(data, config?) => DialogRef` (avec data).
276
+ * @see {@link defineKtDialog} — la façon recommandée d'obtenir cet ouvreur.
296
277
  */
297
278
  declare function injectKtDialogOpener<C, R = unknown>(component: ComponentType<C>, baseConfig?: KtDialogOpenerConfig<void, R, C>): () => DialogRef<R, C>;
298
279
  declare function injectKtDialogOpener<C, D, R = unknown>(component: ComponentType<C>, baseConfig?: KtDialogOpenerConfig<D, R, C>): (data: D, config?: KtDialogOpenerConfig<D, R, C>) => DialogRef<R, C>;
280
+ /**
281
+ * Contrat de dialog typé renvoyé par `defineKtDialog` : les trois accès (données, référence,
282
+ * ouvreur) dérivent des MÊMES génériques `D`/`R`, déclarés une seule fois — impossible donc que
283
+ * le type des données ou du résultat diverge entre le composant et l'ouvreur.
284
+ *
285
+ * @template D Type des données injectées (`void` si aucune).
286
+ * @template R Type du résultat renvoyé à la fermeture.
287
+ */
288
+ interface KtDialogContract<D, R> {
289
+ /** Données injectées (à appeler en contexte d'injection DANS le composant de dialog). */
290
+ injectData(): D;
291
+ /** Référence typée du dialog : `close()` n'accepte QUE le résultat `R` (sûreté de fermeture). */
292
+ injectRef(): DialogRef<R>;
293
+ /** Ouvreur typé CO-LOCALISÉ pour ce composant (cf. `injectKtDialogOpener`). */
294
+ injectOpener<C>(component: ComponentType<C>, baseConfig?: KtDialogOpenerConfig<D, R, C>): (data: D, config?: KtDialogOpenerConfig<D, R, C>) => DialogRef<R, C>;
295
+ }
296
+ /**
297
+ * ✅ STRATÉGIE PAR DÉFAUT pour implémenter un dialog `@ktortu/aaa`. Toute nouvelle dialog DOIT être
298
+ * écrite ainsi. N'utilisez `injectKtDialogData` / `injectKtDialogOpener` directement QUE dans un cas
299
+ * avancé non couvert ici.
300
+ *
301
+ * Définit le contrat typé du dialog (données `D` + résultat `R`) en UN SEUL endroit, co-localisé avec
302
+ * le composant. Renvoie trois accès liés aux mêmes types — `injectData()` (données), `injectRef()`
303
+ * (référence dont `close()` n'accepte que `R`) et `injectOpener()` (ouvreur) — d'où l'impossibilité
304
+ * d'une divergence de type entre composant et ouvreur.
305
+ *
306
+ * Recette à copier pour CHAQUE dialog :
307
+ * 1. `interface XxxData { … }` (ou `void` si aucune donnée) ;
308
+ * 2. `const xxxDialog = defineKtDialog<XxxData, Résultat>();` ;
309
+ * 3. dans le composant : `injectData()` pour lire la donnée, `injectRef()` pour fermer avec résultat ;
310
+ * 4. `export const injectXxxDialog = () => xxxDialog.injectOpener(XxxDialog);` (co-localisé) ;
311
+ * 5. côté consommateur : `private readonly openXxx = injectXxxDialog();` (initialiseur de champ).
312
+ *
313
+ * Convention de fermeture : annuler / fermer SANS résultat via la directive `[ktDialogClose]`
314
+ * (→ `undefined`) ; renvoyer un résultat via `ref.close(résultat)` (typé `R`). Passer un résultat par
315
+ * `[ktDialogClose]="x"` reste possible mais N'EST PAS typé (la directive ignore `R`).
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * // ─── rename-dialog.ts : composant + contrat + ouvreur, co-localisés ───
320
+ * export interface RenameData { currentName: string; }
321
+ *
322
+ * const renameDialog = defineKtDialog<RenameData, string>(); // D et R déclarés UNE seule fois
323
+ *
324
+ * @Component({
325
+ * imports: [KtButton, KtDialogImports],
326
+ * template: `
327
+ * <h2 ktDialogTitle>Renommer</h2>
328
+ * <footer ktDialogActions>
329
+ * <button ktButton ktDialogClose>Annuler</button> // ferme sans résultat (undefined)
330
+ * <button ktButton (click)="submit()">Renommer</button> // ferme avec résultat typé
331
+ * </footer>`,
332
+ * })
333
+ * export class RenameDialog {
334
+ * protected readonly data = renameDialog.injectData(); // RenameData garanti
335
+ * private readonly ref = renameDialog.injectRef(); // DialogRef<string>
336
+ * protected submit() { this.ref.close('nouveau-nom'); } // close(result?: string) — typé
337
+ * }
338
+ *
339
+ * export const injectRenameDialog = () => renameDialog.injectOpener(RenameDialog);
340
+ *
341
+ * // ─── consommateur ───
342
+ * private readonly openRename = injectRenameDialog(); // initialiseur de champ = contexte d'injection
343
+ * rename() {
344
+ * this.openRename({ currentName: 'X' }).closed.subscribe(name => {
345
+ * // name: string | undefined
346
+ * });
347
+ * }
348
+ * ```
349
+ */
350
+ declare function defineKtDialog<D = void, R = unknown>(): KtDialogContract<D, R>;
299
351
 
300
- declare class KtDialogContainer extends CdkDialogContainer implements OnInit {
352
+ declare class KtDialogContainer extends CdkDialogContainer {
301
353
  private readonly dialogRef;
302
354
  private readonly elementRef;
303
355
  private readonly host;
304
356
  private readonly platformId;
305
- private readonly destroyRef;
306
357
  protected readonly isClosing: i0.WritableSignal<boolean>;
358
+ /** Premier id de la file `aria-labelledby` exposée par `CdkDialogContainer`. Le membre
359
+ `_ariaLabelledByQueue` est interne au CDK (préfixe `_`) : on l'encapsule ICI, gardé, pour que
360
+ le binding de template ne lise jamais l'interne directement. Si une montée du CDK retire ou
361
+ renomme ce membre, on dégrade proprement vers `null` au lieu de casser le rendu du conteneur. */
362
+ protected get ariaLabelledBy(): string | null;
307
363
  constructor();
308
- ngOnInit(): void;
309
364
  protected isSheet(): boolean;
310
365
  private animateAndClose;
311
366
  static ɵfac: i0.ɵɵFactoryDeclaration<KtDialogContainer, never>;
@@ -318,5 +373,5 @@ declare class KtDialogContainer extends CdkDialogContainer implements OnInit {
318
373
  */
319
374
  declare const KtDialogImports: readonly [typeof KtDialogHeader, typeof KtDialogTitle, typeof KtDialogDescription, typeof KtDialogContent, typeof KtDialogActions, typeof KtDialogClose, typeof KtDialogFocusInitial, typeof KtDialogSheetHandle];
320
375
 
321
- export { KT_DIALOG_AAA_DEFAULTS, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, injectKtDialogData, injectKtDialogOpener, provideKtDialogDefaults, resolveKtDialogPanelClass };
322
- export type { KtDialogOpenerConfig, KtDialogPresentation };
376
+ export { KT_DIALOG_AAA_DEFAULTS, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, defineKtDialog, injectKtDialogOpener, provideKtDialogDefaults, resolveKtDialogPanelClass };
377
+ export type { KtDialogContract, KtDialogOpenerConfig, KtDialogPresentation };