@ktortu/aaa 0.9.9 → 0.10.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 +40 -3
- package/dialog/dialog.css +85 -0
- package/fesm2022/ktortu-aaa-dialog.mjs +172 -13
- package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-i18n.mjs +7 -0
- package/fesm2022/ktortu-aaa-i18n.mjs.map +1 -1
- package/fesm2022/ktortu-aaa-icon.mjs +174 -0
- package/fesm2022/ktortu-aaa-icon.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-layout.mjs +12 -2
- package/fesm2022/ktortu-aaa-layout.mjs.map +1 -1
- package/fesm2022/ktortu-aaa.mjs +1 -0
- package/fesm2022/ktortu-aaa.mjs.map +1 -1
- package/icon/icon.css +52 -0
- package/package.json +5 -1
- package/styles/icon.css +7 -0
- package/styles/styles.css +1 -0
- package/types/ktortu-aaa-dialog.d.ts +92 -7
- package/types/ktortu-aaa-i18n.d.ts +3 -0
- package/types/ktortu-aaa-icon.d.ts +158 -0
- package/types/ktortu-aaa-layout.d.ts +11 -1
- package/types/ktortu-aaa.d.ts +1 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, PLATFORM_ID, ElementRef, input, booleanAttribute, computed, effect, Directive } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
4
|
+
import { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';
|
|
5
|
+
|
|
6
|
+
const KT_ICON_CONFIG = new InjectionToken('KT_ICON_CONFIG');
|
|
7
|
+
/**
|
|
8
|
+
* Fournit un registre de polices d'icônes (alias → famille) et l'alias par défaut, pour un
|
|
9
|
+
* sous-arbre ou l'application entière. Combiné à l'augmentation de {@link KtIconFontRegistry}, il
|
|
10
|
+
* offre l'autocomplétion typée de l'input `[font]`.
|
|
11
|
+
*
|
|
12
|
+
* `defaultFont` est **contraint aux clés de `fonts`** passées dans le même appel : un typo devient
|
|
13
|
+
* une erreur de compilation (pas de repli silencieux de toutes les icônes sur la police globale).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* providers: [
|
|
18
|
+
* provideKtIcon({
|
|
19
|
+
* fonts: {
|
|
20
|
+
* material: { family: 'Material Symbols Outlined' },
|
|
21
|
+
* rounded: { family: 'Material Symbols Rounded' },
|
|
22
|
+
* },
|
|
23
|
+
* defaultFont: 'material', // 'rouded' → erreur TS
|
|
24
|
+
* }),
|
|
25
|
+
* ];
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function provideKtIcon(config) {
|
|
29
|
+
return { provide: KT_ICON_CONFIG, useValue: config };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Primitive d'icône **autonome** : pose une icône seule (en-tête, item de liste, badge d'état…),
|
|
33
|
+
* là où le bouton et les champs ne suffisent pas. Directive présentationnelle — **jamais focusable
|
|
34
|
+
* ni cliquable** (pour une icône cliquable, utilisez `ktButton iconOnly`).
|
|
35
|
+
*
|
|
36
|
+
* Sélecteur d'attribut `[ktIcon]` (camelCase) ; l'hôte reçoit la classe stable `.kt-icon` (cible du
|
|
37
|
+
* style et du harness). Ne pas confondre avec le slot de projection `[kt-icon]` (kebab) de
|
|
38
|
+
* `kt-nav-item`, qui est un autre mécanisme.
|
|
39
|
+
*
|
|
40
|
+
* Deux régimes, comme dans le reste de la lib :
|
|
41
|
+
* - **Ligature** (`[ktIcon]="nom"`) : le nom devient un glyphe via une police d'icônes à ligatures
|
|
42
|
+
* (défaut `--kt-icon-font` → Material Symbols Outlined). Choisissez une autre police enregistrée
|
|
43
|
+
* via `[font]`.
|
|
44
|
+
* - **Projection** (set à classes, ex. Font Awesome, ou SVG en ligne) : laissez `ktIcon` vide et
|
|
45
|
+
* projetez votre markup dans l'élément ; la primitive ne fait que l'habiller (taille, couleur
|
|
46
|
+
* héritée, alignement). Le contenu projeté **ne doit jamais être focusable** (l'hôte décoratif
|
|
47
|
+
* porte `aria-hidden` : un descendant focusable violerait WCAG 4.1.2).
|
|
48
|
+
*
|
|
49
|
+
* Accessibilité : **décorative par défaut** (`aria-hidden`). Fournissez `[ariaLabel]` (ou un
|
|
50
|
+
* `aria-label` natif) pour une icône **porteuse de sens** (bascule en `role="img"`).
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```html
|
|
54
|
+
* <!-- Ligature autonome, taille et couleur héritées du contexte ; `fill` = version remplie -->
|
|
55
|
+
* <span ktIcon="home"></span>
|
|
56
|
+
* <span ktIcon="favorite" fill size="2rem" style="color: var(--kt-primary)"></span>
|
|
57
|
+
*
|
|
58
|
+
* <!-- Set à classes (Font Awesome) : projection, ktIcon reste vide -->
|
|
59
|
+
* <span ktIcon><i class="fa-solid fa-user"></i></span>
|
|
60
|
+
*
|
|
61
|
+
* <!-- Icône porteuse de sens (nommée pour les lecteurs d'écran) -->
|
|
62
|
+
* <span ktIcon="error" ariaLabel="Erreur" style="color: var(--kt-danger)"></span>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
class KtIcon {
|
|
66
|
+
config = inject(KT_ICON_CONFIG, { optional: true });
|
|
67
|
+
auditEnabled = inject(KT_AUDIT_ENABLED);
|
|
68
|
+
platformId = inject(PLATFORM_ID);
|
|
69
|
+
host = inject(ElementRef).nativeElement;
|
|
70
|
+
// aria-label natif posé par le consommateur, capté à la construction (préservé si l'input est absent).
|
|
71
|
+
nativeAriaLabel = this.host.getAttribute('aria-label');
|
|
72
|
+
/** Nom de l'icône (ligature). Laisser vide pour projeter un set à classes / un SVG. @default '' */
|
|
73
|
+
ktIcon = input('', /* @ts-ignore */
|
|
74
|
+
...(ngDevMode ? [{ debugName: "ktIcon" }] : /* istanbul ignore next */ []));
|
|
75
|
+
/** Alias de police d'icônes du registre (cf. {@link provideKtIcon}). @default `KtIconConfig.defaultFont` */
|
|
76
|
+
font = input(/* @ts-ignore */
|
|
77
|
+
...(ngDevMode ? [undefined, { debugName: "font" }] : /* istanbul ignore next */ []));
|
|
78
|
+
/** Nom accessible : le fournir rend l'icône **porteuse de sens** (`role="img"`). Aligné sur `ktButton`.
|
|
79
|
+
@default undefined (icône décorative ; un `aria-label` natif reste toutefois préservé) */
|
|
80
|
+
ariaLabel = input(/* @ts-ignore */
|
|
81
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
82
|
+
/** Taille de l'icône (longueur CSS, ex. `'24px'`, `'2rem'`, `'1.5em'`). @default `1.25em` (héritée du contexte) */
|
|
83
|
+
size = input(/* @ts-ignore */
|
|
84
|
+
...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
|
|
85
|
+
/** Version **remplie** plutôt que contour, pour les polices variables à axe `FILL` (Material Symbols).
|
|
86
|
+
Force `FILL` à 1 en préservant les autres axes (graisse…) ; sans effet sur une police sans cet axe.
|
|
87
|
+
Nécessite une police chargée **avec l'axe FILL ajustable** (ex. Google Fonts :
|
|
88
|
+
`…Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200`).
|
|
89
|
+
@default false (contour) */
|
|
90
|
+
fill = input(false, { ...(ngDevMode ? { debugName: "fill" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
91
|
+
/** Nom rendu en ligature : coupé, `null` si vide (aucun `::before`, laisse place à la projection). */
|
|
92
|
+
renderedName = computed(() => this.ktIcon()?.trim() || null, /* @ts-ignore */
|
|
93
|
+
...(ngDevMode ? [{ debugName: "renderedName" }] : /* istanbul ignore next */ []));
|
|
94
|
+
/** Libellé accessible résolu : input prioritaire, sinon `aria-label` natif préservé, sinon `null` (décorative). */
|
|
95
|
+
accessibleLabel = computed(() => this.ariaLabel()?.trim() || this.nativeAriaLabel || null, /* @ts-ignore */
|
|
96
|
+
...(ngDevMode ? [{ debugName: "accessibleLabel" }] : /* istanbul ignore next */ []));
|
|
97
|
+
/** Police résolue depuis le registre : alias explicite, sinon police par défaut du provider. */
|
|
98
|
+
resolvedFont = computed(() => {
|
|
99
|
+
const alias = this.font() ?? this.config?.defaultFont;
|
|
100
|
+
if (!alias)
|
|
101
|
+
return undefined;
|
|
102
|
+
return this.config?.fonts?.[alias];
|
|
103
|
+
}, /* @ts-ignore */
|
|
104
|
+
...(ngDevMode ? [{ debugName: "resolvedFont" }] : /* istanbul ignore next */ []));
|
|
105
|
+
resolvedFamily = computed(() => this.resolvedFont()?.family ?? null, /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "resolvedFamily" }] : /* istanbul ignore next */ []));
|
|
107
|
+
resolvedWeight = computed(() => {
|
|
108
|
+
const weight = this.resolvedFont()?.weight;
|
|
109
|
+
return weight == null ? null : String(weight);
|
|
110
|
+
}, /* @ts-ignore */
|
|
111
|
+
...(ngDevMode ? [{ debugName: "resolvedWeight" }] : /* istanbul ignore next */ []));
|
|
112
|
+
resolvedVariation = computed(() => {
|
|
113
|
+
const base = this.resolvedFont()?.variationSettings ?? null;
|
|
114
|
+
if (!this.fill())
|
|
115
|
+
return base;
|
|
116
|
+
// Rempli : on force l'axe FILL à 1. En cas d'axe dupliqué, la DERNIÈRE valeur prime (spec CSS
|
|
117
|
+
// Fonts) → on préserve les autres axes de la police du registre (graisse, GRAD, opsz…).
|
|
118
|
+
// ADR [fill] cross-navigateur : la spec CSS Fonts module 4 stipule que si un axe est
|
|
119
|
+
// dupliqué dans font-variation-settings, la dernière déclaration l'emporte.
|
|
120
|
+
// Cela fonctionne parfaitement sur Chromium. WebKit/Firefox pourraient théoriquement avoir
|
|
121
|
+
// des bugs historiques (bien que non constatés), le risque est assumé (dégradation = contour).
|
|
122
|
+
return base ? `${base}, 'FILL' 1` : `'FILL' 1`;
|
|
123
|
+
}, /* @ts-ignore */
|
|
124
|
+
...(ngDevMode ? [{ debugName: "resolvedVariation" }] : /* istanbul ignore next */ []));
|
|
125
|
+
constructor() {
|
|
126
|
+
// Garde-fou dev : un alias absent du registre (input [font] OU defaultFont) retombe silencieusement
|
|
127
|
+
// sur --kt-icon-font. On résout l'alias effectif pour aussi couvrir un defaultFont mal orthographié.
|
|
128
|
+
effect(() => {
|
|
129
|
+
if (!this.auditEnabled || !isPlatformBrowser(this.platformId))
|
|
130
|
+
return;
|
|
131
|
+
const alias = this.font() ?? this.config?.defaultFont;
|
|
132
|
+
if (alias && !this.config?.fonts?.[alias]) {
|
|
133
|
+
console.warn(`[ktIcon] police "${String(alias)}" absente du registre : ` +
|
|
134
|
+
'enregistrez-la via provideKtIcon({ fonts }) — repli sur --kt-icon-font.');
|
|
135
|
+
}
|
|
136
|
+
// Garde-fou dev opt-in projection focusable
|
|
137
|
+
if (!this.accessibleLabel()) {
|
|
138
|
+
const focusable = this.host.querySelector('a[href],button,input,select,textarea,[tabindex]:not([tabindex="-1"]),[contenteditable="true"]');
|
|
139
|
+
if (focusable) {
|
|
140
|
+
console.warn('[ktIcon] (WCAG 4.1.2) : icône décorative (aria-hidden) avec un enfant projeté focusable. Ajoutez ariaLabel ou retirez le focus.');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
146
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: KtIcon, isStandalone: true, selector: "[ktIcon]", inputs: { ktIcon: { classPropertyName: "ktIcon", publicName: "ktIcon", isSignal: true, isRequired: false, transformFunction: null }, font: { classPropertyName: "font", publicName: "font", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-icon": "renderedName()", "style.--kt-icon-font": "resolvedFamily()", "style.--kt-icon-font-weight": "resolvedWeight()", "style.--kt-icon-font-variation": "resolvedVariation()", "style.--kt-icon-size": "size() || null", "attr.role": "accessibleLabel() ? \"img\" : null", "attr.aria-label": "accessibleLabel()", "attr.aria-hidden": "accessibleLabel() ? null : \"true\"" }, classAttribute: "kt-icon" }, ngImport: i0 });
|
|
147
|
+
}
|
|
148
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtIcon, decorators: [{
|
|
149
|
+
type: Directive,
|
|
150
|
+
args: [{
|
|
151
|
+
selector: '[ktIcon]',
|
|
152
|
+
host: {
|
|
153
|
+
// Marqueur stable posé quel que soit le mode d'usage (`ktIcon="x"` OU `[ktIcon]="x"`) : un binding
|
|
154
|
+
// de propriété ne reflète AUCUN attribut dans le DOM, donc le CSS/harness ne peuvent pas cibler
|
|
155
|
+
// `[ktIcon]`. La classe, elle, est toujours présente → cible du style et du harness.
|
|
156
|
+
class: 'kt-icon',
|
|
157
|
+
'[attr.data-icon]': 'renderedName()',
|
|
158
|
+
'[style.--kt-icon-font]': 'resolvedFamily()',
|
|
159
|
+
'[style.--kt-icon-font-weight]': 'resolvedWeight()',
|
|
160
|
+
'[style.--kt-icon-font-variation]': 'resolvedVariation()',
|
|
161
|
+
'[style.--kt-icon-size]': 'size() || null',
|
|
162
|
+
'[attr.role]': 'accessibleLabel() ? "img" : null',
|
|
163
|
+
'[attr.aria-label]': 'accessibleLabel()',
|
|
164
|
+
'[attr.aria-hidden]': 'accessibleLabel() ? null : "true"',
|
|
165
|
+
},
|
|
166
|
+
}]
|
|
167
|
+
}], ctorParameters: () => [], propDecorators: { ktIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "ktIcon", required: false }] }], font: [{ type: i0.Input, args: [{ isSignal: true, alias: "font", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }] } });
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Generated bundle index. Do not edit.
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
export { KT_ICON_CONFIG, KtIcon, provideKtIcon };
|
|
174
|
+
//# sourceMappingURL=ktortu-aaa-icon.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktortu-aaa-icon.mjs","sources":["../../../../projects/ktortu/aaa/icon/icon.ts","../../../../projects/ktortu/aaa/icon/ktortu-aaa-icon.ts"],"sourcesContent":["import {\n Directive,\n ElementRef,\n InjectionToken,\n PLATFORM_ID,\n Provider,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { KT_AUDIT_ENABLED } from '@ktortu/aaa/cdk';\n\n/**\n * Définition d'une police d'icônes **à ligatures** enregistrée dans le registre (cf.\n * {@link KtIconConfig}). Le registre ne charge AUCUNE police : il se contente d'associer un alias\n * court à la famille CSS réelle (à vous de charger le fichier de police, façon « apportez la vôtre »).\n */\nexport interface KtIconFont {\n /** Famille de police CSS réelle (ex. `'Material Symbols Rounded'`). Valeur `font-family` brute :\n si elle commence par un chiffre ou contient des caractères spéciaux, la citer vous-même. */\n family: string;\n /** Graisse par défaut de cette police (ex. `300` ou `'normal'`). @default hérité de `--kt-icon-font-weight` */\n weight?: string | number;\n /** `font-variation-settings` par défaut (polices variables, ex. `\"'FILL' 0, 'wght' 300\"`). @default `normal` */\n variationSettings?: string;\n}\n\n/**\n * Défauts applicables à toutes les icônes `[ktIcon]` (registre de polices + police par défaut),\n * fournis via {@link provideKtIcon}. Fourni en `Partial` : on ne déclare que ce qu'on veut.\n */\nexport interface KtIconConfig {\n /** Registre `alias → police`. L'alias est ensuite choisi sur la primitive via `[font]`. */\n fonts: Record<string, KtIconFont>;\n /** Alias utilisé quand aucune police n'est précisée sur la primitive (`[font]` absent). */\n defaultFont: string;\n}\n\n/**\n * Interface d'**enregistrement des alias de POLICE** à augmenter côté application pour typer l'input\n * `[font]` et obtenir son **autocomplétion** dans les templates. Vide par défaut : sans augmentation,\n * `[font]` accepte n'importe quelle chaîne (dégradation gracieuse, zéro config).\n *\n * Nommée `KtIconFontRegistry` (et non `KtIconRegistry`) à dessein : le nom générique reste réservé à\n * un futur registre de **noms d'icônes** (SVG), qui bénéficiera du même mécanisme.\n *\n * @example Recette à source unique (le même objet type ET fournit le registre) :\n * ```ts\n * // app-icons.ts\n * export const APP_ICON_FONTS = {\n * material: { family: 'Material Symbols Outlined' },\n * rounded: { family: 'Material Symbols Rounded', variationSettings: \"'FILL' 0, 'wght' 300\" },\n * } as const;\n *\n * declare module '@ktortu/aaa/icon' {\n * // Injecte les clés de APP_ICON_FONTS comme alias connus → autocomplétion de [font].\n * // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n * interface KtIconFontRegistry extends Record<keyof typeof APP_ICON_FONTS, unknown> {}\n * }\n *\n * // app.config.ts\n * providers: [provideKtIcon({ fonts: APP_ICON_FONTS, defaultFont: 'material' })];\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- point d'extension à augmenter côté app.\nexport interface KtIconFontRegistry {}\n\n/**\n * Alias de police connus : l'union des clés de {@link KtIconFontRegistry} une fois augmentée, sinon\n * `string` (registre vide → chaîne libre). Le tuple `[…] extends […]` évite la distribution du\n * conditionnel sur l'union.\n */\nexport type KtIconFontAlias = [keyof KtIconFontRegistry] extends [never] ? string : keyof KtIconFontRegistry;\n\nexport const KT_ICON_CONFIG = new InjectionToken<Partial<KtIconConfig>>('KT_ICON_CONFIG');\n\n/**\n * Fournit un registre de polices d'icônes (alias → famille) et l'alias par défaut, pour un\n * sous-arbre ou l'application entière. Combiné à l'augmentation de {@link KtIconFontRegistry}, il\n * offre l'autocomplétion typée de l'input `[font]`.\n *\n * `defaultFont` est **contraint aux clés de `fonts`** passées dans le même appel : un typo devient\n * une erreur de compilation (pas de repli silencieux de toutes les icônes sur la police globale).\n *\n * @example\n * ```ts\n * providers: [\n * provideKtIcon({\n * fonts: {\n * material: { family: 'Material Symbols Outlined' },\n * rounded: { family: 'Material Symbols Rounded' },\n * },\n * defaultFont: 'material', // 'rouded' → erreur TS\n * }),\n * ];\n * ```\n */\nexport function provideKtIcon<F extends Record<string, KtIconFont>>(config: {\n fonts?: F;\n defaultFont?: keyof F & string;\n}): Provider {\n return { provide: KT_ICON_CONFIG, useValue: config };\n}\n\n/**\n * Primitive d'icône **autonome** : pose une icône seule (en-tête, item de liste, badge d'état…),\n * là où le bouton et les champs ne suffisent pas. Directive présentationnelle — **jamais focusable\n * ni cliquable** (pour une icône cliquable, utilisez `ktButton iconOnly`).\n *\n * Sélecteur d'attribut `[ktIcon]` (camelCase) ; l'hôte reçoit la classe stable `.kt-icon` (cible du\n * style et du harness). Ne pas confondre avec le slot de projection `[kt-icon]` (kebab) de\n * `kt-nav-item`, qui est un autre mécanisme.\n *\n * Deux régimes, comme dans le reste de la lib :\n * - **Ligature** (`[ktIcon]=\"nom\"`) : le nom devient un glyphe via une police d'icônes à ligatures\n * (défaut `--kt-icon-font` → Material Symbols Outlined). Choisissez une autre police enregistrée\n * via `[font]`.\n * - **Projection** (set à classes, ex. Font Awesome, ou SVG en ligne) : laissez `ktIcon` vide et\n * projetez votre markup dans l'élément ; la primitive ne fait que l'habiller (taille, couleur\n * héritée, alignement). Le contenu projeté **ne doit jamais être focusable** (l'hôte décoratif\n * porte `aria-hidden` : un descendant focusable violerait WCAG 4.1.2).\n *\n * Accessibilité : **décorative par défaut** (`aria-hidden`). Fournissez `[ariaLabel]` (ou un\n * `aria-label` natif) pour une icône **porteuse de sens** (bascule en `role=\"img\"`).\n *\n * @example\n * ```html\n * <!-- Ligature autonome, taille et couleur héritées du contexte ; `fill` = version remplie -->\n * <span ktIcon=\"home\"></span>\n * <span ktIcon=\"favorite\" fill size=\"2rem\" style=\"color: var(--kt-primary)\"></span>\n *\n * <!-- Set à classes (Font Awesome) : projection, ktIcon reste vide -->\n * <span ktIcon><i class=\"fa-solid fa-user\"></i></span>\n *\n * <!-- Icône porteuse de sens (nommée pour les lecteurs d'écran) -->\n * <span ktIcon=\"error\" ariaLabel=\"Erreur\" style=\"color: var(--kt-danger)\"></span>\n * ```\n */\n@Directive({\n selector: '[ktIcon]',\n host: {\n // Marqueur stable posé quel que soit le mode d'usage (`ktIcon=\"x\"` OU `[ktIcon]=\"x\"`) : un binding\n // de propriété ne reflète AUCUN attribut dans le DOM, donc le CSS/harness ne peuvent pas cibler\n // `[ktIcon]`. La classe, elle, est toujours présente → cible du style et du harness.\n class: 'kt-icon',\n '[attr.data-icon]': 'renderedName()',\n '[style.--kt-icon-font]': 'resolvedFamily()',\n '[style.--kt-icon-font-weight]': 'resolvedWeight()',\n '[style.--kt-icon-font-variation]': 'resolvedVariation()',\n '[style.--kt-icon-size]': 'size() || null',\n '[attr.role]': 'accessibleLabel() ? \"img\" : null',\n '[attr.aria-label]': 'accessibleLabel()',\n '[attr.aria-hidden]': 'accessibleLabel() ? null : \"true\"',\n },\n})\nexport class KtIcon {\n private readonly config = inject(KT_ICON_CONFIG, { optional: true });\n private readonly auditEnabled = inject(KT_AUDIT_ENABLED);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n // aria-label natif posé par le consommateur, capté à la construction (préservé si l'input est absent).\n private readonly nativeAriaLabel = this.host.getAttribute('aria-label');\n\n /** Nom de l'icône (ligature). Laisser vide pour projeter un set à classes / un SVG. @default '' */\n readonly ktIcon = input<string>('');\n\n /** Alias de police d'icônes du registre (cf. {@link provideKtIcon}). @default `KtIconConfig.defaultFont` */\n readonly font = input<KtIconFontAlias>();\n\n /** Nom accessible : le fournir rend l'icône **porteuse de sens** (`role=\"img\"`). Aligné sur `ktButton`.\n @default undefined (icône décorative ; un `aria-label` natif reste toutefois préservé) */\n readonly ariaLabel = input<string>();\n\n /** Taille de l'icône (longueur CSS, ex. `'24px'`, `'2rem'`, `'1.5em'`). @default `1.25em` (héritée du contexte) */\n readonly size = input<string>();\n\n /** Version **remplie** plutôt que contour, pour les polices variables à axe `FILL` (Material Symbols).\n Force `FILL` à 1 en préservant les autres axes (graisse…) ; sans effet sur une police sans cet axe.\n Nécessite une police chargée **avec l'axe FILL ajustable** (ex. Google Fonts :\n `…Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200`).\n @default false (contour) */\n readonly fill = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Nom rendu en ligature : coupé, `null` si vide (aucun `::before`, laisse place à la projection). */\n protected readonly renderedName = computed(() => this.ktIcon()?.trim() || null);\n\n /** Libellé accessible résolu : input prioritaire, sinon `aria-label` natif préservé, sinon `null` (décorative). */\n protected readonly accessibleLabel = computed(() => this.ariaLabel()?.trim() || this.nativeAriaLabel || null);\n\n /** Police résolue depuis le registre : alias explicite, sinon police par défaut du provider. */\n private readonly resolvedFont = computed<KtIconFont | undefined>(() => {\n const alias = this.font() ?? this.config?.defaultFont;\n if (!alias) return undefined;\n return this.config?.fonts?.[alias as string];\n });\n\n protected readonly resolvedFamily = computed(() => this.resolvedFont()?.family ?? null);\n protected readonly resolvedWeight = computed(() => {\n const weight = this.resolvedFont()?.weight;\n return weight == null ? null : String(weight);\n });\n protected readonly resolvedVariation = computed(() => {\n const base = this.resolvedFont()?.variationSettings ?? null;\n if (!this.fill()) return base;\n // Rempli : on force l'axe FILL à 1. En cas d'axe dupliqué, la DERNIÈRE valeur prime (spec CSS\n // Fonts) → on préserve les autres axes de la police du registre (graisse, GRAD, opsz…).\n // ADR [fill] cross-navigateur : la spec CSS Fonts module 4 stipule que si un axe est\n // dupliqué dans font-variation-settings, la dernière déclaration l'emporte.\n // Cela fonctionne parfaitement sur Chromium. WebKit/Firefox pourraient théoriquement avoir\n // des bugs historiques (bien que non constatés), le risque est assumé (dégradation = contour).\n return base ? `${base}, 'FILL' 1` : `'FILL' 1`;\n });\n\n constructor() {\n // Garde-fou dev : un alias absent du registre (input [font] OU defaultFont) retombe silencieusement\n // sur --kt-icon-font. On résout l'alias effectif pour aussi couvrir un defaultFont mal orthographié.\n effect(() => {\n if (!this.auditEnabled || !isPlatformBrowser(this.platformId)) return;\n const alias = this.font() ?? this.config?.defaultFont;\n if (alias && !this.config?.fonts?.[alias as string]) {\n console.warn(\n `[ktIcon] police \"${String(alias)}\" absente du registre : ` +\n 'enregistrez-la via provideKtIcon({ fonts }) — repli sur --kt-icon-font.',\n );\n }\n\n // Garde-fou dev opt-in projection focusable\n if (!this.accessibleLabel()) {\n const focusable = this.host.querySelector(\n 'a[href],button,input,select,textarea,[tabindex]:not([tabindex=\"-1\"]),[contenteditable=\"true\"]',\n );\n if (focusable) {\n console.warn(\n '[ktIcon] (WCAG 4.1.2) : icône décorative (aria-hidden) avec un enfant projeté focusable. Ajoutez ariaLabel ou retirez le focus.',\n );\n }\n }\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -199,6 +199,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
199
199
|
}]
|
|
200
200
|
}] });
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Item de navigation : projette une icône dans son slot dédié + un libellé.
|
|
204
|
+
*
|
|
205
|
+
* Le slot d'icône accepte :
|
|
206
|
+
* - la directive `[ktIcon]` (primitive d'icône `@ktortu/aaa/icon`), forme canonique.
|
|
207
|
+
* - l'attribut de projection `kt-icon` (historique/legacy).
|
|
208
|
+
*
|
|
209
|
+
* Note : la classe `.kt-icon` (posée à l'exécution par la directive) n'est PAS un critère de
|
|
210
|
+
* projection valable (ng-content matche la forme statique du template) — d'où le sélecteur d'attribut.
|
|
211
|
+
*/
|
|
202
212
|
class KtNavItemComponent {
|
|
203
213
|
label = input.required(/* @ts-ignore */
|
|
204
214
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
@@ -206,7 +216,7 @@ class KtNavItemComponent {
|
|
|
206
216
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtNavItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
207
217
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.1", type: KtNavItemComponent, isStandalone: true, selector: "kt-nav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "kt-nav-item" }, ngImport: i0, template: `
|
|
208
218
|
<div class="kt-nav-item-icon">
|
|
209
|
-
<ng-content select="[kt-icon]"></ng-content>
|
|
219
|
+
<ng-content select="[ktIcon], [kt-icon]"></ng-content>
|
|
210
220
|
</div>
|
|
211
221
|
<div class="kt-nav-item-label">
|
|
212
222
|
{{ label() }}
|
|
@@ -225,7 +235,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
225
235
|
},
|
|
226
236
|
template: `
|
|
227
237
|
<div class="kt-nav-item-icon">
|
|
228
|
-
<ng-content select="[kt-icon]"></ng-content>
|
|
238
|
+
<ng-content select="[ktIcon], [kt-icon]"></ng-content>
|
|
229
239
|
</div>
|
|
230
240
|
<div class="kt-nav-item-label">
|
|
231
241
|
{{ label() }}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ktortu-aaa-layout.mjs","sources":["../../../../projects/ktortu/aaa/layout/layout.service.ts","../../../../projects/ktortu/aaa/layout/layout.component.ts","../../../../projects/ktortu/aaa/layout/sidenav.component.ts","../../../../projects/ktortu/aaa/layout/toolbar.component.ts","../../../../projects/ktortu/aaa/layout/nav-item.component.ts","../../../../projects/ktortu/aaa/layout/sidenav-toggle.directive.ts","../../../../projects/ktortu/aaa/layout/ktortu-aaa-layout.ts"],"sourcesContent":["import { Injectable, computed, inject, InjectionToken, signal } from '@angular/core';\nimport { KtViewport } from '@ktortu/aaa/cdk';\n\n/** Mode d'état de la navigation latérale. */\nexport type KtSidenavState = 'expanded' | 'rail' | 'hidden';\n\nexport const KT_LAYOUT_DEFAULT_STATE = new InjectionToken<KtSidenavState>('KT_LAYOUT_DEFAULT_STATE', {\n providedIn: 'root',\n factory: () => 'expanded',\n});\n\n/**\n * Service global de gestion de la mise en page (Layout).\n * Gère l'état d'ouverture/fermeture de la barre latérale sur mobile et bureau,\n * ainsi que le comportement spécifique de \"rail\" sur bureau.\n *\n * NOTE: Ce service n'est pas \"providedIn: 'root'\". Le développeur doit le fournir\n * (via le tableau providers d'un composant parent ou de l'application) pour\n * maîtriser le scope (ex: avoir plusieurs layouts indépendants).\n */\n@Injectable()\nexport class KtLayoutService {\n private readonly viewport = inject(KtViewport);\n private readonly defaultState = inject(KT_LAYOUT_DEFAULT_STATE);\n\n /**\n * Comportement actif lors de la fermeture sur ordinateur (écran non mobile).\n * Renseigné généralement par le composant racine `KtLayoutComponent`.\n */\n readonly desktopCloseBehavior = signal<'hidden' | 'rail'>('hidden');\n\n private readonly _isDesktopOpen = signal<boolean>(this.defaultState !== 'rail' && this.defaultState !== 'hidden');\n private readonly _isMobileOpen = signal<boolean>(false);\n\n readonly isMobile = this.viewport.isMobile;\n readonly isRail = computed(\n () => !this.isMobile() && !this._isDesktopOpen() && this.desktopCloseBehavior() === 'rail',\n );\n readonly isExpanded = computed(() => !this.isMobile() && this._isDesktopOpen());\n readonly isHidden = computed(\n () =>\n (this.isMobile() && !this._isMobileOpen()) ||\n (!this.isMobile() && !this._isDesktopOpen() && this.desktopCloseBehavior() === 'hidden'),\n );\n readonly isMobileOpen = computed(() => this.isMobile() && this._isMobileOpen());\n\n toggle() {\n if (this.isMobile()) {\n this._isMobileOpen.update((v) => !v);\n } else {\n this._isDesktopOpen.update((v) => !v);\n }\n }\n\n open() {\n if (this.isMobile()) {\n this._isMobileOpen.set(true);\n } else {\n this._isDesktopOpen.set(true);\n }\n }\n\n close() {\n if (this.isMobile()) {\n this._isMobileOpen.set(false);\n } else {\n this._isDesktopOpen.set(false);\n }\n }\n}\n","import { Component, inject, effect, input } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Composant racine de l'application qui gère la structure de base (Sidebar, Toolbar, Contenu).\n * Configure dynamiquement le comportement du layout.\n */\n@Component({\n selector: 'kt-layout',\n standalone: true,\n host: {\n class: 'kt-layout-container',\n },\n template: `\n <ng-content select=\"kt-sidenav\"></ng-content>\n <div class=\"kt-layout-main-wrapper\">\n <ng-content select=\"kt-toolbar\"></ng-content>\n <main class=\"kt-layout-main\" [attr.aria-hidden]=\"layout.isMobileOpen()\">\n <ng-content></ng-content>\n </main>\n </div>\n <div\n class=\"kt-layout-backdrop\"\n [class.is-visible]=\"layout.isMobileOpen()\"\n aria-hidden=\"true\"\n (click)=\"layout.close()\"\n ></div>\n `,\n})\nexport class KtLayoutComponent {\n /**\n * Définit le comportement de la navigation latérale lors de sa fermeture sur bureau (écran large).\n * - `'hidden'` (défaut) : le menu est complètement masqué et retiré du flux visuel/a11y.\n * - `'rail'` : le menu est réduit à une fine barre verticale (mini-sidebar), permettant\n * d'afficher uniquement les icônes. Idéal si les liens possèdent des icônes explicites.\n */\n readonly desktopCloseBehavior = input<'hidden' | 'rail'>('hidden');\n protected readonly layout = inject(KtLayoutService);\n\n constructor() {\n effect(\n () => {\n this.layout.desktopCloseBehavior.set(this.desktopCloseBehavior());\n },\n { allowSignalWrites: true },\n );\n\n effect(\n () => {\n // Clean up mobile state when switching back to desktop\n if (!this.layout.isMobile() && this.layout.isMobileOpen()) {\n this.layout.close();\n }\n },\n { allowSignalWrites: true },\n );\n }\n}\n","import { Component, inject, computed, effect } from '@angular/core';\nimport { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Composant représentant le panneau latéral de navigation.\n * Se comporte comme un tiroir (drawer) superposé sur mobile, et comme un\n * menu latéral persistant (réductible ou masquable) sur ordinateur.\n */\n@Component({\n selector: 'kt-sidenav',\n standalone: true,\n imports: [CdkTrapFocus],\n host: {\n class: 'kt-sidenav',\n '[class.is-hidden]': 'isHidden()',\n '[class.is-rail]': 'layout.isRail()',\n '[class.is-mobile-open]': 'layout.isMobileOpen()',\n '[attr.aria-expanded]': 'layout.isExpanded()',\n '[attr.aria-hidden]': 'isHidden()',\n role: 'navigation',\n },\n hostDirectives: [CdkTrapFocus],\n template: `<ng-content></ng-content>`,\n})\nexport class KtSidenavComponent {\n protected readonly layout = inject(KtLayoutService);\n protected readonly isHidden = computed(() => this.layout.isHidden());\n private readonly trapFocus = inject(CdkTrapFocus);\n\n constructor() {\n effect(() => {\n this.trapFocus.autoCapture = this.layout.isMobileOpen();\n this.trapFocus.enabled = this.layout.isMobileOpen();\n });\n }\n}\n","import { Component } from '@angular/core';\n\n/**\n * Barre d'outils supérieure (Toolbar / Top app bar).\n * Se place généralement dans un `kt-layout`.\n */\n@Component({\n selector: 'kt-toolbar',\n standalone: true,\n host: {\n class: 'kt-toolbar',\n role: 'banner',\n },\n template: `<ng-content></ng-content>`,\n})\nexport class KtToolbarComponent {}\n","import { Component, input, inject } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n@Component({\n selector: 'kt-nav-item',\n standalone: true,\n host: {\n class: 'kt-nav-item',\n },\n template: `\n <div class=\"kt-nav-item-icon\">\n <ng-content select=\"[kt-icon]\"></ng-content>\n </div>\n <div class=\"kt-nav-item-label\">\n {{ label() }}\n <ng-content></ng-content>\n </div>\n <!-- TODO: Add Tooltip component injection here conditionally based on layout.isRail() -->\n `,\n})\nexport class KtNavItemComponent {\n label = input.required<string>();\n protected readonly layout = inject(KtLayoutService);\n}\n","import { Directive, inject } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Directive applicable sur n'importe quel élément (souvent un bouton) pour basculer\n * l'état d'ouverture du panneau latéral (Sidenav).\n *\n * Expose l'état du layout pour des utilisations dynamiques dans le template\n * (ex: changer l'icône selon l'état).\n *\n * @example\n * ```html\n * <button ktSidenavToggle #btn=\"ktSidenavToggle\" [icon]=\"btn.isRail() ? 'right' : 'left'\">\n * ```\n */\n@Directive({\n selector: '[ktSidenavToggle]',\n exportAs: 'ktSidenavToggle',\n standalone: true,\n host: {\n '(click)': 'onClick()',\n },\n})\nexport class KtSidenavToggleDirective {\n private readonly layout = inject(KtLayoutService);\n\n /** Indique si le menu est actuellement réduit en mode rail. */\n readonly isRail = this.layout.isRail;\n\n /** Indique si le menu est actuellement complètement étendu sur bureau. */\n readonly isExpanded = this.layout.isExpanded;\n\n /** Indique si le menu est totalement masqué. */\n readonly isHidden = this.layout.isHidden;\n\n /** Indique si le tiroir est actuellement ouvert par-dessus le contenu (mobile). */\n readonly isMobileOpen = this.layout.isMobileOpen;\n\n /** Indique si l'écran actuel est de taille compacte (mobile). */\n readonly isMobile = this.layout.isMobile;\n\n onClick() {\n this.layout.toggle();\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-layout.mjs","sources":["../../../../projects/ktortu/aaa/layout/layout.service.ts","../../../../projects/ktortu/aaa/layout/layout.component.ts","../../../../projects/ktortu/aaa/layout/sidenav.component.ts","../../../../projects/ktortu/aaa/layout/toolbar.component.ts","../../../../projects/ktortu/aaa/layout/nav-item.component.ts","../../../../projects/ktortu/aaa/layout/sidenav-toggle.directive.ts","../../../../projects/ktortu/aaa/layout/ktortu-aaa-layout.ts"],"sourcesContent":["import { Injectable, computed, inject, InjectionToken, signal } from '@angular/core';\nimport { KtViewport } from '@ktortu/aaa/cdk';\n\n/** Mode d'état de la navigation latérale. */\nexport type KtSidenavState = 'expanded' | 'rail' | 'hidden';\n\nexport const KT_LAYOUT_DEFAULT_STATE = new InjectionToken<KtSidenavState>('KT_LAYOUT_DEFAULT_STATE', {\n providedIn: 'root',\n factory: () => 'expanded',\n});\n\n/**\n * Service global de gestion de la mise en page (Layout).\n * Gère l'état d'ouverture/fermeture de la barre latérale sur mobile et bureau,\n * ainsi que le comportement spécifique de \"rail\" sur bureau.\n *\n * NOTE: Ce service n'est pas \"providedIn: 'root'\". Le développeur doit le fournir\n * (via le tableau providers d'un composant parent ou de l'application) pour\n * maîtriser le scope (ex: avoir plusieurs layouts indépendants).\n */\n@Injectable()\nexport class KtLayoutService {\n private readonly viewport = inject(KtViewport);\n private readonly defaultState = inject(KT_LAYOUT_DEFAULT_STATE);\n\n /**\n * Comportement actif lors de la fermeture sur ordinateur (écran non mobile).\n * Renseigné généralement par le composant racine `KtLayoutComponent`.\n */\n readonly desktopCloseBehavior = signal<'hidden' | 'rail'>('hidden');\n\n private readonly _isDesktopOpen = signal<boolean>(this.defaultState !== 'rail' && this.defaultState !== 'hidden');\n private readonly _isMobileOpen = signal<boolean>(false);\n\n readonly isMobile = this.viewport.isMobile;\n readonly isRail = computed(\n () => !this.isMobile() && !this._isDesktopOpen() && this.desktopCloseBehavior() === 'rail',\n );\n readonly isExpanded = computed(() => !this.isMobile() && this._isDesktopOpen());\n readonly isHidden = computed(\n () =>\n (this.isMobile() && !this._isMobileOpen()) ||\n (!this.isMobile() && !this._isDesktopOpen() && this.desktopCloseBehavior() === 'hidden'),\n );\n readonly isMobileOpen = computed(() => this.isMobile() && this._isMobileOpen());\n\n toggle() {\n if (this.isMobile()) {\n this._isMobileOpen.update((v) => !v);\n } else {\n this._isDesktopOpen.update((v) => !v);\n }\n }\n\n open() {\n if (this.isMobile()) {\n this._isMobileOpen.set(true);\n } else {\n this._isDesktopOpen.set(true);\n }\n }\n\n close() {\n if (this.isMobile()) {\n this._isMobileOpen.set(false);\n } else {\n this._isDesktopOpen.set(false);\n }\n }\n}\n","import { Component, inject, effect, input } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Composant racine de l'application qui gère la structure de base (Sidebar, Toolbar, Contenu).\n * Configure dynamiquement le comportement du layout.\n */\n@Component({\n selector: 'kt-layout',\n standalone: true,\n host: {\n class: 'kt-layout-container',\n },\n template: `\n <ng-content select=\"kt-sidenav\"></ng-content>\n <div class=\"kt-layout-main-wrapper\">\n <ng-content select=\"kt-toolbar\"></ng-content>\n <main class=\"kt-layout-main\" [attr.aria-hidden]=\"layout.isMobileOpen()\">\n <ng-content></ng-content>\n </main>\n </div>\n <div\n class=\"kt-layout-backdrop\"\n [class.is-visible]=\"layout.isMobileOpen()\"\n aria-hidden=\"true\"\n (click)=\"layout.close()\"\n ></div>\n `,\n})\nexport class KtLayoutComponent {\n /**\n * Définit le comportement de la navigation latérale lors de sa fermeture sur bureau (écran large).\n * - `'hidden'` (défaut) : le menu est complètement masqué et retiré du flux visuel/a11y.\n * - `'rail'` : le menu est réduit à une fine barre verticale (mini-sidebar), permettant\n * d'afficher uniquement les icônes. Idéal si les liens possèdent des icônes explicites.\n */\n readonly desktopCloseBehavior = input<'hidden' | 'rail'>('hidden');\n protected readonly layout = inject(KtLayoutService);\n\n constructor() {\n effect(\n () => {\n this.layout.desktopCloseBehavior.set(this.desktopCloseBehavior());\n },\n { allowSignalWrites: true },\n );\n\n effect(\n () => {\n // Clean up mobile state when switching back to desktop\n if (!this.layout.isMobile() && this.layout.isMobileOpen()) {\n this.layout.close();\n }\n },\n { allowSignalWrites: true },\n );\n }\n}\n","import { Component, inject, computed, effect } from '@angular/core';\nimport { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Composant représentant le panneau latéral de navigation.\n * Se comporte comme un tiroir (drawer) superposé sur mobile, et comme un\n * menu latéral persistant (réductible ou masquable) sur ordinateur.\n */\n@Component({\n selector: 'kt-sidenav',\n standalone: true,\n imports: [CdkTrapFocus],\n host: {\n class: 'kt-sidenav',\n '[class.is-hidden]': 'isHidden()',\n '[class.is-rail]': 'layout.isRail()',\n '[class.is-mobile-open]': 'layout.isMobileOpen()',\n '[attr.aria-expanded]': 'layout.isExpanded()',\n '[attr.aria-hidden]': 'isHidden()',\n role: 'navigation',\n },\n hostDirectives: [CdkTrapFocus],\n template: `<ng-content></ng-content>`,\n})\nexport class KtSidenavComponent {\n protected readonly layout = inject(KtLayoutService);\n protected readonly isHidden = computed(() => this.layout.isHidden());\n private readonly trapFocus = inject(CdkTrapFocus);\n\n constructor() {\n effect(() => {\n this.trapFocus.autoCapture = this.layout.isMobileOpen();\n this.trapFocus.enabled = this.layout.isMobileOpen();\n });\n }\n}\n","import { Component } from '@angular/core';\n\n/**\n * Barre d'outils supérieure (Toolbar / Top app bar).\n * Se place généralement dans un `kt-layout`.\n */\n@Component({\n selector: 'kt-toolbar',\n standalone: true,\n host: {\n class: 'kt-toolbar',\n role: 'banner',\n },\n template: `<ng-content></ng-content>`,\n})\nexport class KtToolbarComponent {}\n","import { Component, input, inject } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Item de navigation : projette une icône dans son slot dédié + un libellé.\n *\n * Le slot d'icône accepte :\n * - la directive `[ktIcon]` (primitive d'icône `@ktortu/aaa/icon`), forme canonique.\n * - l'attribut de projection `kt-icon` (historique/legacy).\n *\n * Note : la classe `.kt-icon` (posée à l'exécution par la directive) n'est PAS un critère de\n * projection valable (ng-content matche la forme statique du template) — d'où le sélecteur d'attribut.\n */\n@Component({\n selector: 'kt-nav-item',\n standalone: true,\n host: {\n class: 'kt-nav-item',\n },\n template: `\n <div class=\"kt-nav-item-icon\">\n <ng-content select=\"[ktIcon], [kt-icon]\"></ng-content>\n </div>\n <div class=\"kt-nav-item-label\">\n {{ label() }}\n <ng-content></ng-content>\n </div>\n <!-- TODO: Add Tooltip component injection here conditionally based on layout.isRail() -->\n `,\n})\nexport class KtNavItemComponent {\n label = input.required<string>();\n protected readonly layout = inject(KtLayoutService);\n}\n","import { Directive, inject } from '@angular/core';\nimport { KtLayoutService } from './layout.service';\n\n/**\n * Directive applicable sur n'importe quel élément (souvent un bouton) pour basculer\n * l'état d'ouverture du panneau latéral (Sidenav).\n *\n * Expose l'état du layout pour des utilisations dynamiques dans le template\n * (ex: changer l'icône selon l'état).\n *\n * @example\n * ```html\n * <button ktSidenavToggle #btn=\"ktSidenavToggle\" [icon]=\"btn.isRail() ? 'right' : 'left'\">\n * ```\n */\n@Directive({\n selector: '[ktSidenavToggle]',\n exportAs: 'ktSidenavToggle',\n standalone: true,\n host: {\n '(click)': 'onClick()',\n },\n})\nexport class KtSidenavToggleDirective {\n private readonly layout = inject(KtLayoutService);\n\n /** Indique si le menu est actuellement réduit en mode rail. */\n readonly isRail = this.layout.isRail;\n\n /** Indique si le menu est actuellement complètement étendu sur bureau. */\n readonly isExpanded = this.layout.isExpanded;\n\n /** Indique si le menu est totalement masqué. */\n readonly isHidden = this.layout.isHidden;\n\n /** Indique si le tiroir est actuellement ouvert par-dessus le contenu (mobile). */\n readonly isMobileOpen = this.layout.isMobileOpen;\n\n /** Indique si l'écran actuel est de taille compacte (mobile). */\n readonly isMobile = this.layout.isMobile;\n\n onClick() {\n this.layout.toggle();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;MAMa,uBAAuB,GAAG,IAAI,cAAc,CAAiB,yBAAyB,EAAE;AACnG,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,UAAU;AAC1B,CAAA;AAED;;;;;;;;AAQG;MAEU,eAAe,CAAA;AACT,IAAA,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;AAC7B,IAAA,YAAY,GAAG,MAAM,CAAC,uBAAuB,CAAC;AAE/D;;;AAGG;IACM,oBAAoB,GAAG,MAAM,CAAoB,QAAQ;6FAAC;AAElD,IAAA,cAAc,GAAG,MAAM,CAAU,IAAI,CAAC,YAAY,KAAK,MAAM,IAAI,IAAI,CAAC,YAAY,KAAK,QAAQ;uFAAC;IAChG,aAAa,GAAG,MAAM,CAAU,KAAK;sFAAC;AAE9C,IAAA,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ;IACjC,MAAM,GAAG,QAAQ,CACxB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,oBAAoB,EAAE,KAAK,MAAM;+EAC3F;AACQ,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,cAAc,EAAE;mFAAC;AACtE,IAAA,QAAQ,GAAG,QAAQ,CAC1B,MACE,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;AACzC,SAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,oBAAoB,EAAE,KAAK,QAAQ,CAAC;iFAC3F;AACQ,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,aAAa,EAAE;qFAAC;IAE/E,MAAM,GAAA;AACJ,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;AACnB,YAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACtC;aAAO;AACL,YAAA,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC;IACF;IAEA,IAAI,GAAA;AACF,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;AACnB,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;QAC9B;aAAO;AACL,YAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;QAC/B;IACF;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;AACnB,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;QAC/B;aAAO;AACL,YAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;QAChC;IACF;uGA/CW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAAf,eAAe,EAAA,CAAA;;2FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B;;;ACjBD;;;AAGG;MAuBU,iBAAiB,CAAA;AAC5B;;;;;AAKG;IACM,oBAAoB,GAAG,KAAK,CAAoB,QAAQ;6FAAC;AAC/C,IAAA,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;AAEnD,IAAA,WAAA,GAAA;QACE,MAAM,CACJ,MAAK;AACH,YAAA,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;AACnE,QAAA,CAAC,EACD,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAC5B;QAED,MAAM,CACJ,MAAK;;AAEH,YAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE;AACzD,gBAAA,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;YACrB;AACF,QAAA,CAAC,EACD,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAC5B;IACH;uGA3BW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,EAAA,oBAAA,EAAA,EAAA,iBAAA,EAAA,sBAAA,EAAA,UAAA,EAAA,sBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,qBAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhBlB;;;;;;;;;;;;;;AAcT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAEU,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAtB7B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,WAAW;AACrB,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EAAE,qBAAqB;AAC7B,qBAAA;AACD,oBAAA,QAAQ,EAAE;;;;;;;;;;;;;;AAcT,EAAA,CAAA;AACF,iBAAA;;;ACxBD;;;;AAIG;MAiBU,kBAAkB,CAAA;AACV,IAAA,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;IAChC,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;iFAAC;AACnD,IAAA,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC;AAEjD,IAAA,WAAA,GAAA;QACE,MAAM,CAAC,MAAK;YACV,IAAI,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE;YACvD,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE;AACrD,QAAA,CAAC,CAAC;IACJ;uGAVW,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,2ZAFnB,CAAA,yBAAA,CAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAE1B,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAhB9B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,YAAY;AACtB,oBAAA,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,YAAY,CAAC;AACvB,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EAAE,YAAY;AACnB,wBAAA,mBAAmB,EAAE,YAAY;AACjC,wBAAA,iBAAiB,EAAE,iBAAiB;AACpC,wBAAA,wBAAwB,EAAE,uBAAuB;AACjD,wBAAA,sBAAsB,EAAE,qBAAqB;AAC7C,wBAAA,oBAAoB,EAAE,YAAY;AAClC,wBAAA,IAAI,EAAE,YAAY;AACnB,qBAAA;oBACD,cAAc,EAAE,CAAC,YAAY,CAAC;AAC9B,oBAAA,QAAQ,EAAE,CAAA,yBAAA,CAA2B;AACtC,iBAAA;;;ACtBD;;;AAGG;MAUU,kBAAkB,CAAA;uGAAlB,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,gJAFnB,CAAA,yBAAA,CAA2B,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAE1B,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAT9B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,YAAY;AACtB,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EAAE,YAAY;AACnB,wBAAA,IAAI,EAAE,QAAQ;AACf,qBAAA;AACD,oBAAA,QAAQ,EAAE,CAAA,yBAAA,CAA2B;AACtC,iBAAA;;;ACXD;;;;;;;;;AASG;MAkBU,kBAAkB,CAAA;IAC7B,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAU;AACb,IAAA,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;uGAFxC,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,aAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAXnB;;;;;;;;;AAST,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA;;2FAEU,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAjB9B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,aAAa;AACvB,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,IAAI,EAAE;AACJ,wBAAA,KAAK,EAAE,aAAa;AACrB,qBAAA;AACD,oBAAA,QAAQ,EAAE;;;;;;;;;AAST,EAAA,CAAA;AACF,iBAAA;;;AC1BD;;;;;;;;;;;AAWG;MASU,wBAAwB,CAAA;AAClB,IAAA,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;;AAGxC,IAAA,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;;AAG3B,IAAA,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU;;AAGnC,IAAA,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ;;AAG/B,IAAA,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY;;AAGvC,IAAA,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ;IAExC,OAAO,GAAA;AACL,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;IACtB;uGApBW,wBAAwB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAxB,wBAAwB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,WAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,iBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAxB,wBAAwB,EAAA,UAAA,EAAA,CAAA;kBARpC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,mBAAmB;AAC7B,oBAAA,QAAQ,EAAE,iBAAiB;AAC3B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,IAAI,EAAE;AACJ,wBAAA,SAAS,EAAE,WAAW;AACvB,qBAAA;AACF,iBAAA;;;ACtBD;;AAEG;;;;"}
|
package/fesm2022/ktortu-aaa.mjs
CHANGED
|
@@ -5,6 +5,7 @@ export * from '@ktortu/aaa/dialog';
|
|
|
5
5
|
export * from '@ktortu/aaa/disclosure';
|
|
6
6
|
export * from '@ktortu/aaa/forms';
|
|
7
7
|
export * from '@ktortu/aaa/i18n';
|
|
8
|
+
export * from '@ktortu/aaa/icon';
|
|
8
9
|
export * from '@ktortu/aaa/menu';
|
|
9
10
|
export * from '@ktortu/aaa/snackbar';
|
|
10
11
|
export * from '@ktortu/aaa/tabs';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ktortu-aaa.mjs","sources":["../../../../projects/ktortu/aaa/src/public-api.ts","../../../../projects/ktortu/aaa/src/ktortu-aaa.ts"],"sourcesContent":["/*\n * Public API Surface of @ktortu/aaa\n */\nexport * from '@ktortu/aaa/button';\nexport * from '@ktortu/aaa/card';\nexport * from '@ktortu/aaa/cdk';\nexport * from '@ktortu/aaa/dialog';\nexport * from '@ktortu/aaa/disclosure';\nexport * from '@ktortu/aaa/forms';\nexport * from '@ktortu/aaa/i18n';\nexport * from '@ktortu/aaa/menu';\nexport * from '@ktortu/aaa/snackbar';\nexport * from '@ktortu/aaa/tabs';\nexport * from '@ktortu/aaa/tooltip';\nexport * from '@ktortu/aaa/progress-bar';\nexport * from '@ktortu/aaa/layout';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ktortu-aaa.mjs","sources":["../../../../projects/ktortu/aaa/src/public-api.ts","../../../../projects/ktortu/aaa/src/ktortu-aaa.ts"],"sourcesContent":["/*\n * Public API Surface of @ktortu/aaa\n */\nexport * from '@ktortu/aaa/button';\nexport * from '@ktortu/aaa/card';\nexport * from '@ktortu/aaa/cdk';\nexport * from '@ktortu/aaa/dialog';\nexport * from '@ktortu/aaa/disclosure';\nexport * from '@ktortu/aaa/forms';\nexport * from '@ktortu/aaa/i18n';\nexport * from '@ktortu/aaa/icon';\nexport * from '@ktortu/aaa/menu';\nexport * from '@ktortu/aaa/snackbar';\nexport * from '@ktortu/aaa/tabs';\nexport * from '@ktortu/aaa/tooltip';\nexport * from '@ktortu/aaa/progress-bar';\nexport * from '@ktortu/aaa/layout';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA;;AAEG;;ACFH;;AAEG"}
|
package/icon/icon.css
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@layer kt-aaa.components {
|
|
2
|
+
/* Styles de la primitive d'icône autonome [ktIcon]. Global : chargé via l'agrégat styles.css,
|
|
3
|
+
applicable partout où la directive est posée. Ciblé par la CLASSE `.kt-icon` (posée en dur sur
|
|
4
|
+
l'hôte par la directive) et NON par l'attribut de sélecteur : un binding `[ktIcon]="x"` ne reflète
|
|
5
|
+
aucun attribut dans le DOM, la classe reste le seul marqueur fiable. Leviers = BASCULES NON déclarées
|
|
6
|
+
(le fallback var() fait foi) : --kt-icon-font / --kt-icon-font-weight / --kt-icon-font-variation (posées
|
|
7
|
+
par la directive quand une police du registre est résolue, sinon cascade globale) + --kt-icon-size
|
|
8
|
+
(posée par [size]) + --kt-icon-overflow (bascule d'opt-out du rognage de la boîte ligature). */
|
|
9
|
+
|
|
10
|
+
.kt-icon {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
flex: none;
|
|
15
|
+
/* Taille pilotée par --kt-icon-size (posée par [size]) ; défaut relatif au texte du contexte. */
|
|
16
|
+
font-size: var(--kt-icon-size, 1.25em);
|
|
17
|
+
line-height: 1;
|
|
18
|
+
/* Couleur héritée du texte courant (currentColor) : l'icône suit son contexte sans réglage. */
|
|
19
|
+
color: inherit;
|
|
20
|
+
/* Alignement propre au fil du texte (icône ↔ libellé adjacent). */
|
|
21
|
+
vertical-align: middle;
|
|
22
|
+
-webkit-font-smoothing: antialiased;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Rendu de la LIGATURE (nom → glyphe) via un pseudo-élément, comme Button/Field.
|
|
26
|
+
Police : --kt-icon-font (posée par la directive si police du registre résolue) > cascade globale
|
|
27
|
+
> Material Symbols Outlined. Un set NON-ligature (Font Awesome…) se projette dans l'hôte (ktIcon
|
|
28
|
+
vide) : aucun ::before n'est alors rendu. */
|
|
29
|
+
.kt-icon[data-icon]::before {
|
|
30
|
+
content: attr(data-icon);
|
|
31
|
+
font-family: var(--kt-icon-font, 'Material Symbols Outlined');
|
|
32
|
+
font-weight: var(--kt-icon-font-weight, normal);
|
|
33
|
+
font-style: normal;
|
|
34
|
+
font-size: 1em; /* = taille de l'hôte (--kt-icon-size) */
|
|
35
|
+
line-height: 1;
|
|
36
|
+
font-variation-settings: var(--kt-icon-font-variation, normal);
|
|
37
|
+
font-feature-settings: 'liga';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Boîte réservée (1 em, carrée) UNIQUEMENT en mode ligature : évite le décalage de mise en page au
|
|
41
|
+
chargement de la police et masque le clignotement du nom en clair (FOUT) tant que la police n'est
|
|
42
|
+
pas prête. `clip` + une petite marge tolèrent un léger débordement d'encre (polices tierces dont le
|
|
43
|
+
glyphe dépasse le cadratin) sans rogner, tout en bridant le texte de repli. Bascule d'opt-out via
|
|
44
|
+
--kt-icon-overflow (ex. `visible`). Non appliqué au mode projeté (contenu de largeur libre). */
|
|
45
|
+
.kt-icon[data-icon] {
|
|
46
|
+
inline-size: 1em;
|
|
47
|
+
block-size: 1em;
|
|
48
|
+
overflow: hidden; /* Fallback pour WebView < 90 */
|
|
49
|
+
overflow: var(--kt-icon-overflow, clip);
|
|
50
|
+
overflow-clip-margin: 0.15em;
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ktortu/aaa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Bibliothèque de composants Angular headless + thémés par tokens CSS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
"types": "./types/ktortu-aaa-i18n.d.ts",
|
|
86
86
|
"default": "./fesm2022/ktortu-aaa-i18n.mjs"
|
|
87
87
|
},
|
|
88
|
+
"./icon": {
|
|
89
|
+
"types": "./types/ktortu-aaa-icon.d.ts",
|
|
90
|
+
"default": "./fesm2022/ktortu-aaa-icon.mjs"
|
|
91
|
+
},
|
|
88
92
|
"./layout": {
|
|
89
93
|
"types": "./types/ktortu-aaa-layout.d.ts",
|
|
90
94
|
"default": "./fesm2022/ktortu-aaa-layout.mjs"
|
package/styles/icon.css
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
@layer kt-aaa.tokens, kt-aaa.base, kt-aaa.components;
|
|
2
|
+
|
|
3
|
+
/* Icône autonome (directive ktIcon) : apparence pure (aucun token dédié — cf. les bascules
|
|
4
|
+
--kt-icon-font / --kt-icon-font-weight / --kt-icon-size du socle). Nécessite le socle
|
|
5
|
+
(@ktortu/aaa/foundation.css) et le chargement d'une police d'icônes côté application.
|
|
6
|
+
@import '@ktortu/aaa/icon.css'; */
|
|
7
|
+
@import '../icon/icon.css';
|
package/styles/styles.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { OnInit, OnDestroy, Provider } from '@angular/core';
|
|
2
|
+
import { OnInit, OnDestroy, InjectionToken, Provider } from '@angular/core';
|
|
3
3
|
import { DialogConfig, DialogRef, CdkDialogContainer } from '@angular/cdk/dialog';
|
|
4
4
|
import { ComponentType } from '@angular/cdk/portal';
|
|
5
5
|
import * as _ktortu_aaa_dialog from '@ktortu/aaa/dialog';
|
|
@@ -105,6 +105,10 @@ declare class KtDialogClose {
|
|
|
105
105
|
* aria-labelledby). En usage simple — titre nu sans header — [ktDialogTitle] conserve son propre
|
|
106
106
|
* padding : ce header n'est à utiliser que pour les en-têtes composés (icône, close top-right…).
|
|
107
107
|
*
|
|
108
|
+
* ⚠️ Ne rend RIEN par lui-même — pas de bouton de fermeture implicite : c'est vous qui posez la
|
|
109
|
+
* croix (cf. exemple). En présentation `sheet`, l'option `sheetCloseButton` en rend une
|
|
110
|
+
* automatiquement, hors flux ; les deux ensemble feraient doublon (le conteneur avertit en dev).
|
|
111
|
+
*
|
|
108
112
|
* @example
|
|
109
113
|
* ```html
|
|
110
114
|
* <header ktDialogHeader>
|
|
@@ -163,7 +167,7 @@ declare class KtDialogFocusInitial {
|
|
|
163
167
|
}
|
|
164
168
|
/**
|
|
165
169
|
* @deprecated ADR-0005 — la poignée est désormais RENDUE AUTOMATIQUEMENT par `KtDialogContainer`
|
|
166
|
-
* en présentation `sheet` (opt-out :
|
|
170
|
+
* en présentation `sheet` (opt-out : option typée `sheetHandle: false`), et le
|
|
167
171
|
* drag-to-dismiss s'attrape sur TOUTE la surface de la sheet (scroll-snap natif). Cette
|
|
168
172
|
* directive est INERTE et son hôte est masqué (évite une double poignée) : retirez-la de vos
|
|
169
173
|
* templates. Sera supprimée dans une prochaine version majeure.
|
|
@@ -207,6 +211,63 @@ declare const KT_DIALOG_AAA_DEFAULTS: DialogConfig;
|
|
|
207
211
|
* @returns Un `Provider` pour le token `DEFAULT_DIALOG_CONFIG`.
|
|
208
212
|
*/
|
|
209
213
|
declare function provideKtDialogDefaults(overrides?: Partial<DialogConfig>): Provider;
|
|
214
|
+
/**
|
|
215
|
+
* Options MAISON du dialog (≠ {@link KT_DIALOG_AAA_DEFAULTS}, qui porte la config du CDK).
|
|
216
|
+
* Conforme à l'ADR-0003 : un token `KT_*_CONFIG` par entry-point, défauts anglais neutres,
|
|
217
|
+
* cascade `option d'ouverture ?? token ?? défaut`.
|
|
218
|
+
*
|
|
219
|
+
* Toutes les clés actuelles ne concernent que la présentation `sheet` (préfixe `sheet*`) : sur les
|
|
220
|
+
* présentations `centered` / `fullscreen`, la place d'un bouton de fermeture est un
|
|
221
|
+
* `[ktDialogHeader]`, où on le POSE soi-même, dans le flux. Un usage hors sheet déclenche un
|
|
222
|
+
* avertissement en mode dev.
|
|
223
|
+
*/
|
|
224
|
+
interface KtDialogConfig {
|
|
225
|
+
/**
|
|
226
|
+
* Bouton de fermeture AUTO-RENDU en haut de la carte, en présentation `sheet`.
|
|
227
|
+
*
|
|
228
|
+
* Désactivé par défaut, pour une raison de COMPATIBILITÉ, pas d'ergonomie : `[ktDialogHeader]`
|
|
229
|
+
* ne rend aucune croix (c'est une simple rangée flex), mais un dialog qui en compose un y place
|
|
230
|
+
* généralement la sienne à la main. Activer l'option par défaut ferait donc apparaître une
|
|
231
|
+
* DEUXIÈME croix chez ces consommateurs-là sans qu'ils aient rien changé.
|
|
232
|
+
*
|
|
233
|
+
* Recommandé sur toute sheet qui ne pose pas déjà son propre bouton de fermeture en tête.
|
|
234
|
+
* @default false
|
|
235
|
+
*/
|
|
236
|
+
sheetCloseButton: boolean;
|
|
237
|
+
/** Nom accessible du bouton de fermeture auto-rendu (WCAG 4.1.2). @default 'Close' */
|
|
238
|
+
sheetCloseLabel: string;
|
|
239
|
+
/** Poignée décorative auto-rendue en présentation `sheet` (ADR-0005). @default true */
|
|
240
|
+
sheetHandle: boolean;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Défauts effectifs des options maison du dialog. Objet `Required` figé : c'est la SOURCE des
|
|
244
|
+
* valeurs de repli, lue à la fois par l'ouvreur (résolution) et par le conteneur (fusion
|
|
245
|
+
* idempotente), et énumérée par la garde de complétude i18n.
|
|
246
|
+
*/
|
|
247
|
+
declare const DEFAULT_KT_DIALOG_CONFIG: Required<KtDialogConfig>;
|
|
248
|
+
/**
|
|
249
|
+
* Token des options maison du dialog. Deux usages, un seul chemin de résolution :
|
|
250
|
+
* - **application** : fourni via {@link provideKtDialog} (valeur partielle) ;
|
|
251
|
+
* - **par ouverture** : `injectKtDialogOpener` REFOURNIT ce même token, déjà RÉSOLU, dans
|
|
252
|
+
* l'injecteur du conteneur (canal `container.providers` du CDK). Le conteneur fusionne
|
|
253
|
+
* toujours par-dessus {@link DEFAULT_KT_DIALOG_CONFIG} : l'opération est idempotente sur une
|
|
254
|
+
* valeur déjà résolue, et reste correcte si le conteneur est monté sans passer par l'ouvreur.
|
|
255
|
+
*/
|
|
256
|
+
declare const KT_DIALOG_CONFIG: InjectionToken<Partial<KtDialogConfig>>;
|
|
257
|
+
/**
|
|
258
|
+
* Enregistre les options maison du dialog pour l'application (ou un sous-arbre).
|
|
259
|
+
* Complémentaire de {@link provideKtDialogDefaults}, qui porte la config du CDK : les deux
|
|
260
|
+
* coexistent et ne se recouvrent pas.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* // app.config.ts — bouton de fermeture sur toutes les bottom-sheets
|
|
265
|
+
* providers: [provideKtDialog({ sheetCloseButton: true })];
|
|
266
|
+
* ```
|
|
267
|
+
* @param config Options partielles fusionnées par-dessus `DEFAULT_KT_DIALOG_CONFIG`.
|
|
268
|
+
* @returns Un `Provider` pour le token `KT_DIALOG_CONFIG`.
|
|
269
|
+
*/
|
|
270
|
+
declare function provideKtDialog(config: Partial<KtDialogConfig>): Provider;
|
|
210
271
|
/**
|
|
211
272
|
* Présentation du dialog, CHOISIE EXPLICITEMENT PAR LE DÉVELOPPEUR à l'ouverture (option
|
|
212
273
|
* `presentation` de `injectKtDialogOpener`). Aucune bascule CSS automatique : le dev décide.
|
|
@@ -236,8 +297,11 @@ type KtDialogPresentation = 'centered' | 'fullscreen' | 'sheet' | 'centered-full
|
|
|
236
297
|
*/
|
|
237
298
|
declare function resolveKtDialogPanelClass(presentation?: KtDialogPresentation, compact?: boolean): string[];
|
|
238
299
|
|
|
239
|
-
/**
|
|
240
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Config d'ouverture sans le champ `data` (fourni séparément, typé), enrichie de `presentation` et
|
|
302
|
+
* des options maison de {@link KtDialogConfig} (surcharge par ouverture de `KT_DIALOG_CONFIG`).
|
|
303
|
+
*/
|
|
304
|
+
type KtDialogOpenerConfig<D, R, C> = Omit<DialogConfig<D, DialogRef<R, C>>, 'data'> & Partial<KtDialogConfig> & {
|
|
241
305
|
/** Présentation du dialog (cf. `KtDialogPresentation`) — choisie par le dev. Résolue à CHAQUE
|
|
242
306
|
ouverture : les variantes responsive lisent le signal d'écran compact. Défaut : `'centered'`.
|
|
243
307
|
Combinée à un éventuel `panelClass` additionnel. */
|
|
@@ -343,6 +407,11 @@ declare class KtDialogContainer extends CdkDialogContainer {
|
|
|
343
407
|
private readonly platformId;
|
|
344
408
|
private readonly destroyRef;
|
|
345
409
|
private readonly bodyScrollLock;
|
|
410
|
+
/** Options maison RÉSOLUES. `injectKtDialogOpener` refournit `KT_DIALOG_CONFIG` déjà résolu dans
|
|
411
|
+
l'injecteur du conteneur : la fusion ci-dessous est alors un no-op. Elle reste indispensable
|
|
412
|
+
quand le conteneur est monté sans l'ouvreur (`dialog.open` brut), où le token remonte à
|
|
413
|
+
l'application et n'est que partiel. Un seul chemin de code, idempotent. */
|
|
414
|
+
protected readonly ktConfig: Required<KtDialogConfig>;
|
|
346
415
|
protected readonly isClosing: i0.WritableSignal<boolean>;
|
|
347
416
|
/** Armé quand l'ouverture a dépassé 50px : un repos à ~0 est alors forcément le snap « fermé ». */
|
|
348
417
|
private sheetArmed;
|
|
@@ -355,9 +424,25 @@ declare class KtDialogContainer extends CdkDialogContainer {
|
|
|
355
424
|
renomme ce membre, on dégrade proprement vers `null` au lieu de casser le rendu du conteneur. */
|
|
356
425
|
protected get ariaLabelledBy(): string | null;
|
|
357
426
|
constructor();
|
|
427
|
+
/** Garde-fous dev sur la config maison : usage hors sheet, double bouton de fermeture, opt-out
|
|
428
|
+
déprécié de la poignée. On AVERTIT sans jamais corriger en silence — un masquage automatique
|
|
429
|
+
dépendrait de l'ordre de rendu du portail et serait indébogable. */
|
|
430
|
+
private warnOnConfigMisuse;
|
|
358
431
|
protected isSheet(): boolean;
|
|
359
|
-
/**
|
|
432
|
+
/** panelClass d'opt-out historique de la poignée — repli DÉPRÉCIÉ, remplacé par `sheetHandle`. */
|
|
433
|
+
private hasLegacyNoHandleClass;
|
|
434
|
+
/** Poignée décorative auto-rendue en présentation sheet (ADR-0005). Opt-out : option `sheetHandle`
|
|
435
|
+
(le panelClass historique reste honoré, en déprécié). */
|
|
360
436
|
protected showSheetHandle(): boolean;
|
|
437
|
+
/** Bouton de fermeture auto-rendu : opt-in `sheetCloseButton`, et UNIQUEMENT en présentation sheet
|
|
438
|
+
(ailleurs, la croix se pose à la main dans un `[ktDialogHeader]` — cf. `KtDialogConfig`). */
|
|
439
|
+
protected showSheetClose(): boolean;
|
|
440
|
+
/** Clic sur le bouton de fermeture auto-rendu. Ne consulte VOLONTAIREMENT pas `disableClose` :
|
|
441
|
+
contrairement au tap-extérieur et à Échap (fermetures « par inadvertance » que `disableClose`
|
|
442
|
+
protège), ce bouton est une sortie EXPLICITEMENT demandée par le dev via `sheetCloseButton` —
|
|
443
|
+
le combiner à `disableClose` est le patron « une seule sortie contrôlée ». Le drapeau
|
|
444
|
+
`isClosing` garde l'idempotence face à un geste de glissement simultané. */
|
|
445
|
+
protected onSheetCloseClick(): void;
|
|
361
446
|
/** Tap sur le spacer (zone extérieure à la carte) = fermeture, façon backdrop. Le conteneur
|
|
362
447
|
captant désormais le tactile (pointer-events auto, requis pour le drag iOS), le clic ne
|
|
363
448
|
traverse plus vers le backdrop CDK : on porte donc ICI la fermeture au tap-extérieur, en
|
|
@@ -531,5 +616,5 @@ declare class KtQuickDialog {
|
|
|
531
616
|
static ɵprov: i0.ɵɵInjectableDeclaration<KtQuickDialog>;
|
|
532
617
|
}
|
|
533
618
|
|
|
534
|
-
export { KT_DIALOG_AAA_DEFAULTS, KtAlertDialog, KtConfirmDialog, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, KtQuickDialog, defineKtDialog, injectAlertDialog, injectConfirmDialog, injectKtDialogOpener, provideKtDialogDefaults, resolveKtDialogPanelClass };
|
|
535
|
-
export type { KtAlertData, KtAlertDialogVariant, KtAlertOptions, KtConfirmConfig, KtConfirmResult, KtDecideConfig, KtDialogColor, KtDialogContract, KtDialogOpenerConfig, KtDialogPresentation };
|
|
619
|
+
export { DEFAULT_KT_DIALOG_CONFIG, KT_DIALOG_AAA_DEFAULTS, KT_DIALOG_CONFIG, KtAlertDialog, KtConfirmDialog, KtDialogActions, KtDialogClose, KtDialogContainer, KtDialogContent, KtDialogDescription, KtDialogFocusInitial, KtDialogHeader, KtDialogImports, KtDialogSheetHandle, KtDialogTitle, KtQuickDialog, defineKtDialog, injectAlertDialog, injectConfirmDialog, injectKtDialogOpener, provideKtDialog, provideKtDialogDefaults, resolveKtDialogPanelClass };
|
|
620
|
+
export type { KtAlertData, KtAlertDialogVariant, KtAlertOptions, KtConfirmConfig, KtConfirmResult, KtDecideConfig, KtDialogColor, KtDialogConfig, KtDialogContract, KtDialogOpenerConfig, KtDialogPresentation };
|
|
@@ -2,6 +2,7 @@ import { EnvironmentProviders } from '@angular/core';
|
|
|
2
2
|
import { KtFieldConfig, KtSelectConfigOptions, KtChipsConfig } from '@ktortu/aaa/forms';
|
|
3
3
|
import { KtSnackbarConfig } from '@ktortu/aaa/snackbar';
|
|
4
4
|
import { KtTabsConfig } from '@ktortu/aaa/tabs';
|
|
5
|
+
import { KtDialogConfig } from '@ktortu/aaa/dialog';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Agrégat de TOUS les libellés traduisibles de la lib, groupés par famille de composants.
|
|
@@ -23,6 +24,8 @@ interface KtTranslations {
|
|
|
23
24
|
snackbar?: Partial<KtSnackbarConfig>;
|
|
24
25
|
/** Libellés de la pagination des onglets → {@link KT_TABS_CONFIG}. */
|
|
25
26
|
tabs?: Partial<KtTabsConfig>;
|
|
27
|
+
/** Libellés du dialog (`sheetCloseLabel`) → {@link KT_DIALOG_CONFIG}. */
|
|
28
|
+
dialog?: Partial<KtDialogConfig>;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* Enregistre, en un seul appel, les libellés fournis sur les tokens de config de la lib.
|