@ktortu/aaa 0.1.0-beta.0 → 0.9.1

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 (49) hide show
  1. package/README.md +27 -9
  2. package/cdk/styles/tabs.css +100 -21
  3. package/fesm2022/ktortu-aaa-button.mjs +18 -11
  4. package/fesm2022/ktortu-aaa-button.mjs.map +1 -1
  5. package/fesm2022/ktortu-aaa-card.mjs +29 -4
  6. package/fesm2022/ktortu-aaa-card.mjs.map +1 -1
  7. package/fesm2022/ktortu-aaa-cdk.mjs +59 -15
  8. package/fesm2022/ktortu-aaa-cdk.mjs.map +1 -1
  9. package/fesm2022/ktortu-aaa-dialog.mjs +134 -24
  10. package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -1
  11. package/fesm2022/ktortu-aaa-forms.mjs +748 -294
  12. package/fesm2022/ktortu-aaa-forms.mjs.map +1 -1
  13. package/fesm2022/ktortu-aaa-i18n.mjs +139 -0
  14. package/fesm2022/ktortu-aaa-i18n.mjs.map +1 -0
  15. package/fesm2022/ktortu-aaa-menu.mjs +38 -13
  16. package/fesm2022/ktortu-aaa-menu.mjs.map +1 -1
  17. package/fesm2022/ktortu-aaa-snackbar.mjs +465 -0
  18. package/fesm2022/ktortu-aaa-snackbar.mjs.map +1 -0
  19. package/fesm2022/ktortu-aaa-tabs.mjs +319 -42
  20. package/fesm2022/ktortu-aaa-tabs.mjs.map +1 -1
  21. package/fesm2022/ktortu-aaa-tooltip.mjs +3 -2
  22. package/fesm2022/ktortu-aaa-tooltip.mjs.map +1 -1
  23. package/fesm2022/ktortu-aaa.mjs +2 -0
  24. package/fesm2022/ktortu-aaa.mjs.map +1 -1
  25. package/forms/checkbox/checkbox-group.css +0 -8
  26. package/forms/chips/chip-list.css +5 -0
  27. package/forms/radio/radio-group.css +0 -8
  28. package/forms/styles/field-box.css +152 -2
  29. package/forms/styles/field-pending.css +46 -0
  30. package/forms/styles/select-panel.css +4 -0
  31. package/forms/styles/tokens.css +3 -0
  32. package/menu/menu.css +8 -4
  33. package/package.json +9 -1
  34. package/snackbar/snackbar-tokens.css +53 -0
  35. package/snackbar/snackbar.css +175 -0
  36. package/styles/forms.css +1 -0
  37. package/styles/snackbar.css +9 -0
  38. package/styles/styles.css +1 -0
  39. package/types/ktortu-aaa-button.d.ts +22 -8
  40. package/types/ktortu-aaa-card.d.ts +24 -4
  41. package/types/ktortu-aaa-cdk.d.ts +38 -0
  42. package/types/ktortu-aaa-dialog.d.ts +129 -31
  43. package/types/ktortu-aaa-forms.d.ts +503 -160
  44. package/types/ktortu-aaa-i18n.d.ts +77 -0
  45. package/types/ktortu-aaa-menu.d.ts +15 -8
  46. package/types/ktortu-aaa-snackbar.d.ts +275 -0
  47. package/types/ktortu-aaa-tabs.d.ts +130 -13
  48. package/types/ktortu-aaa-tooltip.d.ts +5 -0
  49. package/types/ktortu-aaa.d.ts +2 -0
