@ktortu/aaa 0.1.0-beta.0 → 0.9.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/README.md +23 -7
- package/cdk/styles/tabs.css +100 -21
- package/fesm2022/ktortu-aaa-button.mjs +18 -11
- package/fesm2022/ktortu-aaa-button.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-card.mjs +29 -4
- package/fesm2022/ktortu-aaa-card.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-cdk.mjs +58 -15
- package/fesm2022/ktortu-aaa-cdk.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-dialog.mjs +69 -12
- package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-forms.mjs +455 -260
- package/fesm2022/ktortu-aaa-forms.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-i18n.mjs +114 -0
- package/fesm2022/ktortu-aaa-i18n.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-menu.mjs +38 -13
- package/fesm2022/ktortu-aaa-menu.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-tabs.mjs +319 -42
- package/fesm2022/ktortu-aaa-tabs.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-tooltip.mjs +3 -2
- package/fesm2022/ktortu-aaa-tooltip.mjs.map +1 -1
- package/fesm2022/ktortu-aaa.mjs +1 -0
- package/fesm2022/ktortu-aaa.mjs.map +1 -1
- package/forms/radio/radio-group.css +3 -3
- package/forms/styles/field-box.css +150 -2
- package/forms/styles/tokens.css +3 -0
- package/menu/menu.css +8 -4
- package/package.json +5 -1
- package/types/ktortu-aaa-button.d.ts +22 -8
- package/types/ktortu-aaa-card.d.ts +24 -4
- package/types/ktortu-aaa-cdk.d.ts +38 -0
- package/types/ktortu-aaa-dialog.d.ts +45 -9
- package/types/ktortu-aaa-forms.d.ts +336 -149
- package/types/ktortu-aaa-i18n.d.ts +74 -0
- package/types/ktortu-aaa-menu.d.ts +15 -8
- package/types/ktortu-aaa-tabs.d.ts +130 -13
- package/types/ktortu-aaa-tooltip.d.ts +5 -0
- package/types/ktortu-aaa.d.ts +1 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { makeEnvironmentProviders } from '@angular/core';
|
|
2
|
+
import { KT_FIELD_CONFIG, KT_SELECT_CONFIG, KT_CHIPS_CONFIG } from '@ktortu/aaa/forms';
|
|
3
|
+
import { KT_TABS_CONFIG } from '@ktortu/aaa/tabs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enregistre, en un seul appel, les libellés fournis sur les tokens de config de la lib.
|
|
7
|
+
* Base = défauts anglais de chaque composant ; seules les familles **présentes** dans `t` sont
|
|
8
|
+
* fournies (les autres gardent leur défaut anglais et restent surchargeables ailleurs).
|
|
9
|
+
*
|
|
10
|
+
* À placer dans les `providers` de `app.config.ts` (ou de tout injecteur).
|
|
11
|
+
*
|
|
12
|
+
* @param t Libellés à fournir, groupés par famille ; seules les familles présentes sont enregistrées.
|
|
13
|
+
* @returns Les `EnvironmentProviders` liant chaque famille présente à son token de config.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* provideKtTranslations({ tabs: { nextLabel: 'Suivant', previousLabel: 'Précédent' } })
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function provideKtTranslations(t) {
|
|
20
|
+
const providers = [];
|
|
21
|
+
if (t.field)
|
|
22
|
+
providers.push({ provide: KT_FIELD_CONFIG, useValue: t.field });
|
|
23
|
+
if (t.select)
|
|
24
|
+
providers.push({ provide: KT_SELECT_CONFIG, useValue: t.select });
|
|
25
|
+
if (t.chips)
|
|
26
|
+
providers.push({ provide: KT_CHIPS_CONFIG, useValue: t.chips });
|
|
27
|
+
if (t.tabs)
|
|
28
|
+
providers.push({ provide: KT_TABS_CONFIG, useValue: t.tabs });
|
|
29
|
+
return makeEnvironmentProviders(providers);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fusion par famille de deux {@link KtTranslations} : `over` écrase `base`, clé par clé.
|
|
33
|
+
* @param base Libellés de référence (ex. un dictionnaire de langue complet).
|
|
34
|
+
* @param over Libellés prioritaires venant écraser `base`.
|
|
35
|
+
* @returns Un nouveau {@link KtTranslations} fusionné, famille par famille.
|
|
36
|
+
* @example mergeKtTranslations(KT_FR_TRANSLATIONS, { tabs: { nextLabel: 'Suivant' } })
|
|
37
|
+
*/
|
|
38
|
+
function mergeKtTranslations(base, over) {
|
|
39
|
+
return {
|
|
40
|
+
field: { ...base.field, ...over.field },
|
|
41
|
+
select: { ...base.select, ...over.select },
|
|
42
|
+
chips: { ...base.chips, ...over.chips },
|
|
43
|
+
tabs: { ...base.tabs, ...over.tabs },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Dictionnaire **français** complet de tous les libellés de la lib. Figé et réutilisable :
|
|
49
|
+
* importable tel quel (ex. pour le composer avec d'autres réglages) ou via {@link provideKtDefaultFR}.
|
|
50
|
+
* Les libellés à pluriels sont des fonctions, pour laisser la grammaire au consommateur.
|
|
51
|
+
*/
|
|
52
|
+
const KT_FR_TRANSLATIONS = {
|
|
53
|
+
field: {
|
|
54
|
+
clearLabel: 'Effacer',
|
|
55
|
+
helpLabel: 'Aide',
|
|
56
|
+
numberParseError: 'La valeur saisie doit être un nombre valide.',
|
|
57
|
+
},
|
|
58
|
+
select: {
|
|
59
|
+
emptyText: 'Aucune option',
|
|
60
|
+
closeLabel: 'Fermer',
|
|
61
|
+
filterLabel: 'Filtrer les options',
|
|
62
|
+
// En français, 0 ET 1 prennent le singulier (≠ anglais où seul 1 est singulier).
|
|
63
|
+
filterResultsText: (count) => (count <= 1 ? `${count} résultat` : `${count} résultats`),
|
|
64
|
+
removeItemLabel: (itemLabel) => `Retirer ${itemLabel}`,
|
|
65
|
+
selectedItemsLabel: (fieldLabel) => fieldLabel ? `Éléments sélectionnés pour ${fieldLabel}` : 'Éléments sélectionnés',
|
|
66
|
+
selectionSummaryText: (count) => (count <= 1 ? `${count} élément sélectionné` : `${count} éléments sélectionnés`),
|
|
67
|
+
itemRemovedText: (itemLabel) => `${itemLabel} retiré`,
|
|
68
|
+
selectionCountText: (count) => (count <= 1 ? `${count} sélectionné` : `${count} sélectionnés`),
|
|
69
|
+
selectAllLabel: 'Tout sélectionner',
|
|
70
|
+
clearAllLabel: 'Tout effacer',
|
|
71
|
+
moreChipsLabel: (hiddenCount) => `+${hiddenCount} de plus`,
|
|
72
|
+
lessChipsLabel: 'Afficher moins',
|
|
73
|
+
truncatedResultsText: (max, total) => `Affichage des ${max} premiers résultats sur ${total}. Affinez votre recherche pour en voir plus.`,
|
|
74
|
+
truncatedResultsAnnouncement: (max, total) => `${max} résultats affichés sur ${total}. Affinez votre recherche pour en voir plus.`,
|
|
75
|
+
},
|
|
76
|
+
chips: {
|
|
77
|
+
removeItemLabel: (itemLabel) => `Retirer ${itemLabel}`,
|
|
78
|
+
itemRemovedText: (itemLabel) => `${itemLabel} retiré`,
|
|
79
|
+
moreLabel: (hiddenCount) => `+${hiddenCount} de plus`,
|
|
80
|
+
lessLabel: 'Afficher moins',
|
|
81
|
+
listLabel: 'Éléments sélectionnés',
|
|
82
|
+
},
|
|
83
|
+
tabs: {
|
|
84
|
+
previousLabel: 'Onglets précédents',
|
|
85
|
+
nextLabel: 'Onglets suivants',
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Enregistre les libellés **français** par défaut sur toute la lib, en un seul appel.
|
|
90
|
+
* `overrides` permet d'ajuster ponctuellement sans tout réécrire (fusion par famille,
|
|
91
|
+
* cf. {@link mergeKtTranslations}). À placer dans les `providers` de `app.config.ts`.
|
|
92
|
+
*
|
|
93
|
+
* @param overrides Libellés à ajuster par-dessus le français (fusion par famille). Défaut : `{}`.
|
|
94
|
+
* @returns Les `EnvironmentProviders` enregistrant le français (fusionné aux `overrides`).
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // app.config.ts
|
|
98
|
+
* providers: [
|
|
99
|
+
* provideKtDefaultFR(), // tout en français
|
|
100
|
+
* // ou en ajustant un libellé précis :
|
|
101
|
+
* provideKtDefaultFR({ tabs: { nextLabel: 'Suivant' } }),
|
|
102
|
+
* ]
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
function provideKtDefaultFR(overrides = {}) {
|
|
106
|
+
return provideKtTranslations(mergeKtTranslations(KT_FR_TRANSLATIONS, overrides));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generated bundle index. Do not edit.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
export { KT_FR_TRANSLATIONS, mergeKtTranslations, provideKtDefaultFR, provideKtTranslations };
|
|
114
|
+
//# sourceMappingURL=ktortu-aaa-i18n.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktortu-aaa-i18n.mjs","sources":["../../../../projects/ktortu/aaa/i18n/translations.ts","../../../../projects/ktortu/aaa/i18n/fr.ts","../../../../projects/ktortu/aaa/i18n/ktortu-aaa-i18n.ts"],"sourcesContent":["import { EnvironmentProviders, Provider, makeEnvironmentProviders } from '@angular/core';\n\nimport { KT_FIELD_CONFIG, KT_CHIPS_CONFIG, KT_SELECT_CONFIG } from '@ktortu/aaa/forms';\nimport type { KtFieldConfig, KtChipsConfig, KtSelectConfigOptions } from '@ktortu/aaa/forms';\nimport { KT_TABS_CONFIG } from '@ktortu/aaa/tabs';\nimport type { KtTabsConfig } from '@ktortu/aaa/tabs';\n\n/**\n * Agrégat de TOUS les libellés traduisibles de la lib, groupés par famille de composants.\n * Chaque clé reprend la config du token correspondant (`Partial` : tout est optionnel — on ne\n * fournit que ce qu'on veut traduire). C'est l'« interface de traduction » unique de la lib :\n * un point d'entrée pour brancher une langue d'un coup.\n *\n * @see provideKtTranslations pour enregistrer un jeu de libellés.\n * @see provideKtDefaultFR pour partir des libellés français.\n */\nexport interface KtTranslations {\n /** Libellés des champs (`clearLabel`, `helpLabel`) → {@link KT_FIELD_CONFIG}. */\n field?: Partial<KtFieldConfig>;\n /** Libellés des selects / multi-selects → {@link KT_SELECT_CONFIG}. */\n select?: Partial<KtSelectConfigOptions>;\n /** Libellés des listes de chips → {@link KT_CHIPS_CONFIG}. */\n chips?: Partial<KtChipsConfig>;\n /** Libellés de la pagination des onglets → {@link KT_TABS_CONFIG}. */\n tabs?: Partial<KtTabsConfig>;\n}\n\n/**\n * Enregistre, en un seul appel, les libellés fournis sur les tokens de config de la lib.\n * Base = défauts anglais de chaque composant ; seules les familles **présentes** dans `t` sont\n * fournies (les autres gardent leur défaut anglais et restent surchargeables ailleurs).\n *\n * À placer dans les `providers` de `app.config.ts` (ou de tout injecteur).\n *\n * @param t Libellés à fournir, groupés par famille ; seules les familles présentes sont enregistrées.\n * @returns Les `EnvironmentProviders` liant chaque famille présente à son token de config.\n * @example\n * ```ts\n * provideKtTranslations({ tabs: { nextLabel: 'Suivant', previousLabel: 'Précédent' } })\n * ```\n */\nexport function provideKtTranslations(t: KtTranslations): EnvironmentProviders {\n const providers: Provider[] = [];\n if (t.field) providers.push({ provide: KT_FIELD_CONFIG, useValue: t.field });\n if (t.select) providers.push({ provide: KT_SELECT_CONFIG, useValue: t.select });\n if (t.chips) providers.push({ provide: KT_CHIPS_CONFIG, useValue: t.chips });\n if (t.tabs) providers.push({ provide: KT_TABS_CONFIG, useValue: t.tabs });\n return makeEnvironmentProviders(providers);\n}\n\n/**\n * Fusion par famille de deux {@link KtTranslations} : `over` écrase `base`, clé par clé.\n * @param base Libellés de référence (ex. un dictionnaire de langue complet).\n * @param over Libellés prioritaires venant écraser `base`.\n * @returns Un nouveau {@link KtTranslations} fusionné, famille par famille.\n * @example mergeKtTranslations(KT_FR_TRANSLATIONS, { tabs: { nextLabel: 'Suivant' } })\n */\nexport function mergeKtTranslations(base: KtTranslations, over: KtTranslations): KtTranslations {\n return {\n field: { ...base.field, ...over.field },\n select: { ...base.select, ...over.select },\n chips: { ...base.chips, ...over.chips },\n tabs: { ...base.tabs, ...over.tabs },\n };\n}\n","import { EnvironmentProviders } from '@angular/core';\n\nimport { KtTranslations, mergeKtTranslations, provideKtTranslations } from './translations';\n\n/**\n * Dictionnaire **français** complet de tous les libellés de la lib. Figé et réutilisable :\n * importable tel quel (ex. pour le composer avec d'autres réglages) ou via {@link provideKtDefaultFR}.\n * Les libellés à pluriels sont des fonctions, pour laisser la grammaire au consommateur.\n */\nexport const KT_FR_TRANSLATIONS: KtTranslations = {\n field: {\n clearLabel: 'Effacer',\n helpLabel: 'Aide',\n numberParseError: 'La valeur saisie doit être un nombre valide.',\n },\n select: {\n emptyText: 'Aucune option',\n closeLabel: 'Fermer',\n filterLabel: 'Filtrer les options',\n // En français, 0 ET 1 prennent le singulier (≠ anglais où seul 1 est singulier).\n filterResultsText: (count) => (count <= 1 ? `${count} résultat` : `${count} résultats`),\n removeItemLabel: (itemLabel) => `Retirer ${itemLabel}`,\n selectedItemsLabel: (fieldLabel) =>\n fieldLabel ? `Éléments sélectionnés pour ${fieldLabel}` : 'Éléments sélectionnés',\n selectionSummaryText: (count) => (count <= 1 ? `${count} élément sélectionné` : `${count} éléments sélectionnés`),\n itemRemovedText: (itemLabel) => `${itemLabel} retiré`,\n selectionCountText: (count) => (count <= 1 ? `${count} sélectionné` : `${count} sélectionnés`),\n selectAllLabel: 'Tout sélectionner',\n clearAllLabel: 'Tout effacer',\n moreChipsLabel: (hiddenCount) => `+${hiddenCount} de plus`,\n lessChipsLabel: 'Afficher moins',\n truncatedResultsText: (max, total) =>\n `Affichage des ${max} premiers résultats sur ${total}. Affinez votre recherche pour en voir plus.`,\n truncatedResultsAnnouncement: (max, total) =>\n `${max} résultats affichés sur ${total}. Affinez votre recherche pour en voir plus.`,\n },\n chips: {\n removeItemLabel: (itemLabel) => `Retirer ${itemLabel}`,\n itemRemovedText: (itemLabel) => `${itemLabel} retiré`,\n moreLabel: (hiddenCount) => `+${hiddenCount} de plus`,\n lessLabel: 'Afficher moins',\n listLabel: 'Éléments sélectionnés',\n },\n tabs: {\n previousLabel: 'Onglets précédents',\n nextLabel: 'Onglets suivants',\n },\n};\n\n/**\n * Enregistre les libellés **français** par défaut sur toute la lib, en un seul appel.\n * `overrides` permet d'ajuster ponctuellement sans tout réécrire (fusion par famille,\n * cf. {@link mergeKtTranslations}). À placer dans les `providers` de `app.config.ts`.\n *\n * @param overrides Libellés à ajuster par-dessus le français (fusion par famille). Défaut : `{}`.\n * @returns Les `EnvironmentProviders` enregistrant le français (fusionné aux `overrides`).\n * @example\n * ```ts\n * // app.config.ts\n * providers: [\n * provideKtDefaultFR(), // tout en français\n * // ou en ajustant un libellé précis :\n * provideKtDefaultFR({ tabs: { nextLabel: 'Suivant' } }),\n * ]\n * ```\n */\nexport function provideKtDefaultFR(overrides: KtTranslations = {}): EnvironmentProviders {\n return provideKtTranslations(mergeKtTranslations(KT_FR_TRANSLATIONS, overrides));\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,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, inject,
|
|
2
|
+
import { Directive, inject, PLATFORM_ID, effect, ElementRef, model, afterNextRender, isDevMode, InjectionToken, input, computed } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser } from '@angular/common';
|
|
4
4
|
import { MenuItem, MenuTrigger } from '@angular/aria/menu';
|
|
5
5
|
import { KtIdGenerator } from '@ktortu/aaa/cdk';
|
|
@@ -55,7 +55,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
class KtMenuItem {
|
|
58
|
-
host = inject(ElementRef).nativeElement;
|
|
59
58
|
// Optionnel : `[ktMenuItem]` reste utilisable seul (simple thème) même hors d'un `[ngMenuItem]`.
|
|
60
59
|
ariaItem = inject(MenuItem, { self: true, optional: true });
|
|
61
60
|
platformId = inject(PLATFORM_ID);
|
|
@@ -72,7 +71,7 @@ class KtMenuItem {
|
|
|
72
71
|
this.anchorName = undefined;
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
75
|
-
this.anchorName ??= `--kt-submenu-anchor-${this.idGen.generateId()}`;
|
|
74
|
+
this.anchorName ??= `--kt-submenu-anchor-${this.idGen.generateId('menu')}`;
|
|
76
75
|
if (isPlatformBrowser(this.platformId)) {
|
|
77
76
|
submenu.style.setProperty('position-anchor', this.anchorName);
|
|
78
77
|
// Marque la surface comme un SOUS-menu : menu.css l'ouvre en latéral (inline-end) et non
|
|
@@ -131,11 +130,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
131
130
|
* ```
|
|
132
131
|
*/
|
|
133
132
|
class KtMenuTrigger {
|
|
134
|
-
host = inject(ElementRef).nativeElement;
|
|
135
133
|
ariaTrigger = inject(MenuTrigger, { self: true });
|
|
136
134
|
platformId = inject(PLATFORM_ID);
|
|
137
135
|
idGen = inject(KtIdGenerator);
|
|
138
|
-
anchorName = `--kt-menu-anchor-${this.idGen.generateId()}`;
|
|
136
|
+
anchorName = `--kt-menu-anchor-${this.idGen.generateId('menu')}`;
|
|
139
137
|
constructor() {
|
|
140
138
|
effect(() => {
|
|
141
139
|
const menuEl = this.ariaTrigger.menu()?.element;
|
|
@@ -171,7 +169,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
171
169
|
* Elles ne touchent pas au `role` (laissé à l'input `role` d'aria, posé par le consommateur) pour
|
|
172
170
|
* éviter tout conflit de binding sur `[attr.role]` ; la démo montre l'usage `role="menuitemradio"`.
|
|
173
171
|
*/
|
|
174
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Activation commune : clic ET clavier (Entrée / Espace). Les deux liaisons coexistent VOLONTAIREMENT
|
|
174
|
+
* pour supporter un hôte non-`<button>` (ex. `<div ngMenuItem>`), où le clavier n'émet pas de `click`
|
|
175
|
+
* natif. Sur un `<button>`, `activate()` appelle `event.preventDefault()` sur le `keydown`, ce qui
|
|
176
|
+
* neutralise le `click` synthétique → une seule bascule (pas de double-toggle). Couvert en E2E.
|
|
177
|
+
*/
|
|
175
178
|
const ACTIVATION_HOST = {
|
|
176
179
|
'(click)': 'activate($event)',
|
|
177
180
|
'(keydown.enter)': 'activate($event)',
|
|
@@ -190,9 +193,21 @@ const ACTIVATION_HOST = {
|
|
|
190
193
|
*/
|
|
191
194
|
class KtMenuItemCheckbox {
|
|
192
195
|
ariaItem = inject(MenuItem, { self: true, optional: true });
|
|
193
|
-
|
|
196
|
+
host = inject(ElementRef).nativeElement;
|
|
197
|
+
/**
|
|
198
|
+
* État coché, bidirectionnel : `[(checked)]`.
|
|
199
|
+
* @default false
|
|
200
|
+
*/
|
|
194
201
|
checked = model(false, /* @ts-ignore */
|
|
195
202
|
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
203
|
+
constructor() {
|
|
204
|
+
// Garde-fou dev : sans `role="menuitemcheckbox"`, `aria-checked` est ignoré → item muet pour les SR.
|
|
205
|
+
afterNextRender(() => {
|
|
206
|
+
if (!isDevMode() || this.host.getAttribute('role') === 'menuitemcheckbox')
|
|
207
|
+
return;
|
|
208
|
+
console.warn('[ktMenuItemCheckbox] attend `role="menuitemcheckbox"` sur l’hôte, sinon `aria-checked` est inopérant.');
|
|
209
|
+
});
|
|
210
|
+
}
|
|
196
211
|
activate(event) {
|
|
197
212
|
if (this.ariaItem?.disabled())
|
|
198
213
|
return;
|
|
@@ -213,7 +228,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
213
228
|
'[attr.aria-checked]': 'checked()',
|
|
214
229
|
},
|
|
215
230
|
}]
|
|
216
|
-
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
231
|
+
}], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
|
|
217
232
|
/** Source de la valeur sélectionnée d'un groupe radio, exposée aux `[ktMenuItemRadio]` enfants. */
|
|
218
233
|
const KT_MENU_RADIO_GROUP = new InjectionToken('KT_MENU_RADIO_GROUP');
|
|
219
234
|
/**
|
|
@@ -230,7 +245,10 @@ const KT_MENU_RADIO_GROUP = new InjectionToken('KT_MENU_RADIO_GROUP');
|
|
|
230
245
|
* ```
|
|
231
246
|
*/
|
|
232
247
|
class KtMenuRadioGroup {
|
|
233
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* Valeur sélectionnée du groupe, bidirectionnelle : `[(value)]`.
|
|
250
|
+
* @default null
|
|
251
|
+
*/
|
|
234
252
|
value = model(null, /* @ts-ignore */
|
|
235
253
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
236
254
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtMenuRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -257,7 +275,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
257
275
|
class KtMenuItemRadio {
|
|
258
276
|
ariaItem = inject(MenuItem, { self: true, optional: true });
|
|
259
277
|
group = inject(KT_MENU_RADIO_GROUP, { optional: true });
|
|
260
|
-
|
|
278
|
+
host = inject(ElementRef).nativeElement;
|
|
279
|
+
/** Valeur portée par ce radio (comparée à celle du groupe). @default (requis) */
|
|
261
280
|
value = input.required(/* @ts-ignore */
|
|
262
281
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
263
282
|
checked = computed(() => this.group?.value() === this.value(), /* @ts-ignore */
|
|
@@ -268,6 +287,12 @@ class KtMenuItemRadio {
|
|
|
268
287
|
console.warn('[ktMenuItemRadio] doit être placé dans un [ktMenuRadioGroup] : ' +
|
|
269
288
|
'sans groupe, la sélection et `aria-checked` restent inopérants.');
|
|
270
289
|
}
|
|
290
|
+
// Garde-fou dev : sans `role="menuitemradio"`, `aria-checked` est ignoré → item muet pour les SR.
|
|
291
|
+
afterNextRender(() => {
|
|
292
|
+
if (!isDevMode() || this.host.getAttribute('role') === 'menuitemradio')
|
|
293
|
+
return;
|
|
294
|
+
console.warn('[ktMenuItemRadio] attend `role="menuitemradio"` sur l’hôte, sinon `aria-checked` est inopérant.');
|
|
295
|
+
});
|
|
271
296
|
}
|
|
272
297
|
activate(event) {
|
|
273
298
|
if (this.ariaItem?.disabled())
|
|
@@ -293,11 +318,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
293
318
|
|
|
294
319
|
/**
|
|
295
320
|
* Import ergonomique de toute la famille menu (couche de THÈME ktortu) en une fois :
|
|
296
|
-
* `imports: [
|
|
321
|
+
* `imports: [KtMenuImports]` au lieu d'énumérer chaque directive. À composer avec les directives
|
|
297
322
|
* d'`@angular/aria/menu` (`Menu`, `MenuItem`, `MenuTrigger`, `MenuContent`), qui apportent le
|
|
298
323
|
* comportement accessible.
|
|
299
324
|
*/
|
|
300
|
-
const
|
|
325
|
+
const KtMenuImports = [
|
|
301
326
|
KtMenu,
|
|
302
327
|
KtMenuItem,
|
|
303
328
|
KtMenuSeparator,
|
|
@@ -311,5 +336,5 @@ const KT_MENU = [
|
|
|
311
336
|
* Generated bundle index. Do not edit.
|
|
312
337
|
*/
|
|
313
338
|
|
|
314
|
-
export {
|
|
339
|
+
export { KT_MENU_RADIO_GROUP, KtMenu, KtMenuImports, KtMenuItem, KtMenuItemCheckbox, KtMenuItemRadio, KtMenuRadioGroup, KtMenuSeparator, KtMenuTrigger };
|
|
315
340
|
//# sourceMappingURL=ktortu-aaa-menu.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ktortu-aaa-menu.mjs","sources":["../../../../projects/ktortu/aaa/menu/menu.ts","../../../../projects/ktortu/aaa/menu/menu-trigger.ts","../../../../projects/ktortu/aaa/menu/menu-toggle.ts","../../../../projects/ktortu/aaa/menu/public-api.ts","../../../../projects/ktortu/aaa/menu/ktortu-aaa-menu.ts"],"sourcesContent":["import { Directive, ElementRef, effect, inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MenuItem as AriaMenuItem } from '@angular/aria/menu';\nimport { KtIdGenerator } from '@ktortu/aaa/cdk';\n\n/**\n * `[ktMenu]` — marqueur de THÈME posé sur l'hôte `[ngMenu]` d'`@angular/aria`. Style UNIQUEMENT la\n * SURFACE du menu (fond, bord, ombre, rayon, padding, positionnement en dropdown ancré). N'ajoute\n * AUCUN rôle ni comportement : la sémantique (`role=\"menu\"`), le clavier, le focus roving, le\n * retour de focus au trigger, les sous-menus et le typeahead viennent intégralement d'aria\n * (`[ngMenu]`). Même philosophie que `[ktCard]` : la lib thématise, aria gère l'accessibilité.\n *\n * La directive n'a pas de logique propre : tout vit dans `menu.css` via le sélecteur `[ktMenu]`,\n * piloté par l'attribut `data-visible` qu'aria pose sur l'hôte (ouvert/fermé). Le positionnement\n * (CSS Anchor Positioning) est câblé par `[ktMenuTrigger]` (menu racine) et par `[ktMenuItem]`\n * (sous-menus) qui posent les `anchor-name` / `position-anchor`.\n *\n * @example\n * ```html\n * <button ngMenuTrigger ktMenuTrigger [menu]=\"m\">Options</button>\n * <div ngMenu ktMenu #m=\"ngMenu\" (itemSelected)=\"onSelect($event)\" aria-label=\"Options\">\n * <button ngMenuItem ktMenuItem value=\"new\">Nouveau</button>\n * </div>\n * ```\n */\n@Directive({ selector: '[ktMenu]' })\nexport class KtMenu {}\n\n/**\n * `[ktMenuItem]` — marqueur de thème posé sur l'hôte `[ngMenuItem]`. Style la RANGÉE (hauteur de\n * cible tactile ≥44px, focus, survol, désactivé, item de sous-menu avec chevron). Comme `[ktMenu]`,\n * sans logique de sélection : l'état stylé est lu sur les attributs ARIA déjà posés par aria\n * (`:focus-visible` pour l'item actif via focus roving, `[aria-disabled]`, `[aria-expanded]`,\n * `[aria-haspopup]`, `[aria-checked]`).\n *\n * SEULE responsabilité technique : si l'item porte un sous-menu (`[submenu]` côté aria), on câble\n * l'ancrage CSS du sous-menu sur cet item (anchor-name sur l'item, position-anchor sur la surface\n * du sous-menu, marqueur `data-kt-submenu` pour l'ouvrir en latéral plutôt qu'en dropdown).\n *\n * EXIGENCE DE STRUCTURE (sous-menus) : le `[ngMenu]` d'un sous-menu DOIT être un DESCENDANT DOM du\n * menu parent (et non un frère). aria décide de maintenir un menu ouvert via `element.contains()`\n * sur la cible du focus/survol ; un sous-menu placé hors du sous-arbre du parent ferait croire à\n * aria que le focus a quitté le menu et provoquerait sa fermeture dès qu'on survole le sous-menu.\n * Le sous-menu reste rendu en place (top-layer non requis : `position: fixed` + ancrage CSS).\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem value=\"open\">Ouvrir</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItem]',\n host: {\n '[style.anchor-name]': 'anchorName',\n },\n})\nexport class KtMenuItem {\n private readonly host = inject(ElementRef).nativeElement;\n // Optionnel : `[ktMenuItem]` reste utilisable seul (simple thème) même hors d'un `[ngMenuItem]`.\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n private readonly platformId = inject(PLATFORM_ID);\n\n // Alloué à la PREMIÈRE détection d'un sous-menu (et non pour chaque item) : un item simple ne\n // consomme pas d'anchor-name.\n protected anchorName: string | undefined;\n private readonly idGen = inject(KtIdGenerator);\n\n constructor() {\n if (!this.ariaItem) return;\n\n effect(() => {\n const submenu = this.ariaItem!.submenu()?.element;\n if (!submenu) {\n this.anchorName = undefined;\n return;\n }\n this.anchorName ??= `--kt-submenu-anchor-${this.idGen.generateId()}`;\n if (isPlatformBrowser(this.platformId)) {\n submenu.style.setProperty('position-anchor', this.anchorName);\n // Marque la surface comme un SOUS-menu : menu.css l'ouvre en latéral (inline-end) et non\n // sous l'item, avec ses propres fallbacks de débordement.\n submenu.setAttribute('data-kt-submenu', '');\n }\n });\n }\n}\n\n/**\n * `[ktMenuSeparator]` — filet de séparation entre groupes d'items. Pose `role=\"separator\"`\n * (sémantique standard d'un séparateur de menu) ; la ligne elle-même vit dans `menu.css`.\n *\n * @example\n * ```html\n * <hr ktMenuSeparator />\n * ```\n */\n@Directive({\n selector: '[ktMenuSeparator]',\n host: { role: 'separator' },\n})\nexport class KtMenuSeparator {}\n","import { Directive, ElementRef, effect, inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MenuTrigger as AriaMenuTrigger } from '@angular/aria/menu';\nimport { KtIdGenerator } from '@ktortu/aaa/cdk';\n\n/**\n * `[ktMenuTrigger]` — sucre de POSITIONNEMENT pour le menu racine. À poser SUR le même élément\n * qu'`[ngMenuTrigger]` (le bouton qui ouvre le menu). Il ne réimplémente RIEN du comportement :\n * l'ouverture/fermeture, `aria-expanded`, Échap, le clic-dehors et le retour de focus sont gérés\n * par aria (`[ngMenuTrigger]`). Sa seule tâche : poser un `anchor-name` sur le trigger et le\n * `position-anchor` correspondant sur la surface du menu, pour que `menu.css` l'ancre en dropdown\n * (CSS Anchor Positioning), comme le popup du Select — sans CDK Overlay (styles scopés conservés,\n * et pas de coordination fragile avec le focus qu'aria pose à l'ouverture).\n *\n * Requiert `[ngMenuTrigger]` sur le même hôte (injecté en `self`).\n *\n * @example\n * ```html\n * <button ngMenuTrigger ktMenuTrigger [menu]=\"m\">Fichier</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuTrigger]',\n host: {\n '[style.anchor-name]': 'anchorName',\n },\n})\nexport class KtMenuTrigger {\n private readonly host = inject(ElementRef).nativeElement;\n private readonly ariaTrigger = inject(AriaMenuTrigger, { self: true });\n private readonly platformId = inject(PLATFORM_ID);\n\n private readonly idGen = inject(KtIdGenerator);\n protected readonly anchorName = `--kt-menu-anchor-${this.idGen.generateId()}`;\n\n constructor() {\n effect(() => {\n const menuEl = this.ariaTrigger.menu()?.element;\n if (!menuEl) return;\n if (isPlatformBrowser(this.platformId)) {\n menuEl.style.setProperty('position-anchor', this.anchorName);\n }\n });\n }\n}\n","import { Directive, InjectionToken, computed, inject, input, isDevMode, model } from '@angular/core';\nimport { MenuItem as AriaMenuItem } from '@angular/aria/menu';\n\n/**\n * Items de menu À ÉTAT (checkbox / radio).\n *\n * Pourquoi ces directives existent : `@angular/aria` accepte bien `role=\"menuitemcheckbox\"` /\n * `\"menuitemradio\"` sur `[ngMenuItem]`, MAIS NE POSE JAMAIS `aria-checked` — son pattern se\n * contente d'émettre `itemSelected(value)`. Sans `aria-checked`, l'item coché est MUET pour les\n * lecteurs d'écran (échec WCAG 4.1.2). Ces directives comblent ce trou : elles possèdent l'état\n * coché, le bindent en `aria-checked`, et le basculent à l'activation (clic / Entrée / Espace).\n *\n * Elles ne touchent pas au `role` (laissé à l'input `role` d'aria, posé par le consommateur) pour\n * éviter tout conflit de binding sur `[attr.role]` ; la démo montre l'usage `role=\"menuitemradio\"`.\n */\n\n/** Activation commune : un item de menu se déclenche au clic ET au clavier (Entrée / Espace). */\nconst ACTIVATION_HOST = {\n '(click)': 'activate($event)',\n '(keydown.enter)': 'activate($event)',\n '(keydown.space)': 'activate($event)',\n} as const;\n\n/**\n * `[ktMenuItemCheckbox]` — case à cocher de menu (bascule indépendante). À poser sur un\n * `[ngMenuItem] role=\"menuitemcheckbox\"`. Bind `aria-checked` et bascule `checked` à l'activation.\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem ktMenuItemCheckbox role=\"menuitemcheckbox\" [(checked)]=\"wrap\">\n * Retour à la ligne\n * </button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItemCheckbox]',\n host: {\n ...ACTIVATION_HOST,\n '[attr.aria-checked]': 'checked()',\n },\n})\nexport class KtMenuItemCheckbox {\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n\n /** État coché, bidirectionnel : `[(checked)]`. */\n readonly checked = model<boolean>(false);\n\n protected activate(event: Event): void {\n if (this.ariaItem?.disabled()) return;\n if (event.type === 'keydown') {\n event.preventDefault();\n }\n this.checked.update((v) => !v);\n }\n}\n\n/** Source de la valeur sélectionnée d'un groupe radio, exposée aux `[ktMenuItemRadio]` enfants. */\nexport const KT_MENU_RADIO_GROUP = new InjectionToken<KtMenuRadioGroup<unknown>>('KT_MENU_RADIO_GROUP');\n\n/**\n * `[ktMenuRadioGroup]` — coordinateur d'un groupe d'items radio mutuellement exclusifs. À poser sur\n * un conteneur englobant les `[ktMenuItemRadio]` (typiquement un `role=\"group\"`). Détient la valeur\n * sélectionnée ; chaque radio s'y compare pour son `aria-checked` et la met à jour à l'activation.\n *\n * @example\n * ```html\n * <div role=\"group\" ktMenuRadioGroup [(value)]=\"sortBy\" aria-label=\"Trier par\">\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'name'\">Nom</button>\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'date'\">Date</button>\n * </div>\n * ```\n */\n@Directive({\n selector: '[ktMenuRadioGroup]',\n exportAs: 'ktMenuRadioGroup',\n providers: [{ provide: KT_MENU_RADIO_GROUP, useExisting: KtMenuRadioGroup }],\n})\nexport class KtMenuRadioGroup<V> {\n /** Valeur sélectionnée du groupe, bidirectionnelle : `[(value)]`. */\n readonly value = model<V | null>(null);\n}\n\n/**\n * `[ktMenuItemRadio]` — bouton radio de menu. À poser sur un `[ngMenuItem] role=\"menuitemradio\"`\n * dans un `[ktMenuRadioGroup]`. `aria-checked` reflète l'égalité avec la valeur du groupe ;\n * l'activation sélectionne cette valeur.\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'name'\">Trier par nom</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItemRadio]',\n host: {\n ...ACTIVATION_HOST,\n '[attr.aria-checked]': 'checked()',\n },\n})\nexport class KtMenuItemRadio<V> {\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n private readonly group = inject<KtMenuRadioGroup<V>>(KT_MENU_RADIO_GROUP, { optional: true });\n\n /** Valeur portée par ce radio (comparée à celle du groupe). */\n readonly value = input.required<V>();\n\n protected readonly checked = computed(() => this.group?.value() === this.value());\n\n constructor() {\n // Garde-fou dev : sans [ktMenuRadioGroup] parent, aria-checked reste figé (item muet pour les SR).\n if (isDevMode() && !this.group) {\n console.warn(\n '[ktMenuItemRadio] doit être placé dans un [ktMenuRadioGroup] : ' +\n 'sans groupe, la sélection et `aria-checked` restent inopérants.',\n );\n }\n }\n\n protected activate(event: Event): void {\n if (this.ariaItem?.disabled()) return;\n if (event.type === 'keydown') {\n event.preventDefault();\n }\n this.group?.value.set(this.value());\n }\n}\n","import { KtMenu, KtMenuItem, KtMenuSeparator } from './menu';\nimport { KtMenuTrigger } from './menu-trigger';\nimport { KtMenuItemCheckbox, KtMenuItemRadio, KtMenuRadioGroup } from './menu-toggle';\n\nexport * from './menu';\nexport * from './menu-trigger';\nexport * from './menu-toggle';\n\n/**\n * Import ergonomique de toute la famille menu (couche de THÈME ktortu) en une fois :\n * `imports: [KT_MENU]` au lieu d'énumérer chaque directive. À composer avec les directives\n * d'`@angular/aria/menu` (`Menu`, `MenuItem`, `MenuTrigger`, `MenuContent`), qui apportent le\n * comportement accessible.\n */\nexport const KT_MENU = [\n KtMenu,\n KtMenuItem,\n KtMenuSeparator,\n KtMenuTrigger,\n KtMenuItemCheckbox,\n KtMenuItemRadio,\n KtMenuRadioGroup,\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["AriaMenuItem","AriaMenuTrigger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ktortu-aaa-menu.mjs","sources":["../../../../projects/ktortu/aaa/menu/menu.ts","../../../../projects/ktortu/aaa/menu/menu-trigger.ts","../../../../projects/ktortu/aaa/menu/menu-toggle.ts","../../../../projects/ktortu/aaa/menu/public-api.ts","../../../../projects/ktortu/aaa/menu/ktortu-aaa-menu.ts"],"sourcesContent":["import { Directive, effect, inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MenuItem as AriaMenuItem } from '@angular/aria/menu';\nimport { KtIdGenerator } from '@ktortu/aaa/cdk';\n\n/**\n * `[ktMenu]` — marqueur de THÈME posé sur l'hôte `[ngMenu]` d'`@angular/aria`. Style UNIQUEMENT la\n * SURFACE du menu (fond, bord, ombre, rayon, padding, positionnement en dropdown ancré). N'ajoute\n * AUCUN rôle ni comportement : la sémantique (`role=\"menu\"`), le clavier, le focus roving, le\n * retour de focus au trigger, les sous-menus et le typeahead viennent intégralement d'aria\n * (`[ngMenu]`). Même philosophie que `[ktCard]` : la lib thématise, aria gère l'accessibilité.\n *\n * La directive n'a pas de logique propre : tout vit dans `menu.css` via le sélecteur `[ktMenu]`,\n * piloté par l'attribut `data-visible` qu'aria pose sur l'hôte (ouvert/fermé). Le positionnement\n * (CSS Anchor Positioning) est câblé par `[ktMenuTrigger]` (menu racine) et par `[ktMenuItem]`\n * (sous-menus) qui posent les `anchor-name` / `position-anchor`.\n *\n * @example\n * ```html\n * <button ngMenuTrigger ktMenuTrigger [menu]=\"m\">Options</button>\n * <div ngMenu ktMenu #m=\"ngMenu\" (itemSelected)=\"onSelect($event)\" aria-label=\"Options\">\n * <button ngMenuItem ktMenuItem value=\"new\">Nouveau</button>\n * </div>\n * ```\n */\n@Directive({ selector: '[ktMenu]' })\nexport class KtMenu {}\n\n/**\n * `[ktMenuItem]` — marqueur de thème posé sur l'hôte `[ngMenuItem]`. Style la RANGÉE (hauteur de\n * cible tactile ≥44px, focus, survol, désactivé, item de sous-menu avec chevron). Comme `[ktMenu]`,\n * sans logique de sélection : l'état stylé est lu sur les attributs ARIA déjà posés par aria\n * (`:focus-visible` pour l'item actif via focus roving, `[aria-disabled]`, `[aria-expanded]`,\n * `[aria-haspopup]`, `[aria-checked]`).\n *\n * SEULE responsabilité technique : si l'item porte un sous-menu (`[submenu]` côté aria), on câble\n * l'ancrage CSS du sous-menu sur cet item (anchor-name sur l'item, position-anchor sur la surface\n * du sous-menu, marqueur `data-kt-submenu` pour l'ouvrir en latéral plutôt qu'en dropdown).\n *\n * EXIGENCE DE STRUCTURE (sous-menus) : le `[ngMenu]` d'un sous-menu DOIT être un DESCENDANT DOM du\n * menu parent (et non un frère). aria décide de maintenir un menu ouvert via `element.contains()`\n * sur la cible du focus/survol ; un sous-menu placé hors du sous-arbre du parent ferait croire à\n * aria que le focus a quitté le menu et provoquerait sa fermeture dès qu'on survole le sous-menu.\n * Le sous-menu reste rendu en place (top-layer non requis : `position: fixed` + ancrage CSS).\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem value=\"open\">Ouvrir</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItem]',\n host: {\n '[style.anchor-name]': 'anchorName',\n },\n})\nexport class KtMenuItem {\n // Optionnel : `[ktMenuItem]` reste utilisable seul (simple thème) même hors d'un `[ngMenuItem]`.\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n private readonly platformId = inject(PLATFORM_ID);\n\n // Alloué à la PREMIÈRE détection d'un sous-menu (et non pour chaque item) : un item simple ne\n // consomme pas d'anchor-name.\n protected anchorName: string | undefined;\n private readonly idGen = inject(KtIdGenerator);\n\n constructor() {\n if (!this.ariaItem) return;\n\n effect(() => {\n const submenu = this.ariaItem!.submenu()?.element;\n if (!submenu) {\n this.anchorName = undefined;\n return;\n }\n this.anchorName ??= `--kt-submenu-anchor-${this.idGen.generateId('menu')}`;\n if (isPlatformBrowser(this.platformId)) {\n submenu.style.setProperty('position-anchor', this.anchorName);\n // Marque la surface comme un SOUS-menu : menu.css l'ouvre en latéral (inline-end) et non\n // sous l'item, avec ses propres fallbacks de débordement.\n submenu.setAttribute('data-kt-submenu', '');\n }\n });\n }\n}\n\n/**\n * `[ktMenuSeparator]` — filet de séparation entre groupes d'items. Pose `role=\"separator\"`\n * (sémantique standard d'un séparateur de menu) ; la ligne elle-même vit dans `menu.css`.\n *\n * @example\n * ```html\n * <hr ktMenuSeparator />\n * ```\n */\n@Directive({\n selector: '[ktMenuSeparator]',\n host: { role: 'separator' },\n})\nexport class KtMenuSeparator {}\n","import { Directive, effect, inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { MenuTrigger as AriaMenuTrigger } from '@angular/aria/menu';\nimport { KtIdGenerator } from '@ktortu/aaa/cdk';\n\n/**\n * `[ktMenuTrigger]` — sucre de POSITIONNEMENT pour le menu racine. À poser SUR le même élément\n * qu'`[ngMenuTrigger]` (le bouton qui ouvre le menu). Il ne réimplémente RIEN du comportement :\n * l'ouverture/fermeture, `aria-expanded`, Échap, le clic-dehors et le retour de focus sont gérés\n * par aria (`[ngMenuTrigger]`). Sa seule tâche : poser un `anchor-name` sur le trigger et le\n * `position-anchor` correspondant sur la surface du menu, pour que `menu.css` l'ancre en dropdown\n * (CSS Anchor Positioning), comme le popup du Select — sans CDK Overlay (styles scopés conservés,\n * et pas de coordination fragile avec le focus qu'aria pose à l'ouverture).\n *\n * Requiert `[ngMenuTrigger]` sur le même hôte (injecté en `self`).\n *\n * @example\n * ```html\n * <button ngMenuTrigger ktMenuTrigger [menu]=\"m\">Fichier</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuTrigger]',\n host: {\n '[style.anchor-name]': 'anchorName',\n },\n})\nexport class KtMenuTrigger {\n private readonly ariaTrigger = inject(AriaMenuTrigger, { self: true });\n private readonly platformId = inject(PLATFORM_ID);\n\n private readonly idGen = inject(KtIdGenerator);\n protected readonly anchorName = `--kt-menu-anchor-${this.idGen.generateId('menu')}`;\n\n constructor() {\n effect(() => {\n const menuEl = this.ariaTrigger.menu()?.element;\n if (!menuEl) return;\n if (isPlatformBrowser(this.platformId)) {\n menuEl.style.setProperty('position-anchor', this.anchorName);\n }\n });\n }\n}\n","import { Directive, ElementRef, afterNextRender, computed, inject, input, InjectionToken, isDevMode, model } from '@angular/core';\nimport { MenuItem as AriaMenuItem } from '@angular/aria/menu';\n\n/**\n * Items de menu À ÉTAT (checkbox / radio).\n *\n * Pourquoi ces directives existent : `@angular/aria` accepte bien `role=\"menuitemcheckbox\"` /\n * `\"menuitemradio\"` sur `[ngMenuItem]`, MAIS NE POSE JAMAIS `aria-checked` — son pattern se\n * contente d'émettre `itemSelected(value)`. Sans `aria-checked`, l'item coché est MUET pour les\n * lecteurs d'écran (échec WCAG 4.1.2). Ces directives comblent ce trou : elles possèdent l'état\n * coché, le bindent en `aria-checked`, et le basculent à l'activation (clic / Entrée / Espace).\n *\n * Elles ne touchent pas au `role` (laissé à l'input `role` d'aria, posé par le consommateur) pour\n * éviter tout conflit de binding sur `[attr.role]` ; la démo montre l'usage `role=\"menuitemradio\"`.\n */\n\n/**\n * Activation commune : clic ET clavier (Entrée / Espace). Les deux liaisons coexistent VOLONTAIREMENT\n * pour supporter un hôte non-`<button>` (ex. `<div ngMenuItem>`), où le clavier n'émet pas de `click`\n * natif. Sur un `<button>`, `activate()` appelle `event.preventDefault()` sur le `keydown`, ce qui\n * neutralise le `click` synthétique → une seule bascule (pas de double-toggle). Couvert en E2E.\n */\nconst ACTIVATION_HOST = {\n '(click)': 'activate($event)',\n '(keydown.enter)': 'activate($event)',\n '(keydown.space)': 'activate($event)',\n} as const;\n\n/**\n * `[ktMenuItemCheckbox]` — case à cocher de menu (bascule indépendante). À poser sur un\n * `[ngMenuItem] role=\"menuitemcheckbox\"`. Bind `aria-checked` et bascule `checked` à l'activation.\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem ktMenuItemCheckbox role=\"menuitemcheckbox\" [(checked)]=\"wrap\">\n * Retour à la ligne\n * </button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItemCheckbox]',\n host: {\n ...ACTIVATION_HOST,\n '[attr.aria-checked]': 'checked()',\n },\n})\nexport class KtMenuItemCheckbox {\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /**\n * État coché, bidirectionnel : `[(checked)]`.\n * @default false\n */\n readonly checked = model<boolean>(false);\n\n constructor() {\n // Garde-fou dev : sans `role=\"menuitemcheckbox\"`, `aria-checked` est ignoré → item muet pour les SR.\n afterNextRender(() => {\n if (!isDevMode() || this.host.getAttribute('role') === 'menuitemcheckbox') return;\n console.warn('[ktMenuItemCheckbox] attend `role=\"menuitemcheckbox\"` sur l’hôte, sinon `aria-checked` est inopérant.');\n });\n }\n\n protected activate(event: Event): void {\n if (this.ariaItem?.disabled()) return;\n if (event.type === 'keydown') {\n event.preventDefault();\n }\n this.checked.update((v) => !v);\n }\n}\n\n/** Source de la valeur sélectionnée d'un groupe radio, exposée aux `[ktMenuItemRadio]` enfants. */\nexport const KT_MENU_RADIO_GROUP = new InjectionToken<KtMenuRadioGroup<unknown>>('KT_MENU_RADIO_GROUP');\n\n/**\n * `[ktMenuRadioGroup]` — coordinateur d'un groupe d'items radio mutuellement exclusifs. À poser sur\n * un conteneur englobant les `[ktMenuItemRadio]` (typiquement un `role=\"group\"`). Détient la valeur\n * sélectionnée ; chaque radio s'y compare pour son `aria-checked` et la met à jour à l'activation.\n *\n * @example\n * ```html\n * <div role=\"group\" ktMenuRadioGroup [(value)]=\"sortBy\" aria-label=\"Trier par\">\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'name'\">Nom</button>\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'date'\">Date</button>\n * </div>\n * ```\n */\n@Directive({\n selector: '[ktMenuRadioGroup]',\n exportAs: 'ktMenuRadioGroup',\n providers: [{ provide: KT_MENU_RADIO_GROUP, useExisting: KtMenuRadioGroup }],\n})\nexport class KtMenuRadioGroup<V> {\n /**\n * Valeur sélectionnée du groupe, bidirectionnelle : `[(value)]`.\n * @default null\n */\n readonly value = model<V | null>(null);\n}\n\n/**\n * `[ktMenuItemRadio]` — bouton radio de menu. À poser sur un `[ngMenuItem] role=\"menuitemradio\"`\n * dans un `[ktMenuRadioGroup]`. `aria-checked` reflète l'égalité avec la valeur du groupe ;\n * l'activation sélectionne cette valeur.\n *\n * @example\n * ```html\n * <button ngMenuItem ktMenuItem ktMenuItemRadio role=\"menuitemradio\" [value]=\"'name'\">Trier par nom</button>\n * ```\n */\n@Directive({\n selector: '[ktMenuItemRadio]',\n host: {\n ...ACTIVATION_HOST,\n '[attr.aria-checked]': 'checked()',\n },\n})\nexport class KtMenuItemRadio<V> {\n private readonly ariaItem = inject(AriaMenuItem, { self: true, optional: true });\n private readonly group = inject<KtMenuRadioGroup<V>>(KT_MENU_RADIO_GROUP, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** Valeur portée par ce radio (comparée à celle du groupe). @default (requis) */\n readonly value = input.required<V>();\n\n protected readonly checked = computed(() => this.group?.value() === this.value());\n\n constructor() {\n // Garde-fou dev : sans [ktMenuRadioGroup] parent, aria-checked reste figé (item muet pour les SR).\n if (isDevMode() && !this.group) {\n console.warn(\n '[ktMenuItemRadio] doit être placé dans un [ktMenuRadioGroup] : ' +\n 'sans groupe, la sélection et `aria-checked` restent inopérants.',\n );\n }\n // Garde-fou dev : sans `role=\"menuitemradio\"`, `aria-checked` est ignoré → item muet pour les SR.\n afterNextRender(() => {\n if (!isDevMode() || this.host.getAttribute('role') === 'menuitemradio') return;\n console.warn('[ktMenuItemRadio] attend `role=\"menuitemradio\"` sur l’hôte, sinon `aria-checked` est inopérant.');\n });\n }\n\n protected activate(event: Event): void {\n if (this.ariaItem?.disabled()) return;\n if (event.type === 'keydown') {\n event.preventDefault();\n }\n this.group?.value.set(this.value());\n }\n}\n","import { KtMenu, KtMenuItem, KtMenuSeparator } from './menu';\nimport { KtMenuTrigger } from './menu-trigger';\nimport { KtMenuItemCheckbox, KtMenuItemRadio, KtMenuRadioGroup } from './menu-toggle';\n\nexport * from './menu';\nexport * from './menu-trigger';\nexport * from './menu-toggle';\n\n/**\n * Import ergonomique de toute la famille menu (couche de THÈME ktortu) en une fois :\n * `imports: [KtMenuImports]` au lieu d'énumérer chaque directive. À composer avec les directives\n * d'`@angular/aria/menu` (`Menu`, `MenuItem`, `MenuTrigger`, `MenuContent`), qui apportent le\n * comportement accessible.\n */\nexport const KtMenuImports = [\n KtMenu,\n KtMenuItem,\n KtMenuSeparator,\n KtMenuTrigger,\n KtMenuItemCheckbox,\n KtMenuItemRadio,\n KtMenuRadioGroup,\n] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["AriaMenuItem","AriaMenuTrigger"],"mappings":"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"}
|