@ktortu/aaa 0.10.2 → 0.11.0

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/button/button.css CHANGED
@@ -213,6 +213,7 @@
213
213
  /* ============ ICON-ONLY (carré = hauteur, icône centrée ; forme suit --btn-radius) ============ */
214
214
  [ktButton][data-icon-only] {
215
215
  --btn-pad-x: 0;
216
+ padding-inline: 0 !important;
216
217
  gap: 0;
217
218
  inline-size: var(--btn-height-md, 44px);
218
219
  min-inline-size: var(--btn-height-md, 44px);
@@ -227,6 +228,87 @@
227
228
  min-inline-size: var(--btn-height-lg, 52px);
228
229
  }
229
230
 
231
+ /* Icône compacte : s'active en mode iconOnly */
232
+ [ktButton][data-compact-icon][data-icon-only]::before {
233
+ content: attr(data-compact-icon);
234
+ font-family: var(--btn-icon-font, var(--kt-icon-font, 'Material Symbols Outlined'));
235
+ font-weight: var(--btn-icon-font-weight, var(--kt-icon-font-weight, normal));
236
+ font-style: normal;
237
+ font-size: var(--btn-icon-size, 20px);
238
+ line-height: 1;
239
+ font-feature-settings: 'liga';
240
+ -webkit-font-smoothing: antialiased;
241
+ }
242
+
243
+ /* Masquage visuel accessible (sr-only) du libellé en mode iconOnly */
244
+ [ktButton][data-icon-only] [ktButtonLabel],
245
+ [ktButton][data-icon-only] .kt-button-label,
246
+ [ktButton][data-icon-only] > span:not([data-icon]):not(.kt-icon):not([class*='icon']) {
247
+ position: absolute;
248
+ inset-block-start: 0;
249
+ inset-inline-start: 0;
250
+ inline-size: 1px;
251
+ block-size: 1px;
252
+ min-block-size: 0;
253
+ padding: 0;
254
+ margin: -1px;
255
+ overflow: hidden;
256
+ clip-path: inset(50%);
257
+ white-space: nowrap;
258
+ }
259
+
260
+ /* ============ BASCULE RESPONSIVE CSS (COLLAPSE-COMPACT) ============ */
261
+ @media (max-width: 599.98px) {
262
+ [ktButton][data-collapse-compact],
263
+ [ktButton][data-icon][data-collapse-compact] {
264
+ --btn-pad-x: 0;
265
+ padding-inline: 0 !important;
266
+ gap: 0;
267
+ inline-size: var(--btn-height-md, 44px);
268
+ min-inline-size: var(--btn-height-md, 44px);
269
+ position: relative;
270
+ }
271
+
272
+ [ktButton][data-size='sm'][data-collapse-compact] {
273
+ inline-size: var(--btn-height-sm, 32px);
274
+ min-inline-size: var(--btn-height-sm, 32px);
275
+ }
276
+
277
+ [ktButton][data-size='lg'][data-collapse-compact] {
278
+ inline-size: var(--btn-height-lg, 52px);
279
+ min-inline-size: var(--btn-height-lg, 52px);
280
+ }
281
+
282
+ /* Remplacement d'icône compacte sous seuil mobile */
283
+ [ktButton][data-compact-icon][data-collapse-compact]::before {
284
+ content: attr(data-compact-icon);
285
+ font-family: var(--btn-icon-font, var(--kt-icon-font, 'Material Symbols Outlined'));
286
+ font-weight: var(--btn-icon-font-weight, var(--kt-icon-font-weight, normal));
287
+ font-style: normal;
288
+ font-size: var(--btn-icon-size, 20px);
289
+ line-height: 1;
290
+ font-feature-settings: 'liga';
291
+ -webkit-font-smoothing: antialiased;
292
+ }
293
+
294
+ /* Masquage visuel accessible du libellé sous seuil mobile */
295
+ [ktButton][data-collapse-compact] [ktButtonLabel],
296
+ [ktButton][data-collapse-compact] .kt-button-label,
297
+ [ktButton][data-collapse-compact] > span:not([data-icon]):not(.kt-icon):not([class*='icon']) {
298
+ position: absolute;
299
+ inset-block-start: 0;
300
+ inset-inline-start: 0;
301
+ inline-size: 1px;
302
+ block-size: 1px;
303
+ min-block-size: 0;
304
+ padding: 0;
305
+ margin: -1px;
306
+ overflow: hidden;
307
+ clip-path: inset(50%);
308
+ white-space: nowrap;
309
+ }
310
+ }
311
+
230
312
  /* sm boutons : zone de clic étendue à au moins 44x44px (AAA 2.5.5) via ::after transparent.
231
313
  Hors loading uniquement : le spinner occupe déjà ::after (états mutuellement exclusifs). */