package/README.md CHANGED
@@ -4,6 +4,8 @@ Bibliothèque de composants Angular **headless + thémés par tokens CSS** (`--k
4
4
  Les composants sont des directives/composants accessibles (appui sur `@angular/aria` / CDK) ;
5
5
  leur apparence vit dans des feuilles CSS globales que vous importez à part.
6
6
 
7
+ > **Démo & documentation vivante** : [ktortu-aaa-demo.web.app](https://ktortu-aaa-demo.web.app/)
8
+
7
9
  ## Convention de nommage
8
10
 
9
11
  - **Selectors** (templates) : préfixe `kt` — `[ktButton]`, `<kt-text-field>`, `[ktDialogTitle]`…
@@ -17,7 +19,7 @@ leur apparence vit dans des feuilles CSS globales que vous importez à part.
17
19
  ## Utilisation (TypeScript)
18
20
 
19
21
  Chaque famille est importable depuis son point d'entrée `@ktortu/aaa/<feature>` ; les utilitaires
20
- transverses (breakpoints, viewport, sheet-drag) depuis la racine `@ktortu/aaa`.
22
+ transverses (breakpoints, viewport, sheet-drag, générateur d'id) depuis `@ktortu/aaa/cdk`.
21
23
 
22
24
  ```ts
23
25
  import { Component } from '@angular/core';
@@ -37,11 +39,25 @@ export class Exemple {
37
39
  }
38
40
  ```
39
41
 
40
- Points d'entrée : `@ktortu/aaa/button`, `/card`, `/dialog`, `/menu`, `/tabs`, `/tooltip`,
41
- `/forms`, `/themes`, et la racine `@ktortu/aaa` (breakpoints, viewport, sheet-drag).
42
- Familles agrégées prêtes pour `imports:` : `KT_CARD`, `KT_MENU` (ex. `imports: [...KT_CARD]`).
43
- Le chrome de champ générique (`KtField` + `KtFieldControl`) enveloppe un contrôle natif :
44
- `<kt-field label="…"><input ktFieldControl [(ngModel)]="…" /></kt-field>`.
42
+ Points d'entrée TypeScript : `@ktortu/aaa/button`, `/card`, `/dialog`, `/menu`, `/snackbar`, `/tabs`,
43
+ `/tooltip`, `/forms`, `/cdk` (breakpoints, viewport, sheet-drag, id-generator) et `/i18n`
44
+ (traductions). La racine `@ktortu/aaa` ré-exporte tout par commodité, mais importer depuis le
45
+ point d'entrée précis préserve le tree-shaking. Les thèmes sont des fichiers CSS (cf. §Styles),
46
+ pas un point d'entrée TypeScript.
47
+ Familles agrégées prêtes pour `imports:` : `KtCardImports`, `KtMenuImports`, `KtDialogImports`
48
+ (ex. `imports: [...KtCardImports]`).
49
+
50
+ ### Formulaires & Signal Forms
51
+
52
+ Les contrôles dédiés (`kt-text-field`, `kt-number-field`, `kt-select`, `kt-checkbox`, champs
53
+ temporels…) implémentent le contrat **Signal Forms** (`FormValueControl`) : on les lie via
54
+ `[(value)]` ou via le directive `[formField]` d'`@angular/forms/signals`. Ils n'implémentent PAS
55
+ `ControlValueAccessor` ; pour les intégrer à des `ReactiveForms` existants, passez par le pont
56
+ officiel d'Angular (`compatForm` / `SignalFormControl`, `@angular/forms/signals`) et liez avec
57
+ `[formField]` plutôt que `formControlName`.
58
+
59
+ Pour un contrôle natif quelconque, le chrome de champ générique (`KtField` + `KtFieldControl`)
60
+ l'enveloppe : `<kt-field label="…"><input ktFieldControl [(value)]="…" /></kt-field>`.
45
61
 
46
62
  ### Configuration
47
63
 
@@ -50,7 +66,7 @@ type `Partial<…>`) ou via les helpers `provideKt*` :
50
66
 
51
67
  ```ts
52
68
  import { KT_BUTTON_CONFIG } from '@ktortu/aaa/button';
53
- import { provideKtBreakpoints } from '@ktortu/aaa';
69
+ import { provideKtBreakpoints } from '@ktortu/aaa/cdk';
54
70
  import { provideKtDialogDefaults } from '@ktortu/aaa/dialog';
55
71
 
56
72
  providers: [
@@ -84,8 +100,9 @@ garanti) :
84
100
  ]