232
314
  [ktButton][data-size='sm']:not(.loading)::after {
@@ -52,6 +52,8 @@ class KtButton {
52
52
  fullWidth = input(false, { ...(ngDevMode ? { debugName: "fullWidth" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
53
53
  /** Bouton carré sans texte ; impose un nom accessible via `ariaLabel`. @default false */
54
54
  iconOnly = input(false, { ...(ngDevMode ? { debugName: "iconOnly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
55
+ /** Bascule automatiquement le bouton en icône seule sous le seuil compact (600px). @default false */
56
+ collapseCompact = input(false, { ...(ngDevMode ? { debugName: "collapseCompact" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
55
57
  /** Nom accessible. **Obligatoire si `iconOnly`** ; à défaut, l'`aria-label` natif est préservé. @default undefined */
56
58
  ariaLabel = input(/* @ts-ignore */
57
59
  ...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
@@ -63,6 +65,13 @@ class KtButton {
63
65
  /** Nom de l'icône (rendu via CSS `data-icon`). @default undefined */
64
66
  icon = input(/* @ts-ignore */
65
67
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
68
+ /**
69
+ * Nom de l'icône spécifique au mode compact (`iconOnly` ou `collapseCompact`).
70
+ * Permet d'avoir un bouton avec texte seul sur grand écran et une icône dédiée sur petit écran.
71
+ * @default undefined
72
+ */
73
+ compactIcon = input(/* @ts-ignore */
74
+ ...(ngDevMode ? [undefined, { debugName: "compactIcon" }] : /* istanbul ignore next */ []));
66
75
  /** Position de l'icône relative au texte. @default 'start' */
67
76
  iconPosition = input('start', /* @ts-ignore */
68
77
  ...(ngDevMode ? [{ debugName: "iconPosition" }] : /* istanbul ignore next */ []));
@@ -87,14 +96,25 @@ class KtButton {
87
96
  return;
88
97
  if (!this.viewInitialized())
89
98
  return;
90
- if (!this.auditEnabled || !this.iconOnly())
99
+ if (!this.auditEnabled)
91
100
  return;
92
- if (!this.icon()) {
93
- console.warn('[ktButton] iconOnly attend une icône via [icon].');
101
+ if (this.iconOnly()) {
102
+ if (!this.icon() && !this.compactIcon()) {
103
+ console.warn('[ktButton] iconOnly attend une icône via [icon].');
104
+ }
105
+ if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby')) {
106
+ console.warn('[ktButton] iconOnly sans nom accessible : ajoutez [ariaLabel] (WCAG 4.1.2) — ' +
107
+ 'le bouton serait annoncé sans libellé.');
108
+ }
94
109
  }
95
- if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby')) {
96
- console.warn('[ktButton] iconOnly sans nom accessible : ajoutez [ariaLabel] (WCAG 4.1.2) — ' +
97
- 'le bouton serait annoncé sans libellé.');
110
+ else if (this.collapseCompact()) {
111
+ if (!this.icon() && !this.compactIcon()) {
112
+ console.warn('[ktButton] collapseCompact attend une icône via [icon] ou [compactIcon].');
113
+ }
114
+ const hasText = Boolean(this.host.textContent?.trim());
115
+ if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby') && !hasText) {
116
+ console.warn('[ktButton] collapseCompact sans nom accessible : ajoutez un libellé ou [ariaLabel] (WCAG 4.1.2).');
117
+ }
98
118
  }
99
119
  });
100
120
  }
@@ -108,7 +128,7 @@ class KtButton {
108
128
  event.stopImmediatePropagation();
109
129
  }
110
130
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtButton, deps: [], target: i0.ɵɵFactoryTarget.Directive });
111
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: KtButton, isStandalone: true, selector: "button[ktButton], a[ktButton]", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, disabledInteractive: { classPropertyName: "disabledInteractive", publicName: "disabledInteractive", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "haltDisabledEvents($event)" }, properties: { "attr.data-mode": "mode()", "attr.data-color": "color()", "attr.data-size": "size()", "attr.data-full-width": "fullWidth() ? \"\" : null", "attr.data-icon-only": "iconOnly() ? \"\" : null", "attr.type": "!isLink ? type() : null", "attr.aria-label": "resolvedAriaLabel()", "class.loading": "loading()", "class.disabled": "isDisabled()", "attr.data-icon": "icon()", "attr.data-icon-position": "iconPosition()", "attr.disabled": "isDisabled() && !disabledInteractive() && !isLink ? \"\" : null", "attr.aria-disabled": "isDisabled() && (disabledInteractive() || isLink) ? \"true\" : null", "attr.tabindex": "isDisabled() && !disabledInteractive() && isLink ? \"-1\" : null", "attr.aria-busy": "loading() ? \"true\" : null" } }, ngImport: i0 });
131
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: KtButton, isStandalone: true, selector: "button[ktButton], a[ktButton]", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, collapseCompact: { classPropertyName: "collapseCompact", publicName: "collapseCompact", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, compactIcon: { classPropertyName: "compactIcon", publicName: "compactIcon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, disabledInteractive: { classPropertyName: "disabledInteractive", publicName: "disabledInteractive", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "haltDisabledEvents($event)" }, properties: { "attr.data-mode": "mode()", "attr.data-color": "color()", "attr.data-size": "size()", "attr.data-full-width": "fullWidth() ? \"\" : null", "attr.data-icon-only": "iconOnly() ? \"\" : null", "attr.data-collapse-compact": "collapseCompact() ? \"\" : null", "attr.type": "!isLink ? type() : null", "attr.aria-label": "resolvedAriaLabel()", "class.loading": "loading()", "class.disabled": "isDisabled()", "attr.data-icon": "icon()", "attr.data-compact-icon": "compactIcon()", "attr.data-icon-position": "iconPosition()", "attr.disabled": "isDisabled() && !disabledInteractive() && !isLink ? \"\" : null", "attr.aria-disabled": "isDisabled() && (disabledInteractive() || isLink) ? \"true\" : null", "attr.tabindex": "isDisabled() && !disabledInteractive() && isLink ? \"-1\" : null", "attr.aria-busy": "loading() ? \"true\" : null" } }, ngImport: i0 });
112
132
  }
113
133
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtButton, decorators: [{
114
134
  type: Directive,
@@ -120,11 +140,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
120
140
  '[attr.data-size]': 'size()',
121
141
  '[attr.data-full-width]': 'fullWidth() ? "" : null',
122
142
  '[attr.data-icon-only]': 'iconOnly() ? "" : null',
143
+ '[attr.data-collapse-compact]': 'collapseCompact() ? "" : null',
123
144
  '[attr.type]': '!isLink ? type() : null',
124
145
  '[attr.aria-label]': 'resolvedAriaLabel()',
125
146
  '[class.loading]': 'loading()',
126
147
  '[class.disabled]': 'isDisabled()',
127
148
  '[attr.data-icon]': 'icon()',
149
+ '[attr.data-compact-icon]': 'compactIcon()',
128
150
  '[attr.data-icon-position]': 'iconPosition()',
129
151
  '[attr.disabled]': 'isDisabled() && !disabledInteractive() && !isLink ? "" : null',
130
152
  '[attr.aria-disabled]': 'isDisabled() && (disabledInteractive() || isLink) ? "true" : null',
@@ -133,11 +155,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
133
155
  '(click)': 'haltDisabledEvents($event)',
134
156
  },
135
157
  }]
136
- }], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], disabledInteractive: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledInteractive", required: false }] }] } });
158
+ }], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], collapseCompact: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseCompact", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], compactIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "compactIcon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], disabledInteractive: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledInteractive", required: false }] }] } });
159
+
160
+ /**
161
+ * Libellé textuel pour un bouton `[ktButton]`.
162
+ * Permet de masquer visuellement le texte de façon accessible (WCAG 4.1.2 / 2.5.3)
163
+ * lorsque le bouton bascule en mode compact / icône seule (`iconOnly` ou `collapseCompact`).
164
+ *
165
+ * @example
166
+ * ```html
167
+ * <button ktButton icon="download" collapseCompact>
168
+ * <span ktButtonLabel>Télécharger</span>
169
+ * </button>
170
+ * ```
171
+ */
172
+ class KtButtonLabel {
173
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtButtonLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
174
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtButtonLabel, isStandalone: true, selector: "[ktButtonLabel], .kt-button-label", host: { classAttribute: "kt-button-label" }, ngImport: i0 });
175
+ }
176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtButtonLabel, decorators: [{
177
+ type: Directive,
178
+ args: [{
179
+ selector: '[ktButtonLabel], .kt-button-label',
180
+ host: {
181
+ class: 'kt-button-label',
182
+ },
183
+ }]
184
+ }] });
137
185
 