85
101
  ```
86
102
 
87
- > Le **Dialog** s'appuie sur l'overlay du CDK : ajoutez aussi
88
- > `@import '@angular/cdk/overlay-prebuilt.css';`.
103
+ > Le **Dialog** et la **Snackbar** s'appuient sur l'overlay du CDK : ajoutez aussi
104
+ > `@import '@angular/cdk/overlay-prebuilt.css';`. La **Snackbar** annonce via le `LiveAnnouncer` —
105
+ > ajoutez également `@import '@angular/cdk/a11y-prebuilt.css';` (masque l'élément d'annonce).
89
106
 
90
107
  ### Thèmes (optionnels, à la carte)
91
108
 
@@ -125,6 +142,7 @@ Bundles disponibles :
125
142
  | `@ktortu/aaa/menu.css` | menu (`ktMenu`, `ktMenuItem`, …) |
126
143
  | `@ktortu/aaa/tooltip.css` | tooltip (`ktTooltip`) |
127
144
  | `@ktortu/aaa/dialog.css` | dialog (requiert aussi l'overlay CDK) |
145
+ | `@ktortu/aaa/snackbar.css` | snackbar (requiert l'overlay CDK + `a11y-prebuilt.css`) |
128
146
  | `@ktortu/aaa/forms.css` | base des formulaires (champs, chips, switch) |
129
147
  | `@ktortu/aaa/tabs.css` | onglets |
130
148
 
@@ -9,6 +9,9 @@
9
9
  --tab-height: var(--kt-control-height, 44px);
10
10
  --tab-text-color: var(--kt-muted);
11
11
  --tab-text-hover: var(--kt-on-surface);
12
+ /* Couleur (et non opacité) de l'état désactivé : idiome de la lib (cf. button.css), évite de
13
+ faire baisser le contraste de tout le sous-arbre. Surchargeable par le thème. */
14
+ --tab-text-disabled: color-mix(in srgb, var(--tab-text-color) 55%, transparent);
12
15
 
13
16
  /* L'état actif prend la couleur primaire du thème par défaut */
14
17
  --tab-active-color: var(--kt-primary);
@@ -40,6 +43,12 @@
40
43
  --tab-scroll-margin: 1rem;
41
44
  --tab-scroll-snap: none; /* `x proximity` pour activer l'accroche */
42
45
  --tab-scrollbar-width: thin;
46
+
47
+ /* ============ PAGINATION (directive ktTabScroller / composant kt-tab-scroller) ============ */
48
+ /* Quand la pagination réactive est active, la scrollbar native est masquée (les chevrons +
49
+ ombres prennent le relais). Réafficher la barre via `--tab-scroller-scrollbar: thin`. */
50
+ --tab-scroller-scrollbar: none;
51
+ --tab-scroller-gap: 0; /* espace boutons chevrons ↔ viewport */
43
52
  /* Géométrie et fond (pour les thèmes extrêmes comme Win98) */
44
53
  --tab-bg: transparent;
45
54
  --tab-bg-hover: color-mix(in srgb, var(--tab-text-hover) 5%, transparent);
@@ -93,16 +102,36 @@
93
102
 
94
103
  /* Ombres de défilement (Lea Verou) : couche "cache" (couleur surface, attachement local) +
95
104
  couche "ombre" (attachement scroll). L'ombre n'apparaît que s'il reste du contenu de ce côté
96
- et s'estompe à l'extrémité → pas de "ghosting" permanent du 1er/dernier onglet. */
97
- background:
98
- linear-gradient(to right, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent) 0 0 /
99
- var(--tab-scroll-shadow-size, 2rem) 100% no-repeat local,
100
- linear-gradient(to left, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent) 100% 0 /
101
- var(--tab-scroll-shadow-size, 2rem) 100% no-repeat local,
102
- radial-gradient(farthest-side at 0 50%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent) 0 0 /
103
- var(--tab-scroll-shadow-spread, 0.85rem) 100% no-repeat scroll,
104
- radial-gradient(farthest-side at 100% 50%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent) 100% 0 /
105
- var(--tab-scroll-shadow-spread, 0.85rem) 100% no-repeat scroll;
105
+ et s'estompe à l'extrémité → pas de "ghosting" permanent du 1er/dernier onglet.
106
+ Longhands (et non shorthand `background`) pour NE PAS réinitialiser `background-color` : un
107
+ consommateur peut donc poser sa propre couleur de fond sans collision. */
108
+ background-image:
109
+ linear-gradient(to right, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent),
110
+ linear-gradient(to left, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent),
111
+ radial-gradient(farthest-side at 0 50%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent),
112
+ radial-gradient(farthest-side at 100% 50%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent);
113
+ background-position:
114
+ 0 0,
115
+ 100% 0,
116
+ 0 0,
117
+ 100% 0;
118
+ background-size:
119
+ var(--tab-scroll-shadow-size, 2rem) 100%,
120
+ var(--tab-scroll-shadow-size, 2rem) 100%,
121
+ var(--tab-scroll-shadow-spread, 0.85rem) 100%,
122
+ var(--tab-scroll-shadow-spread, 0.85rem) 100%;
123
+ background-repeat: no-repeat;
124
+ background-attachment: local, local, scroll, scroll;
125
+ }
126
+
127
+ /* 2b. Pagination active (directive ktTabScroller) → scrollbar native masquée ; les chevrons et
128
+ les ombres Lea Verou prennent le relais. `--tab-scroller-scrollbar: thin` réaffiche la barre. */
129
+ [ngTabList][data-kt-tab-scroller] {
130
+ scrollbar-width: var(--tab-scroller-scrollbar, none);
131
+ }
132
+ [ngTabList][data-kt-tab-scroller]::-webkit-scrollbar {
133
+ width: 0;
134
+ height: 0;
106
135
  }
107
136
 
108
137
  /* 3. Un onglet individuel */
@@ -171,9 +200,8 @@
171
200
  /* 6. État désactivé */
172
201
  [ngTab][disabled],
173
202
  [ngTab][aria-disabled='true'] {
174
- opacity: 0.4;
175
203
  cursor: not-allowed;
176
- color: var(--tab-text-color);
204
+ color: var(--tab-text-disabled);
177
205
  }
178
206
 
179
207
  /* 7. Panneaux de contenu (Tab Panels) */
@@ -239,15 +267,24 @@
239
267
  /* Débordement vertical (la hauteur est contrainte par le conteneur hôte) + ombres haut/bas. */
240
268
  overflow-x: hidden;
241
269
  overflow-y: var(--tab-list-overflow, auto);
242
- background:
243
- linear-gradient(to bottom, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent) 0 0 / 100%
244
- var(--tab-scroll-shadow-size, 2rem) no-repeat local,
245
- linear-gradient(to top, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent) 0 100% / 100%
246
- var(--tab-scroll-shadow-size, 2rem) no-repeat local,
247
- radial-gradient(farthest-side at 50% 0, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent) 0 0 / 100%
248
- var(--tab-scroll-shadow-spread, 0.85rem) no-repeat scroll,
249
- radial-gradient(farthest-side at 50% 100%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent) 0 100% /
250
- 100% var(--tab-scroll-shadow-spread, 0.85rem) no-repeat scroll;
270
+ /* Longhands (cf. bloc horizontal) : préserve un éventuel `background-color` du consommateur. */
271
+ background-image:
272
+ linear-gradient(to bottom, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent),
273
+ linear-gradient(to top, var(--tab-list-bg, var(--kt-surface, #fff)) 30%, transparent),
274
+ radial-gradient(farthest-side at 50% 0, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent),
275
+ radial-gradient(farthest-side at 50% 100%, var(--tab-scroll-shadow-color, rgb(0 0 0 / 16%)), transparent);
276
+ background-position:
277
+ 0 0,
278
+ 0 100%,
279
+ 0 0,
280
+ 0 100%;
281
+ background-size:
282
+ 100% var(--tab-scroll-shadow-size, 2rem),
283
+ 100% var(--tab-scroll-shadow-size, 2rem),
284
+ 100% var(--tab-scroll-shadow-spread, 0.85rem),
285
+ 100% var(--tab-scroll-shadow-spread, 0.85rem);
286
+ background-repeat: no-repeat;
287
+ background-attachment: local, local, scroll, scroll;
251
288
  }
252
289
 
253
290
  [ngTabList][aria-orientation='vertical'] [ngTab],
@@ -273,4 +310,46 @@
273
310
  flex-grow: 1;
274
311
  padding: 0 1.5rem;
275
312
  }
313
+
314
+ /* ==========================================================================
315
+ PAGINATION CLÉ-EN-MAIN (composant <kt-tab-scroller>)
316
+ On ne stylise QUE le layout (flex : chevron / viewport / chevron) — l'apparence des chevrons
317
+ vient de [ktButton]. `min-inline-size: 0` est critique : sans lui, le `ul` (dont les onglets
318
+ sont `flex: none`) impose sa largeur intrinsèque et la liste ne déborde jamais.
319
+ ========================================================================== */
320
+ kt-tab-scroller {
321
+ display: flex;
322
+ align-items: stretch;
323
+ gap: var(--tab-scroller-gap, 0);
324
+ min-inline-size: 0;
325
+ }
326
+
327
+ kt-tab-scroller > .kt-tab-scroller__viewport {
328
+ flex: 1 1 auto;
329
+ min-inline-size: 0;
330
+ display: flex;
331
+ }
332
+
333
+ kt-tab-scroller > .kt-tab-scroller__viewport > [ngTabList] {
334
+ flex: 1 1 auto;
335
+ min-inline-size: 0;
336
+ }
337
+
338
+ .kt-tab-scroller__chevron {
339
+ flex: none;
340
+ align-self: center;
341
+ }
342
+
343
+ /* Pas de débordement → chevrons retirés (la liste occupe toute la barre). */
344
+ kt-tab-scroller:not([data-overflowing]) > .kt-tab-scroller__chevron {
345
+ display: none;
346
+ }
347
+
348
+ /* Orientation verticale : barre en colonne, chevrons haut/bas. */
349
+ kt-tab-scroller[data-orientation='vertical'] {
350
+ flex-direction: column;
351
+ }
352
+ kt-tab-scroller[data-orientation='vertical'] > .kt-tab-scroller__viewport {
353
+ min-block-size: 0;
354
+ }
276
355
  }
@@ -3,6 +3,17 @@ import { InjectionToken, inject, ElementRef, PLATFORM_ID, signal, input, boolean
3
3
  import { isPlatformBrowser } from '@angular/common';
4
4
 
5
5
  const KT_BUTTON_CONFIG = new InjectionToken('KT_BUTTON_CONFIG');
6
+ /**
7
+ * Fournit des défauts de bouton (mode/couleur/taille…) pour un sous-arbre ou l'application entière.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * providers: [provideKtButton({ mode: 'tonal', size: 'lg' })]
12
+ * ```
13
+ */
14
+ function provideKtButton(config) {
15
+ return { provide: KT_BUTTON_CONFIG, useValue: config };
16
+ }
6
17
  /**
7
18
  * Bouton ou lien stylé par la directive `ktButton`. À poser sur un `<button>`
8
19
  * (action) ou un `<a>` (navigation) — l'accessibilité s'adapte automatiquement
@@ -35,31 +46,27 @@ class KtButton {
35
46
  size = input(this.config?.size ?? 'md', /* @ts-ignore */
36
47
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
37
48
  /** Étire le bouton sur toute la largeur disponible. @default false */
38
- fullWidth = input(false, /* @ts-ignore */
39
- ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
49
+ fullWidth = input(false, { ...(ngDevMode ? { debugName: "fullWidth" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
40
50
  /** Bouton carré sans texte ; impose un nom accessible via `ariaLabel`. @default false */
41
51
  iconOnly = input(false, { ...(ngDevMode ? { debugName: "iconOnly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
42
- /** Nom accessible. Obligatoire si `iconOnly` ; à défaut, l'`aria-label` natif est préservé. */
52
+ /** Nom accessible. **Obligatoire si `iconOnly`** ; à défaut, l'`aria-label` natif est préservé. @default undefined */
43
53
  ariaLabel = input(/* @ts-ignore */
44
54
  ...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
45
55
  /** Attribut HTML natif (comportement formulaire), distinct de `mode`. @default 'button' */
46
56
  type = input('button', /* @ts-ignore */
47
57
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
48
58
  /** Affiche l'état de chargement et rend le bouton inerte (`aria-busy`). @default false */
49
- loading = input(false, /* @ts-ignore */
50
- ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
51
- /** Nom de l'icône (rendu via CSS `data-icon`). */
59
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
60
+ /** Nom de l'icône (rendu via CSS `data-icon`). @default undefined */
52
61
  icon = input(/* @ts-ignore */
53
62
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
54
63
  /** Position de l'icône relative au texte. @default 'start' */
55
64
  iconPosition = input('start', /* @ts-ignore */
56
65
  ...(ngDevMode ? [{ debugName: "iconPosition" }] : /* istanbul ignore next */ []));
57
66
  /** Désactive le bouton. @default false */
58
- disabled = input(false, /* @ts-ignore */
59
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
67
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
60
68
  /** Garde le bouton focusable même désactivé (via `aria-disabled`). @default false (ou KT_BUTTON_CONFIG.disabledInteractive) */
61
- disabledInteractive = input(this.config?.disabledInteractive ?? false, /* @ts-ignore */
62
- ...(ngDevMode ? [{ debugName: "disabledInteractive" }] : /* istanbul ignore next */ []));
69
+ disabledInteractive = input(this.config?.disabledInteractive ?? false, { ...(ngDevMode ? { debugName: "disabledInteractive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
63
70
  // loading implique un bouton inerte
64
71
  isDisabled = computed(() => this.disabled() || this.loading(), /* @ts-ignore */
65
72
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
@@ -124,5 +131,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
124
131
  * Generated bundle index. Do not edit.
125
132
  */
126
133
 
127
- export { KT_BUTTON_CONFIG, KtButton };
134
+ export { KT_BUTTON_CONFIG, KtButton, provideKtButton };
128
135
  //# sourceMappingURL=ktortu-aaa-button.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ktortu-aaa-button.mjs","sources":["../../../../projects/ktortu/aaa/button/button.ts","../../../../projects/ktortu/aaa/button/ktortu-aaa-button.ts"],"sourcesContent":["import {\n Directive,\n ElementRef,\n InjectionToken,\n booleanAttribute,\n computed,\n inject,\n input,\n isDevMode,\n AfterViewInit,\n PLATFORM_ID,\n effect,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/** Axe \"emphase\" (apparence). */\nexport type KtButtonMode = 'filled' | 'tonal' | 'outlined' | 'text';\n/** Axe \"intention\" (couleur sémantique). */\nexport type KtButtonColor = 'primary' | 'neutral' | 'danger';\n/** Taille (la cible md ~44-48px vise le confort tactile / AAA). */\nexport type KtButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface KtButtonConfig {\n disabledInteractive: boolean;\n mode: KtButtonMode;\n color: KtButtonColor;\n size: KtButtonSize;\n}\n\nexport const KT_BUTTON_CONFIG = new InjectionToken<Partial<KtButtonConfig>>('KT_BUTTON_CONFIG');\n\n/**\n * Bouton ou lien stylé par la directive `ktButton`. À poser sur un `<button>`\n * (action) ou un `<a>` (navigation) — l'accessibilité s'adapte automatiquement\n * (un `<a>` désactivé bascule sur `aria-disabled` plutôt que `disabled`).\n *\n * @example\n * ```html\n * <button ktButton mode=\"filled\" color=\"primary\">Enregistrer</button>\n * <a ktButton mode=\"text\" href=\"/aide\">Aide</a>\n * <button ktButton iconOnly icon=\"close\" ariaLabel=\"Fermer\"></button>\n * ```\n */\n@Directive({\n selector: 'button[ktButton], a[ktButton]',\n host: {\n '[attr.data-mode]': 'mode()',\n '[attr.data-color]': 'color()',\n '[attr.data-size]': 'size()',\n '[attr.data-full-width]': 'fullWidth() ? \"\" : null',\n '[attr.data-icon-only]': 'iconOnly() ? \"\" : null',\n '[attr.type]': '!isLink ? type() : null',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n '[class.loading]': 'loading()',\n '[class.disabled]': 'isDisabled()',\n '[attr.data-icon]': 'icon()',\n '[attr.data-icon-position]': 'iconPosition()',\n '[attr.disabled]': 'isDisabled() && !disabledInteractive() && !isLink ? \"\" : null',\n '[attr.aria-disabled]': 'isDisabled() && (disabledInteractive() || isLink) ? \"true\" : null',\n '[attr.tabindex]': 'isDisabled() && !disabledInteractive() && isLink ? \"-1\" : null',\n '[attr.aria-busy]': 'loading() ? \"true\" : null',\n '(click)': 'haltDisabledEvents($event)',\n },\n})\nexport class KtButton implements AfterViewInit {\n private readonly config = inject(KT_BUTTON_CONFIG, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly platformId = inject(PLATFORM_ID);\n private readonly viewInitialized = signal(false);\n\n // Un <a> reste toujours focusable : on bascule sur aria-disabled au lieu du disabled natif.\n protected readonly isLink = this.host.tagName === 'A';\n // aria-label natif posé par le consommateur, capté à la construction (préservé si l'input n'est pas fourni).\n private readonly nativeAriaLabel = this.host.getAttribute('aria-label');\n\n /** Emphase visuelle du bouton (apparence, ≠ `type`). @default 'filled' (ou KT_BUTTON_CONFIG.mode) */\n readonly mode = input<KtButtonMode>(this.config?.mode ?? 'filled');\n\n /** Intention sémantique (couleur). @default 'primary' (ou KT_BUTTON_CONFIG.color) */\n readonly color = input<KtButtonColor>(this.config?.color ?? 'primary');\n\n /** Taille ; `md` vise une cible tactile ~44-48px. @default 'md' (ou KT_BUTTON_CONFIG.size) */\n readonly size = input<KtButtonSize>(this.config?.size ?? 'md');\n\n /** Étire le bouton sur toute la largeur disponible. @default false */\n readonly fullWidth = input<boolean>(false);\n\n /** Bouton carré sans texte ; impose un nom accessible via `ariaLabel`. @default false */\n readonly iconOnly = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Nom accessible. Obligatoire si `iconOnly` ; à défaut, l'`aria-label` natif est préservé. */\n readonly ariaLabel = input<string>();\n\n /** Attribut HTML natif (comportement formulaire), distinct de `mode`. @default 'button' */\n readonly type = input<'button' | 'submit' | 'reset'>('button');\n\n /** Affiche l'état de chargement et rend le bouton inerte (`aria-busy`). @default false */\n readonly loading = input<boolean>(false);\n\n /** Nom de l'icône (rendu via CSS `data-icon`). */\n readonly icon = input<string>();\n\n /** Position de l'icône relative au texte. @default 'start' */\n readonly iconPosition = input<'start' | 'end'>('start');\n\n /** Désactive le bouton. @default false */\n readonly disabled = input<boolean>(false);\n\n /** Garde le bouton focusable même désactivé (via `aria-disabled`). @default false (ou KT_BUTTON_CONFIG.disabledInteractive) */\n readonly disabledInteractive = input<boolean>(this.config?.disabledInteractive ?? false);\n\n // loading implique un bouton inerte\n protected readonly isDisabled = computed(() => this.disabled() || this.loading());\n\n // input prioritaire, sinon aria-label natif préservé (non destructif), sinon rien.\n protected readonly resolvedAriaLabel = computed(() => this.ariaLabel()?.trim() || this.nativeAriaLabel || null);\n\n constructor() {\n effect(() => {\n if (!isPlatformBrowser(this.platformId)) return;\n if (!this.viewInitialized()) return;\n if (!isDevMode() || !this.iconOnly()) return;\n\n if (!this.icon()) {\n console.warn('[ktButton] iconOnly attend une icône via [icon].');\n }\n if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby')) {\n console.warn(\n '[ktButton] iconOnly sans nom accessible : ajoutez [ariaLabel] (WCAG 4.1.2) — ' +\n 'le bouton serait annoncé sans libellé.',\n );\n }\n });\n }\n\n ngAfterViewInit(): void {\n this.viewInitialized.set(true);\n }\n\n protected haltDisabledEvents(event: Event): void {\n if (!this.isDisabled()) return;\n\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n}\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-button.mjs","sources":["../../../../projects/ktortu/aaa/button/button.ts","../../../../projects/ktortu/aaa/button/ktortu-aaa-button.ts"],"sourcesContent":["import {\n Directive,\n ElementRef,\n InjectionToken,\n Provider,\n booleanAttribute,\n computed,\n inject,\n input,\n isDevMode,\n AfterViewInit,\n PLATFORM_ID,\n effect,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\n\n/** Axe \"emphase\" (apparence). */\nexport type KtButtonMode = 'filled' | 'tonal' | 'outlined' | 'text';\n/** Axe \"intention\" (couleur sémantique). */\nexport type KtButtonColor = 'primary' | 'neutral' | 'danger';\n/** Taille (la cible md ~44-48px vise le confort tactile / AAA). */\nexport type KtButtonSize = 'sm' | 'md' | 'lg';\n\n/** Défauts applicables à tous les `[ktButton]` (surchargeables par bouton via les inputs). */\nexport interface KtButtonConfig {\n /** Garde le bouton focusable même désactivé (via `aria-disabled`). */\n disabledInteractive: boolean;\n /** Emphase visuelle par défaut (apparence). */\n mode: KtButtonMode;\n /** Intention sémantique par défaut (couleur). */\n color: KtButtonColor;\n /** Taille par défaut. */\n size: KtButtonSize;\n}\n\nexport const KT_BUTTON_CONFIG = new InjectionToken<Partial<KtButtonConfig>>('KT_BUTTON_CONFIG');\n\n/**\n * Fournit des défauts de bouton (mode/couleur/taille…) pour un sous-arbre ou l'application entière.\n *\n * @example\n * ```ts\n * providers: [provideKtButton({ mode: 'tonal', size: 'lg' })]\n * ```\n */\nexport function provideKtButton(config: Partial<KtButtonConfig>): Provider {\n return { provide: KT_BUTTON_CONFIG, useValue: config };\n}\n\n/**\n * Bouton ou lien stylé par la directive `ktButton`. À poser sur un `<button>`\n * (action) ou un `<a>` (navigation) — l'accessibilité s'adapte automatiquement\n * (un `<a>` désactivé bascule sur `aria-disabled` plutôt que `disabled`).\n *\n * @example\n * ```html\n * <button ktButton mode=\"filled\" color=\"primary\">Enregistrer</button>\n * <a ktButton mode=\"text\" href=\"/aide\">Aide</a>\n * <button ktButton iconOnly icon=\"close\" ariaLabel=\"Fermer\"></button>\n * ```\n */\n@Directive({\n selector: 'button[ktButton], a[ktButton]',\n host: {\n '[attr.data-mode]': 'mode()',\n '[attr.data-color]': 'color()',\n '[attr.data-size]': 'size()',\n '[attr.data-full-width]': 'fullWidth() ? \"\" : null',\n '[attr.data-icon-only]': 'iconOnly() ? \"\" : null',\n '[attr.type]': '!isLink ? type() : null',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n '[class.loading]': 'loading()',\n '[class.disabled]': 'isDisabled()',\n '[attr.data-icon]': 'icon()',\n '[attr.data-icon-position]': 'iconPosition()',\n '[attr.disabled]': 'isDisabled() && !disabledInteractive() && !isLink ? \"\" : null',\n '[attr.aria-disabled]': 'isDisabled() && (disabledInteractive() || isLink) ? \"true\" : null',\n '[attr.tabindex]': 'isDisabled() && !disabledInteractive() && isLink ? \"-1\" : null',\n '[attr.aria-busy]': 'loading() ? \"true\" : null',\n '(click)': 'haltDisabledEvents($event)',\n },\n})\nexport class KtButton implements AfterViewInit {\n private readonly config = inject(KT_BUTTON_CONFIG, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private readonly platformId = inject(PLATFORM_ID);\n private readonly viewInitialized = signal(false);\n\n // Un <a> reste toujours focusable : on bascule sur aria-disabled au lieu du disabled natif.\n protected readonly isLink = this.host.tagName === 'A';\n // aria-label natif posé par le consommateur, capté à la construction (préservé si l'input n'est pas fourni).\n private readonly nativeAriaLabel = this.host.getAttribute('aria-label');\n\n /** Emphase visuelle du bouton (apparence, ≠ `type`). @default 'filled' (ou KT_BUTTON_CONFIG.mode) */\n readonly mode = input<KtButtonMode>(this.config?.mode ?? 'filled');\n\n /** Intention sémantique (couleur). @default 'primary' (ou KT_BUTTON_CONFIG.color) */\n readonly color = input<KtButtonColor>(this.config?.color ?? 'primary');\n\n /** Taille ; `md` vise une cible tactile ~44-48px. @default 'md' (ou KT_BUTTON_CONFIG.size) */\n readonly size = input<KtButtonSize>(this.config?.size ?? 'md');\n\n /** Étire le bouton sur toute la largeur disponible. @default false */\n readonly fullWidth = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Bouton carré sans texte ; impose un nom accessible via `ariaLabel`. @default false */\n readonly iconOnly = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Nom accessible. **Obligatoire si `iconOnly`** ; à défaut, l'`aria-label` natif est préservé. @default undefined */\n readonly ariaLabel = input<string>();\n\n /** Attribut HTML natif (comportement formulaire), distinct de `mode`. @default 'button' */\n readonly type = input<'button' | 'submit' | 'reset'>('button');\n\n /** Affiche l'état de chargement et rend le bouton inerte (`aria-busy`). @default false */\n readonly loading = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Nom de l'icône (rendu via CSS `data-icon`). @default undefined */\n readonly icon = input<string>();\n\n /** Position de l'icône relative au texte. @default 'start' */\n readonly iconPosition = input<'start' | 'end'>('start');\n\n /** Désactive le bouton. @default false */\n readonly disabled = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Garde le bouton focusable même désactivé (via `aria-disabled`). @default false (ou KT_BUTTON_CONFIG.disabledInteractive) */\n readonly disabledInteractive = input<boolean, unknown>(this.config?.disabledInteractive ?? false, {\n transform: booleanAttribute,\n });\n\n // loading implique un bouton inerte\n protected readonly isDisabled = computed(() => this.disabled() || this.loading());\n\n // input prioritaire, sinon aria-label natif préservé (non destructif), sinon rien.\n protected readonly resolvedAriaLabel = computed(() => this.ariaLabel()?.trim() || this.nativeAriaLabel || null);\n\n constructor() {\n effect(() => {\n if (!isPlatformBrowser(this.platformId)) return;\n if (!this.viewInitialized()) return;\n if (!isDevMode() || !this.iconOnly()) return;\n\n if (!this.icon()) {\n console.warn('[ktButton] iconOnly attend une icône via [icon].');\n }\n if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby')) {\n console.warn(\n '[ktButton] iconOnly sans nom accessible : ajoutez [ariaLabel] (WCAG 4.1.2) — ' +\n 'le bouton serait annoncé sans libellé.',\n );\n }\n });\n }\n\n ngAfterViewInit(): void {\n this.viewInitialized.set(true);\n }\n\n protected haltDisabledEvents(event: Event): void {\n if (!this.isDisabled()) return;\n\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Directive, inject, ElementRef, afterNextRender, isDevMode, InjectionToken, contentChild, input, booleanAttribute, effect } from '@angular/core';
2
+ import { Directive, inject, ElementRef, afterNextRender, isDevMode, InjectionToken, PLATFORM_ID, contentChild, input, booleanAttribute, effect } from '@angular/core';
3
+ import { isPlatformBrowser } from '@angular/common';
3
4
 
4
5
  /**
5
6
  * En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur
@@ -83,6 +84,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
83
84
  *
84
85
  * Quand la carte ancêtre est `disabled`, le lien sort de l'ordre de tabulation (`tabindex="-1"`)
85
86
  * et est annoncé `aria-disabled` : une carte inerte ne piège pas le focus clavier (WCAG 2.4.3).
87
+ *
88
+ * @example
89
+ * ```html
90
+ * <article ktCard interactive>
91
+ * <div ktCardContent>
92
+ * <h3 id="t1">Titre</h3>
93
+ * <a ktCardLink routerLink="/detail" aria-labelledby="t1">Voir le détail</a>
94
+ * </div>
95
+ * </article>
96
+ * ```
86
97
  */
87
98
  class KtCardLink {
88
99
  host = inject(ElementRef).nativeElement;
@@ -128,6 +139,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
128
139
  }], ctorParameters: () => [] });
129
140
 
130
141
  const KT_CARD_CONFIG = new InjectionToken('KT_CARD_CONFIG');
142
+ /**
143
+ * Fournit des défauts de carte (apparence de surface) pour un sous-arbre ou l'application entière.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * providers: [provideKtCard({ variant: 'outlined' })]
148
+ * ```
149
+ */
150
+ function provideKtCard(config) {
151
+ return { provide: KT_CARD_CONFIG, useValue: config };
152
+ }
131
153
  /**
132
154
  * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi
133
155
  * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de
@@ -155,6 +177,7 @@ const KT_CARD_CONFIG = new InjectionToken('KT_CARD_CONFIG');
155
177
  class KtCard {
156
178
  config = inject(KT_CARD_CONFIG, { optional: true });
157
179
  host = inject(ElementRef).nativeElement;
180
+ platformId = inject(PLATFORM_ID);
158
181
  /** Référence réactive sur le lien primaire de la carte */
159
182
  cardLink = contentChild(KtCardLink, { ...(ngDevMode ? { debugName: "cardLink" } : /* istanbul ignore next */ {}), descendants: true });
160
183
  /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */
@@ -171,6 +194,8 @@ class KtCard {
171
194
  // Garde-fou a11y (dev seulement) : une carte interactive sans cible cliquable affiche une
172
195
  // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).
173
196
  effect(() => {
197
+ if (!isPlatformBrowser(this.platformId))
198
+ return;
174
199
  if (!isDevMode() || !this.interactive())
175
200
  return;
176
201
  const hasTarget = this.host.matches('a, button') || !!this.cardLink();
@@ -197,13 +222,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
197
222
 
198
223
  /**
199
224
  * Import ergonomique de toute la famille card en une fois :
200
- * `imports: [KT_CARD]` au lieu d'énumérer chaque directive.
225
+ * `imports: [KtCardImports]` au lieu d'énumérer chaque directive.
201
226
  */
202
- const KT_CARD = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink];
227
+ const KtCardImports = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink];
203
228
 
204
229
  /**
205
230
  * Generated bundle index. Do not edit.
206
231
  */
207
232
 
208
- export { KT_CARD, KT_CARD_CONFIG, KtCard, KtCardActions, KtCardContent, KtCardHeader, KtCardLink, KtCardMedia };
233
+ export { KT_CARD_CONFIG, KtCard, KtCardActions, KtCardContent, KtCardHeader, KtCardImports, KtCardLink, KtCardMedia, provideKtCard };
209
234
  //# sourceMappingURL=ktortu-aaa-card.mjs.map
@@ -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, isDevMode } from '@angular/core';\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@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 /**\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 (dev seulement) : un lien étiré sans nom accessible n'est pas annonçable.\n afterNextRender(() => {\n if (!isDevMode()) 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 Directive,\n ElementRef,\n InjectionToken,\n booleanAttribute,\n contentChild,\n effect,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\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 * 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\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 constructor() {\n // Garde-fou a11y (dev seulement) : 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 (!isDevMode() || !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: [KT_CARD]` au lieu d'énumérer chaque directive.\n */\nexport const KT_CARD = [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-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, isDevMode } from '@angular/core';\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 /**\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 (dev seulement) : un lien étiré sans nom accessible n'est pas annonçable.\n afterNextRender(() => {\n if (!isDevMode()) 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 Directive,\n ElementRef,\n InjectionToken,\n PLATFORM_ID,\n Provider,\n booleanAttribute,\n contentChild,\n effect,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\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\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 constructor() {\n // Garde-fou a11y (dev seulement) : 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 (!isPlatformBrowser(this.platformId)) return;\n if (!isDevMode() || !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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"}