138
186
  /**
139
187
  * Generated bundle index. Do not edit.
140
188
  */
141
189
 
142
- export { KT_BUTTON_CONFIG, KtButton, provideKtButton };
190
+ export { KT_BUTTON_CONFIG, KtButton, KtButtonLabel, provideKtButton };
143
191
  //# 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 DestroyRef,\n Directive,\n ElementRef,\n InjectionToken,\n Provider,\n booleanAttribute,\n computed,\n inject,\n input,\n AfterViewInit,\n PLATFORM_ID,\n effect,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\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 isDestroyed = false;\n private readonly viewInitialized = signal(false);\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\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 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.viewInitialized()) return;\n if (!this.auditEnabled || !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/button-label.directive.ts","../../../../projects/ktortu/aaa/button/ktortu-aaa-button.ts"],"sourcesContent":["import {\n DestroyRef,\n Directive,\n ElementRef,\n InjectionToken,\n Provider,\n booleanAttribute,\n computed,\n inject,\n input,\n AfterViewInit,\n PLATFORM_ID,\n effect,\n signal,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\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.data-collapse-compact]': 'collapseCompact() ? \"\" : 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-compact-icon]': 'compactIcon()',\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 isDestroyed = false;\n private readonly viewInitialized = signal(false);\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\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 /** Bascule automatiquement le bouton en icône seule sous le seuil compact (600px). @default false */\n readonly collapseCompact = 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 /**\n * Nom de l'icône spécifique au mode compact (`iconOnly` ou `collapseCompact`).\n * Permet d'avoir un bouton avec texte seul sur grand écran et une icône dédiée sur petit écran.\n * @default undefined\n */\n readonly compactIcon = 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 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.viewInitialized()) return;\n if (!this.auditEnabled) return;\n\n if (this.iconOnly()) {\n if (!this.icon() && !this.compactIcon()) {\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 } else if (this.collapseCompact()) {\n if (!this.icon() && !this.compactIcon()) {\n console.warn('[ktButton] collapseCompact attend une icône via [icon] ou [compactIcon].');\n }\n const hasText = Boolean(this.host.textContent?.trim());\n if (!this.resolvedAriaLabel() && !this.host.hasAttribute('aria-labelledby') && !hasText) {\n console.warn(\n '[ktButton] collapseCompact sans nom accessible : ajoutez un libellé ou [ariaLabel] (WCAG 4.1.2).',\n );\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","import { Directive } from '@angular/core';\n\n/**\n * Libellé textuel pour un bouton `[ktButton]`.\n * Permet de masquer visuellement le texte de façon accessible (WCAG 4.1.2 / 2.5.3)\n * lorsque le bouton bascule en mode compact / icône seule (`iconOnly` ou `collapseCompact`).\n *\n * @example\n * ```html\n * <button ktButton icon=\"download\" collapseCompact>\n * <span ktButtonLabel>Télécharger</span>\n * </button>\n * ```\n */\n@Directive({\n selector: '[ktButtonLabel], .kt-button-label',\n host: {\n class: 'kt-button-label',\n },\n})\nexport class KtButtonLabel {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -108,6 +108,10 @@ class KtDialogDescription {
108
108
  if (this.dialogRef?.config.ariaDescribedBy === this.id) {
109
109
  this.dialogRef.config.ariaDescribedBy = null;
110
110
  }
111
+ const container = this.host.closest('.cdk-dialog-container');
112
+ if (container && container.getAttribute('aria-describedby') === this.id) {
113
+ this.renderer.removeAttribute(container, 'aria-describedby');
114
+ }
111
115
  }
112
116
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtDialogDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
113
117
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtDialogDescription, isStandalone: true, selector: "[ktDialogDescription]", host: { properties: { "id": "id" } }, ngImport: i0 });
@@ -780,7 +784,6 @@ function injectKtDialogOpener(component, baseConfig) {
780
784
  const merged = [...new Set([...resolveKtDialogPanelClass(presentation, viewport.isCompact()), ...extra])];
781
785
  const uid = idGen.generateId('dialog');
782
786
  const titleId = `kt-dialog-title-${uid}`;
783
- const descId = `kt-dialog-desc-${uid}`;
784
787
  return dialog.open(component, {
785
788
  // Plancher a11y garanti par l'opener (ariaModal/role/restoreFocus/autoFocus…), même si le
786
789
  // consommateur n'a pas câblé provideKtDialogDefaults(). `...rest` (config par appel) prime.
@@ -792,7 +795,6 @@ function injectKtDialogOpener(component, baseConfig) {
792
795
  providers: () => [{ provide: KT_DIALOG_CONFIG, useValue: ktConfig }],
793
796
  },
794
797
  ariaLabelledBy: titleId,
795
- ariaDescribedBy: descId,
796
798
  ...rest,
797
799
  panelClass: merged,
798
800
  data,
@@ -914,7 +916,7 @@ class KtAlertDialog {
914
916
  {{ data.closeLabel || 'Fermer' }}
915
917
  </button>
916
918
  </footer>
917
- `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "ariaLabel", "type", "loading", "icon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogClose, selector: "[ktDialogClose]", inputs: ["ktDialogClose"] }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
919
+ `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "collapseCompact", "ariaLabel", "type", "loading", "icon", "compactIcon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogClose, selector: "[ktDialogClose]", inputs: ["ktDialogClose"] }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
918
920
  }
919
921
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtAlertDialog, decorators: [{
920
922
  type: Component,
@@ -1008,7 +1010,7 @@ class KtConfirmDialog {
1008
1010
  {{ data.confirmLabel || 'Oui' }}
1009
1011
  </button>
1010
1012
  </footer>
1011
- `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "ariaLabel", "type", "loading", "icon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1013
+ `, isInline: true, dependencies: [{ kind: "directive", type: KtButton, selector: "button[ktButton], a[ktButton]", inputs: ["mode", "color", "size", "fullWidth", "iconOnly", "collapseCompact", "ariaLabel", "type", "loading", "icon", "compactIcon", "iconPosition", "disabled", "disabledInteractive"] }, { kind: "directive", type: KtDialogTitle, selector: "[ktDialogTitle]" }, { kind: "directive", type: KtDialogDescription, selector: "[ktDialogDescription]" }, { kind: "directive", type: KtDialogActions, selector: "[ktDialogActions]" }, { kind: "directive", type: KtDialogFocusInitial, selector: "[ktDialogFocusInitial]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1012
1014
  }
1013
1015
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtConfirmDialog, decorators: [{
1014
1016
  type: Component,