@ktortu/aaa 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +151 -0
- package/button/button-tokens.css +152 -0
- package/button/button.css +319 -0
- package/card/card-tokens.css +49 -0
- package/card/card.css +200 -0
- package/cdk/styles/foundation.css +83 -0
- package/cdk/styles/tabs.css +276 -0
- package/dialog/dialog.css +350 -0
- package/fesm2022/ktortu-aaa-button.mjs +128 -0
- package/fesm2022/ktortu-aaa-button.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-card.mjs +209 -0
- package/fesm2022/ktortu-aaa-card.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-cdk.mjs +183 -0
- package/fesm2022/ktortu-aaa-cdk.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-dialog.mjs +512 -0
- package/fesm2022/ktortu-aaa-dialog.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-forms.mjs +3215 -0
- package/fesm2022/ktortu-aaa-forms.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-menu.mjs +315 -0
- package/fesm2022/ktortu-aaa-menu.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-tabs.mjs +79 -0
- package/fesm2022/ktortu-aaa-tabs.mjs.map +1 -0
- package/fesm2022/ktortu-aaa-tooltip.mjs +356 -0
- package/fesm2022/ktortu-aaa-tooltip.mjs.map +1 -0
- package/fesm2022/ktortu-aaa.mjs +17 -0
- package/fesm2022/ktortu-aaa.mjs.map +1 -0
- package/forms/checkbox/checkbox-group.css +55 -0
- package/forms/checkbox/checkbox.css +216 -0
- package/forms/chips/chip-list.css +70 -0
- package/forms/chips/chip.css +92 -0
- package/forms/chips/tokens.css +102 -0
- package/forms/field/field.css +87 -0
- package/forms/multi-select/multi-select.css +136 -0
- package/forms/radio/radio-group.css +55 -0
- package/forms/radio/radio.css +165 -0
- package/forms/styles/field-box.css +171 -0
- package/forms/styles/select-panel.css +464 -0
- package/forms/styles/tokens.css +67 -0
- package/forms/switch/switch.css +188 -0
- package/menu/menu-tokens.css +58 -0
- package/menu/menu.css +224 -0
- package/package.json +96 -0
- package/styles/button.css +6 -0
- package/styles/card.css +6 -0
- package/styles/dialog.css +6 -0
- package/styles/forms.css +13 -0
- package/styles/foundation.css +7 -0
- package/styles/menu.css +6 -0
- package/styles/styles.css +24 -0
- package/styles/tabs.css +5 -0
- package/styles/tooltip.css +5 -0
- package/themes/theme-ant.css +44 -0
- package/themes/theme-architecte.css +83 -0
- package/themes/theme-aurora.css +97 -0
- package/themes/theme-bootstrap.css +46 -0
- package/themes/theme-carbon.css +49 -0
- package/themes/theme-catppuccin.css +66 -0
- package/themes/theme-cyberpunk.css +211 -0
- package/themes/theme-fluent.css +45 -0
- package/themes/theme-material-you.css +74 -0
- package/themes/theme-material.css +48 -0
- package/themes/theme-primer.css +46 -0
- package/themes/theme-vegetal.css +78 -0
- package/tooltip/tooltip.css +129 -0
- package/types/ktortu-aaa-button.d.ts +70 -0
- package/types/ktortu-aaa-card.d.ts +143 -0
- package/types/ktortu-aaa-cdk.d.ts +110 -0
- package/types/ktortu-aaa-dialog.d.ts +286 -0
- package/types/ktortu-aaa-forms.d.ts +1574 -0
- package/types/ktortu-aaa-menu.d.ts +171 -0
- package/types/ktortu-aaa-tabs.d.ts +27 -0
- package/types/ktortu-aaa-tooltip.d.ts +90 -0
- package/types/ktortu-aaa.d.ts +8 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Directive, inject, ElementRef, afterNextRender, isDevMode, InjectionToken, contentChild, input, booleanAttribute, effect } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur
|
|
6
|
+
* structurel sans logique — la mise en forme vit dans `card.css` via `[ktCardHeader]`.
|
|
7
|
+
* L'ÉTIQUETTE accessible reste le titre fourni par le consommateur (`<h3 id>` + `aria-labelledby`
|
|
8
|
+
* sur l'hôte), comme [ktDialogTitle] pour le dialog : on sépare layout et sémantique.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```html
|
|
12
|
+
* <header ktCardHeader><h3 id="t1">Titre</h3></header>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
class KtCardHeader {
|
|
16
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
17
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardHeader, isStandalone: true, selector: "[ktCardHeader]", ngImport: i0 });
|
|
18
|
+
}
|
|
19
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardHeader, decorators: [{
|
|
20
|
+
type: Directive,
|
|
21
|
+
args: [{ selector: '[ktCardHeader]' }]
|
|
22
|
+
}] });
|
|
23
|
+
/**
|
|
24
|
+
* Média pleine largeur (image/vidéo). Marqueur structurel sans logique : `card.css` lui donne le
|
|
25
|
+
* full-bleed (marge négative = padding de la carte) et `[ktCard]` clippe au rayon quand un média
|
|
26
|
+
* est présent. À envelopper autour d'un `<img ngSrc>` (NgOptimizedImage).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```html
|
|
30
|
+
* <div ktCardMedia><img ngSrc="cover.jpg" width="400" height="200" alt="" /></div>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
class KtCardMedia {
|
|
34
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardMedia, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
35
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardMedia, isStandalone: true, selector: "[ktCardMedia]", ngImport: i0 });
|
|
36
|
+
}
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardMedia, decorators: [{
|
|
38
|
+
type: Directive,
|
|
39
|
+
args: [{ selector: '[ktCardMedia]' }]
|
|
40
|
+
}] });
|
|
41
|
+
/**
|
|
42
|
+
* Corps de la carte. Marqueur structurel sans logique : la mise en forme (rythme vertical) vit
|
|
43
|
+
* dans `card.css` via `[ktCardContent]`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```html
|
|
47
|
+
* <div ktCardContent>Texte de la carte.</div>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
class KtCardContent {
|
|
51
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
52
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardContent, isStandalone: true, selector: "[ktCardContent]", ngImport: i0 });
|
|
53
|
+
}
|
|
54
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardContent, decorators: [{
|
|
55
|
+
type: Directive,
|
|
56
|
+
args: [{ selector: '[ktCardContent]' }]
|
|
57
|
+
}] });
|
|
58
|
+
/**
|
|
59
|
+
* Barre d'actions de la carte (rangée de boutons/liens). Marqueur structurel sans logique :
|
|
60
|
+
* la mise en forme (flex, gap, épinglée en pied) vit dans `card.css` via `[ktCardActions]`.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```html
|
|
64
|
+
* <footer ktCardActions><button ktButton>Action</button></footer>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
class KtCardActions {
|
|
68
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardActions, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
69
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardActions, isStandalone: true, selector: "[ktCardActions]", ngImport: i0 });
|
|
70
|
+
}
|
|
71
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardActions, decorators: [{
|
|
72
|
+
type: Directive,
|
|
73
|
+
args: [{ selector: '[ktCardActions]' }]
|
|
74
|
+
}] });
|
|
75
|
+
/**
|
|
76
|
+
* Lien (ou bouton) PRIMAIRE d'une carte interactive : pattern « lien étiré » (Inclusive
|
|
77
|
+
* Components). Un pseudo-élément `::after` couvre toute la carte (cf. `card.css`) → toute la
|
|
78
|
+
* surface est cliquable, SANS imbriquer de contrôles interactifs (anti-pattern WCAG 4.1.2). Les
|
|
79
|
+
* actions secondaires de la carte repassent au-dessus du lien (z-index dans `card.css`).
|
|
80
|
+
*
|
|
81
|
+
* UN SEUL [ktCardLink] par carte. Le focus clavier est porté sur ce lien ; l'anneau de focus est
|
|
82
|
+
* relayé sur toute la carte (`[ktCard]:has([ktCardLink]:focus-visible)`).
|
|
83
|
+
*
|
|
84
|
+
* Quand la carte ancêtre est `disabled`, le lien sort de l'ordre de tabulation (`tabindex="-1"`)
|
|
85
|
+
* et est annoncé `aria-disabled` : une carte inerte ne piège pas le focus clavier (WCAG 2.4.3).
|
|
86
|
+
*/
|
|
87
|
+
class KtCardLink {
|
|
88
|
+
host = inject(ElementRef).nativeElement;
|
|
89
|
+
/**
|
|
90
|
+
* Carte ancêtre, injectée optionnellement via `inject(Card, { optional: true })` (DI par
|
|
91
|
+
* hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer
|
|
92
|
+
* l'état `disabled` de la carte (aria-disabled / tabindex) sans planter hors contexte.
|
|
93
|
+
*/
|
|
94
|
+
card = inject(KtCard, { optional: true });
|
|
95
|
+
handleClick(event) {
|
|
96
|
+
if (this.card?.disabled()) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
event.stopPropagation();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
constructor() {
|
|
102
|
+
// Garde-fou a11y (dev seulement) : un lien étiré sans nom accessible n'est pas annonçable.
|
|
103
|
+
afterNextRender(() => {
|
|
104
|
+
if (!isDevMode())
|
|
105
|
+
return;
|
|
106
|
+
const hasName = !!this.host.textContent?.trim() ||
|
|
107
|
+
!!this.host.getAttribute('aria-label')?.trim() ||
|
|
108
|
+
this.host.hasAttribute('aria-labelledby');
|
|
109
|
+
if (!hasName) {
|
|
110
|
+
console.warn('[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +
|
|
111
|
+
'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardLink, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
116
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: KtCardLink, isStandalone: true, selector: "a[ktCardLink], button[ktCardLink]", host: { listeners: { "click": "handleClick($event)" }, properties: { "attr.aria-disabled": "card?.disabled() ? \"true\" : null", "attr.tabindex": "card?.disabled() ? \"-1\" : null" } }, ngImport: i0 });
|
|
117
|
+
}
|
|
118
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCardLink, decorators: [{
|
|
119
|
+
type: Directive,
|
|
120
|
+
args: [{
|
|
121
|
+
selector: 'a[ktCardLink], button[ktCardLink]',
|
|
122
|
+
host: {
|
|
123
|
+
'[attr.aria-disabled]': 'card?.disabled() ? "true" : null',
|
|
124
|
+
'[attr.tabindex]': 'card?.disabled() ? "-1" : null',
|
|
125
|
+
'(click)': 'handleClick($event)',
|
|
126
|
+
},
|
|
127
|
+
}]
|
|
128
|
+
}], ctorParameters: () => [] });
|
|
129
|
+
|
|
130
|
+
const KT_CARD_CONFIG = new InjectionToken('KT_CARD_CONFIG');
|
|
131
|
+
/**
|
|
132
|
+
* Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi
|
|
133
|
+
* par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de
|
|
134
|
+
* wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).
|
|
135
|
+
*
|
|
136
|
+
* Trois axes pilotés en `data-*` (même contrat que ktButton) :
|
|
137
|
+
* - variant : data-variant = elevated | outlined | filled (apparence)
|
|
138
|
+
* - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable
|
|
139
|
+
* est un [ktCardLink], lien/bouton primaire au lien étiré)
|
|
140
|
+
* - disabled : data-disabled (surface inerte)
|
|
141
|
+
*
|
|
142
|
+
* La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs
|
|
143
|
+
* et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```html
|
|
147
|
+
* <article ktCard variant="outlined" interactive>
|
|
148
|
+
* <div ktCardContent>
|
|
149
|
+
* <h3 id="t1">Titre</h3>
|
|
150
|
+
* <a ktCardLink routerLink="/detail" aria-labelledby="t1">Voir le détail</a>
|
|
151
|
+
* </div>
|
|
152
|
+
* </article>
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
class KtCard {
|
|
156
|
+
config = inject(KT_CARD_CONFIG, { optional: true });
|
|
157
|
+
host = inject(ElementRef).nativeElement;
|
|
158
|
+
/** Référence réactive sur le lien primaire de la carte */
|
|
159
|
+
cardLink = contentChild(KtCardLink, { ...(ngDevMode ? { debugName: "cardLink" } : /* istanbul ignore next */ {}), descendants: true });
|
|
160
|
+
/** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */
|
|
161
|
+
variant = input(this.config?.variant ?? 'elevated', /* @ts-ignore */
|
|
162
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
163
|
+
/**
|
|
164
|
+
* Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton
|
|
165
|
+
* primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false
|
|
166
|
+
*/
|
|
167
|
+
interactive = input(false, { ...(ngDevMode ? { debugName: "interactive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
168
|
+
/** Rend la surface inerte (état `data-disabled`). @default false */
|
|
169
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
170
|
+
constructor() {
|
|
171
|
+
// Garde-fou a11y (dev seulement) : une carte interactive sans cible cliquable affiche une
|
|
172
|
+
// affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).
|
|
173
|
+
effect(() => {
|
|
174
|
+
if (!isDevMode() || !this.interactive())
|
|
175
|
+
return;
|
|
176
|
+
const hasTarget = this.host.matches('a, button') || !!this.cardLink();
|
|
177
|
+
if (!hasTarget) {
|
|
178
|
+
console.warn('[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +
|
|
179
|
+
'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
184
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.1", type: KtCard, isStandalone: true, selector: "[ktCard]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variant": "variant()", "attr.data-interactive": "interactive() ? \"\" : null", "attr.data-disabled": "disabled() ? \"\" : null" } }, queries: [{ propertyName: "cardLink", first: true, predicate: KtCardLink, descendants: true, isSignal: true }], ngImport: i0 });
|
|
185
|
+
}
|
|
186
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtCard, decorators: [{
|
|
187
|
+
type: Directive,
|
|
188
|
+
args: [{
|
|
189
|
+
selector: '[ktCard]',
|
|
190
|
+
host: {
|
|
191
|
+
'[attr.data-variant]': 'variant()',
|
|
192
|
+
'[attr.data-interactive]': 'interactive() ? "" : null',
|
|
193
|
+
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
194
|
+
},
|
|
195
|
+
}]
|
|
196
|
+
}], ctorParameters: () => [], propDecorators: { cardLink: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KtCardLink), { ...{ descendants: true }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Import ergonomique de toute la famille card en une fois :
|
|
200
|
+
* `imports: [KT_CARD]` au lieu d'énumérer chaque directive.
|
|
201
|
+
*/
|
|
202
|
+
const KT_CARD = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink];
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Generated bundle index. Do not edit.
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
export { KT_CARD, KT_CARD_CONFIG, KtCard, KtCardActions, KtCardContent, KtCardHeader, KtCardLink, KtCardMedia };
|
|
209
|
+
//# sourceMappingURL=ktortu-aaa-card.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktortu-aaa-card.mjs","sources":["../../../../projects/ktortu/aaa/card/card-structure.ts","../../../../projects/ktortu/aaa/card/card.ts","../../../../projects/ktortu/aaa/card/public-api.ts","../../../../projects/ktortu/aaa/card/ktortu-aaa-card.ts"],"sourcesContent":["import { Directive, ElementRef, afterNextRender, inject, isDevMode } from '@angular/core';\n\nimport { KtCard } from './card';\n\n/**\n * En-tête de la carte (rangée flex : média/avatar + titre + éventuelle action). Marqueur\n * structurel sans logique — la mise en forme vit dans `card.css` via `[ktCardHeader]`.\n * L'ÉTIQUETTE accessible reste le titre fourni par le consommateur (`<h3 id>` + `aria-labelledby`\n * sur l'hôte), comme [ktDialogTitle] pour le dialog : on sépare layout et sémantique.\n *\n * @example\n * ```html\n * <header ktCardHeader><h3 id=\"t1\">Titre</h3></header>\n * ```\n */\n@Directive({ selector: '[ktCardHeader]' })\nexport class KtCardHeader {}\n\n/**\n * Média pleine largeur (image/vidéo). Marqueur structurel sans logique : `card.css` lui donne le\n * full-bleed (marge négative = padding de la carte) et `[ktCard]` clippe au rayon quand un média\n * est présent. À envelopper autour d'un `<img ngSrc>` (NgOptimizedImage).\n *\n * @example\n * ```html\n * <div ktCardMedia><img ngSrc=\"cover.jpg\" width=\"400\" height=\"200\" alt=\"\" /></div>\n * ```\n */\n@Directive({ selector: '[ktCardMedia]' })\nexport class KtCardMedia {}\n\n/**\n * Corps de la carte. Marqueur structurel sans logique : la mise en forme (rythme vertical) vit\n * dans `card.css` via `[ktCardContent]`.\n *\n * @example\n * ```html\n * <div ktCardContent>Texte de la carte.</div>\n * ```\n */\n@Directive({ selector: '[ktCardContent]' })\nexport class KtCardContent {}\n\n/**\n * Barre d'actions de la carte (rangée de boutons/liens). Marqueur structurel sans logique :\n * la mise en forme (flex, gap, épinglée en pied) vit dans `card.css` via `[ktCardActions]`.\n *\n * @example\n * ```html\n * <footer ktCardActions><button ktButton>Action</button></footer>\n * ```\n */\n@Directive({ selector: '[ktCardActions]' })\nexport class KtCardActions {}\n\n/**\n * Lien (ou bouton) PRIMAIRE d'une carte interactive : pattern « lien étiré » (Inclusive\n * Components). Un pseudo-élément `::after` couvre toute la carte (cf. `card.css`) → toute la\n * surface est cliquable, SANS imbriquer de contrôles interactifs (anti-pattern WCAG 4.1.2). Les\n * actions secondaires de la carte repassent au-dessus du lien (z-index dans `card.css`).\n *\n * UN SEUL [ktCardLink] par carte. Le focus clavier est porté sur ce lien ; l'anneau de focus est\n * relayé sur toute la carte (`[ktCard]:has([ktCardLink]:focus-visible)`).\n *\n * Quand la carte ancêtre est `disabled`, le lien sort de l'ordre de tabulation (`tabindex=\"-1\"`)\n * et est annoncé `aria-disabled` : une carte inerte ne piège pas le focus clavier (WCAG 2.4.3).\n */\n@Directive({\n selector: 'a[ktCardLink], button[ktCardLink]',\n host: {\n '[attr.aria-disabled]': 'card?.disabled() ? \"true\" : null',\n '[attr.tabindex]': 'card?.disabled() ? \"-1\" : null',\n '(click)': 'handleClick($event)',\n },\n})\nexport class KtCardLink {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n /**\n * Carte ancêtre, injectée optionnellement via `inject(Card, { optional: true })` (DI par\n * hiérarchie d'éléments) : `null` si [ktCardLink] est utilisé hors d'une [ktCard]. Sert à relayer\n * l'état `disabled` de la carte (aria-disabled / tabindex) sans planter hors contexte.\n */\n protected readonly card = inject(KtCard, { optional: true });\n\n handleClick(event: Event): void {\n if (this.card?.disabled()) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n constructor() {\n // Garde-fou a11y (dev seulement) : un lien étiré sans nom accessible n'est pas annonçable.\n afterNextRender(() => {\n if (!isDevMode()) return;\n\n const hasName =\n !!this.host.textContent?.trim() ||\n !!this.host.getAttribute('aria-label')?.trim() ||\n this.host.hasAttribute('aria-labelledby');\n if (!hasName) {\n console.warn(\n '[ktCardLink] lien sans nom accessible : ajoutez du texte visible, [attr.aria-label] ' +\n 'ou aria-labelledby (WCAG 2.4.4 / 4.1.2).',\n );\n }\n });\n }\n}\n","import {\n Directive,\n ElementRef,\n InjectionToken,\n booleanAttribute,\n contentChild,\n effect,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\nimport { KtCardLink } from './card-structure';\n\n/** Axe \"apparence\" de la surface. */\nexport type KtCardVariant = 'elevated' | 'outlined' | 'filled';\n\n/** Défauts applicables à toutes les `[ktCard]` (surchargeables par carte via les inputs). */\nexport interface KtCardConfig {\n /** Apparence par défaut de la surface. */\n variant: KtCardVariant;\n}\n\nexport const KT_CARD_CONFIG = new InjectionToken<Partial<KtCardConfig>>('KT_CARD_CONFIG');\n\n/**\n * Carte : SURFACE de contenu. Directive (pas de composant) posée sur l'élément SÉMANTIQUE choisi\n * par le consommateur (`<article>`, `<section>`, `<li>`, `<a>`…) — la lib n'impose jamais de\n * wrapper non sémantique ni de rôle (cf. précédent Dialog : la sémantique appartient à l'hôte).\n *\n * Trois axes pilotés en `data-*` (même contrat que ktButton) :\n * - variant : data-variant = elevated | outlined | filled (apparence)\n * - interactive : data-interactive (affordance hover/focus — PAS un rôle ; la cible cliquable\n * est un [ktCardLink], lien/bouton primaire au lien étiré)\n * - disabled : data-disabled (surface inerte)\n *\n * La mise en forme (layout des marqueurs, lien étiré, états) vit dans `card.css` ; les couleurs\n * et la géométrie dérivent du socle `--kt-*` via `card-tokens.css`.\n *\n * @example\n * ```html\n * <article ktCard variant=\"outlined\" interactive>\n * <div ktCardContent>\n * <h3 id=\"t1\">Titre</h3>\n * <a ktCardLink routerLink=\"/detail\" aria-labelledby=\"t1\">Voir le détail</a>\n * </div>\n * </article>\n * ```\n */\n@Directive({\n selector: '[ktCard]',\n host: {\n '[attr.data-variant]': 'variant()',\n '[attr.data-interactive]': 'interactive() ? \"\" : null',\n '[attr.data-disabled]': 'disabled() ? \"\" : null',\n },\n})\nexport class KtCard {\n private readonly config = inject(KT_CARD_CONFIG, { optional: true });\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** Référence réactive sur le lien primaire de la carte */\n readonly cardLink = contentChild(KtCardLink, { descendants: true });\n\n /** Apparence de la surface : `elevated` | `outlined` | `filled`. @default 'elevated' (ou `KT_CARD_CONFIG.variant`) */\n readonly variant = input<KtCardVariant>(this.config?.variant ?? 'elevated');\n\n /**\n * Affordance visuelle d'élément cliquable (hover/focus). N'AJOUTE pas de rôle : un lien/bouton\n * primaire ([ktCardLink]) porte l'interaction et le nom accessible. @default false\n */\n readonly interactive = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n /** Rend la surface inerte (état `data-disabled`). @default false */\n readonly disabled = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n constructor() {\n // Garde-fou a11y (dev seulement) : une carte interactive sans cible cliquable affiche une\n // affordance trompeuse (hover/focus) qui ne mène à rien (WCAG 1.3.1).\n effect(() => {\n if (!isDevMode() || !this.interactive()) return;\n\n const hasTarget = this.host.matches('a, button') || !!this.cardLink();\n if (!hasTarget) {\n console.warn(\n '[ktCard] interactive sans cible cliquable : ajoutez un [ktCardLink] (lien/bouton ' +\n 'primaire) ou posez [ktCard] sur un <a>/<button> — sinon l’affordance est trompeuse.',\n );\n }\n });\n }\n}\n","import { KtCard } from './card';\nimport { KtCardActions, KtCardContent, KtCardHeader, KtCardLink, KtCardMedia } from './card-structure';\n\nexport * from './card';\nexport * from './card-structure';\n\n/**\n * Import ergonomique de toute la famille card en une fois :\n * `imports: [KT_CARD]` au lieu d'énumérer chaque directive.\n */\nexport const KT_CARD = [KtCard, KtCardHeader, KtCardMedia, KtCardContent, KtCardActions, KtCardLink] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
5
|
+
import { map } from 'rxjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drag-to-dismiss vertical (vers le BAS uniquement) d'une bottom-sheet, factorisé entre le Select
|
|
9
|
+
* (popup Popover) et le Dialog (pane CDK). Chaque appelant ne fournit que SON élément à translater
|
|
10
|
+
* et SON callback de fermeture.
|
|
11
|
+
*
|
|
12
|
+
* Geste DOUBLÉ côté appelant par Échap + bouton Fermer + tap-extérieur (WCAG 2.5.1) : ce module ne
|
|
13
|
+
* gère que le glissement. Le gating « écran compact uniquement » reste à l'appelant (signal `isCompact`
|
|
14
|
+
* de `KtViewport`) — la primitive est volontairement agnostique.
|
|
15
|
+
*/
|
|
16
|
+
function createKtSheetDrag(opts) {
|
|
17
|
+
const threshold = opts.threshold ?? 0.25;
|
|
18
|
+
let startY = 0;
|
|
19
|
+
let pane = null;
|
|
20
|
+
let handle = null;
|
|
21
|
+
const cleanupListeners = () => {
|
|
22
|
+
if (!handle)
|
|
23
|
+
return;
|
|
24
|
+
handle.removeEventListener('pointermove', onMove);
|
|
25
|
+
handle.removeEventListener('pointerup', onEnd);
|
|
26
|
+
handle.removeEventListener('pointercancel', onEnd);
|
|
27
|
+
handle.removeEventListener('lostpointercapture', onEnd);
|
|
28
|
+
};
|
|
29
|
+
const onMove = (event) => {
|
|
30
|
+
if (!pane)
|
|
31
|
+
return;
|
|
32
|
+
const dy = Math.max(0, event.clientY - startY); // on ne tire que vers le bas
|
|
33
|
+
pane.style.translate = `0 ${dy}px`;
|
|
34
|
+
};
|
|
35
|
+
const onEnd = (event) => {
|
|
36
|
+
if (!pane || !handle)
|
|
37
|
+
return;
|
|
38
|
+
cleanupListeners();
|
|
39
|
+
pane.classList.remove(opts.draggingClass);
|
|
40
|
+
const dragged = event.clientY - startY;
|
|
41
|
+
if (dragged > pane.getBoundingClientRect().height * threshold) {
|
|
42
|
+
pane.style.translate = '0 100%'; // continue le glissement jusqu'en bas, puis ferme
|
|
43
|
+
opts.onDismiss();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
pane.style.translate = ''; // snap-back : la transition CSS ramène à 0
|
|
47
|
+
}
|
|
48
|
+
pane = null;
|
|
49
|
+
handle = null;
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
start(event) {
|
|
53
|
+
if (event.button !== 0)
|
|
54
|
+
return;
|
|
55
|
+
const el = opts.pane();
|
|
56
|
+
if (!el)
|
|
57
|
+
return;
|
|
58
|
+
handle = event.currentTarget;
|
|
59
|
+
// La poignée ne doit PAS voler le focus (sinon un close-on-blur fermerait prématurément).
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
handle.setPointerCapture(event.pointerId);
|
|
62
|
+
startY = event.clientY;
|
|
63
|
+
pane = el;
|
|
64
|
+
pane.classList.add(opts.draggingClass);
|
|
65
|
+
handle.addEventListener('pointermove', onMove);
|
|
66
|
+
handle.addEventListener('pointerup', onEnd);
|
|
67
|
+
handle.addEventListener('pointercancel', onEnd);
|
|
68
|
+
handle.addEventListener('lostpointercapture', onEnd);
|
|
69
|
+
},
|
|
70
|
+
destroy() {
|
|
71
|
+
cleanupListeners();
|
|
72
|
+
pane = null;
|
|
73
|
+
handle = null;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Défauts : bascule bottom-sheet sous 600px (aligné sur l'ancien `MOBILE_QUERY`). */
|
|
79
|
+
const KT_DEFAULT_BREAKPOINTS = { mobile: 0, tablet: 600, desktop: 1024 };
|
|
80
|
+
/** Token des seuils responsive. Défaut = `KT_DEFAULT_BREAKPOINTS` ; surchargé par `provideKtBreakpoints`. */
|
|
81
|
+
const KT_BREAKPOINTS = new InjectionToken('KT_BREAKPOINTS', {
|
|
82
|
+
providedIn: 'root',
|
|
83
|
+
factory: () => KT_DEFAULT_BREAKPOINTS,
|
|
84
|
+
});
|
|
85
|
+
/**
|
|
86
|
+
* À ajouter aux `providers` de `app.config.ts`. Fusionne avec les défauts → surcharge partielle OK.
|
|
87
|
+
* @example provideKtBreakpoints({ tablet: 768, desktop: 1200 })
|
|
88
|
+
* @example provideKtBreakpoints({ tablet: '(min-width: 768px) and (orientation: landscape)' })
|
|
89
|
+
*/
|
|
90
|
+
function provideKtBreakpoints(overrides) {
|
|
91
|
+
return { provide: KT_BREAKPOINTS, useValue: { ...KT_DEFAULT_BREAKPOINTS, ...overrides } };
|
|
92
|
+
}
|
|
93
|
+
/** Évite le chevauchement d'1px entre deux bandes adjacentes (max-width juste sous le plancher suivant). */
|
|
94
|
+
const EPSILON = 0.02;
|
|
95
|
+
function minWidth(v) {
|
|
96
|
+
return `(min-width: ${v}px)`;
|
|
97
|
+
}
|
|
98
|
+
function maxWidth(nextFloor) {
|
|
99
|
+
return `(max-width: ${nextFloor - EPSILON}px)`;
|
|
100
|
+
}
|
|
101
|
+
/** `not all` ne matche jamais : repli sûr (= desktop) si une bande numérique se retrouve sans borne. */
|
|
102
|
+
function join(...parts) {
|
|
103
|
+
return parts.filter(Boolean).join(' and ') || 'not all';
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Convertit les seuils (nombres et/ou chaînes) en 3 media queries. Un palier-chaîne est repris
|
|
107
|
+
* verbatim ; un palier-nombre devient la bande `[plancher courant .. plancher suivant[`. Si le
|
|
108
|
+
* plancher suivant est une chaîne (pas de borne numérique), la bande reste ouverte de ce côté.
|
|
109
|
+
*/
|
|
110
|
+
function resolveKtBreakpointMedia(bp) {
|
|
111
|
+
const { mobile, tablet, desktop } = bp;
|
|
112
|
+
return {
|
|
113
|
+
mobile: typeof mobile === 'string'
|
|
114
|
+
? mobile
|
|
115
|
+
: join(mobile > 0 ? minWidth(mobile) : '', typeof tablet === 'number' ? maxWidth(tablet) : ''),
|
|
116
|
+
tablet: typeof tablet === 'string'
|
|
117
|
+
? tablet
|
|
118
|
+
: join(minWidth(tablet), typeof desktop === 'number' ? maxWidth(desktop) : ''),
|
|
119
|
+
desktop: typeof desktop === 'string' ? desktop : minWidth(desktop),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Expose l'état responsive courant en SIGNALS, dérivé des seuils configurés par l'appli
|
|
125
|
+
* (`KT_BREAKPOINTS` / `provideKtBreakpoints`). Fondé sur `BreakpointObserver` (`@angular/cdk/layout`,
|
|
126
|
+
* déjà dépendance) : SSR-safe (`matches: false` côté serveur → desktop par défaut) et nettoyage des
|
|
127
|
+
* listeners géré par le CDK.
|
|
128
|
+
*
|
|
129
|
+
* À injecter aussi bien dans les composants de la lib (bascule bottom-sheet) que dans le code des
|
|
130
|
+
* applis consommatrices pour leur propre layout responsive.
|
|
131
|
+
*/
|
|
132
|
+
class KtViewport {
|
|
133
|
+
observer = inject(BreakpointObserver);
|
|
134
|
+
/** Media queries résolues (largeur dérivée ou verbatim selon le token) — utile pour miroir CSS. */
|
|
135
|
+
media = resolveKtBreakpointMedia(inject(KT_BREAKPOINTS));
|
|
136
|
+
/** Le viewport est dans la bande mobile. */
|
|
137
|
+
isMobile = this.matches(this.media.mobile);
|
|
138
|
+
/** Le viewport est dans la bande tablette. */
|
|
139
|
+
isTablet = this.matches(this.media.tablet);
|
|
140
|
+
/** Le viewport est dans la bande desktop. */
|
|
141
|
+
isDesktop = this.matches(this.media.desktop);
|
|
142
|
+
/** Alias de `isMobile` : palier où les composants de la lib basculent en bottom-sheet. */
|
|
143
|
+
isCompact = this.isMobile;
|
|
144
|
+
matches(query) {
|
|
145
|
+
return toSignal(this.observer.observe(query).pipe(map((state) => state.matches)), {
|
|
146
|
+
initialValue: false,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtViewport, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
150
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtViewport, providedIn: 'root' });
|
|
151
|
+
}
|
|
152
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtViewport, decorators: [{
|
|
153
|
+
type: Injectable,
|
|
154
|
+
args: [{ providedIn: 'root' }]
|
|
155
|
+
}] });
|
|
156
|
+
|
|
157
|
+
class KtIdGenerator {
|
|
158
|
+
counters = new Map();
|
|
159
|
+
nextGlobalId = 0;
|
|
160
|
+
generateId(prefix) {
|
|
161
|
+
if (!prefix) {
|
|
162
|
+
return this.nextGlobalId++;
|
|
163
|
+
}
|
|
164
|
+
const current = this.counters.get(prefix) ?? 0;
|
|
165
|
+
this.counters.set(prefix, current + 1);
|
|
166
|
+
return current;
|
|
167
|
+
}
|
|
168
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtIdGenerator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
169
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtIdGenerator, providedIn: 'root' });
|
|
170
|
+
}
|
|
171
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: KtIdGenerator, decorators: [{
|
|
172
|
+
type: Injectable,
|
|
173
|
+
args: [{
|
|
174
|
+
providedIn: 'root',
|
|
175
|
+
}]
|
|
176
|
+
}] });
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Generated bundle index. Do not edit.
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
export { KT_BREAKPOINTS, KT_DEFAULT_BREAKPOINTS, KtIdGenerator, KtViewport, createKtSheetDrag, provideKtBreakpoints, resolveKtBreakpointMedia };
|
|
183
|
+
//# sourceMappingURL=ktortu-aaa-cdk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktortu-aaa-cdk.mjs","sources":["../../../../projects/ktortu/aaa/cdk/sheet/sheet-drag.ts","../../../../projects/ktortu/aaa/cdk/breakpoints/breakpoints.ts","../../../../projects/ktortu/aaa/cdk/breakpoints/viewport.ts","../../../../projects/ktortu/aaa/cdk/id-generator.ts","../../../../projects/ktortu/aaa/cdk/ktortu-aaa-cdk.ts"],"sourcesContent":["/** Contrôleur de drag-to-dismiss d'une bottom-sheet. À câbler sur le `pointerdown` de la poignée. */\nexport interface KtSheetDrag {\n start(event: PointerEvent): void;\n destroy(): void;\n}\n\nexport interface KtSheetDragOptions {\n /** Élément translaté pendant le glissement (la feuille elle-même). Lu à chaque `start`. */\n pane: () => HTMLElement | null;\n /** Fermeture déclenchée quand le seuil est franchi (ex. `expanded.set(false)` / `dialogRef.close()`). */\n onDismiss: () => void;\n /** Classe togglée sur le pane pendant le drag (coupe la transition CSS du glissement). */\n draggingClass: string;\n /** Fraction de la hauteur de la feuille au-delà de laquelle on ferme (défaut 0.25). */\n threshold?: number;\n}\n\n/**\n * Drag-to-dismiss vertical (vers le BAS uniquement) d'une bottom-sheet, factorisé entre le Select\n * (popup Popover) et le Dialog (pane CDK). Chaque appelant ne fournit que SON élément à translater\n * et SON callback de fermeture.\n *\n * Geste DOUBLÉ côté appelant par Échap + bouton Fermer + tap-extérieur (WCAG 2.5.1) : ce module ne\n * gère que le glissement. Le gating « écran compact uniquement » reste à l'appelant (signal `isCompact`\n * de `KtViewport`) — la primitive est volontairement agnostique.\n */\nexport function createKtSheetDrag(opts: KtSheetDragOptions): KtSheetDrag {\n const threshold = opts.threshold ?? 0.25;\n let startY = 0;\n let pane: HTMLElement | null = null;\n let handle: HTMLElement | null = null;\n\n const cleanupListeners = (): void => {\n if (!handle) return;\n handle.removeEventListener('pointermove', onMove);\n handle.removeEventListener('pointerup', onEnd);\n handle.removeEventListener('pointercancel', onEnd);\n handle.removeEventListener('lostpointercapture', onEnd);\n };\n\n const onMove = (event: PointerEvent): void => {\n if (!pane) return;\n const dy = Math.max(0, event.clientY - startY); // on ne tire que vers le bas\n pane.style.translate = `0 ${dy}px`;\n };\n\n const onEnd = (event: PointerEvent): void => {\n if (!pane || !handle) return;\n cleanupListeners();\n pane.classList.remove(opts.draggingClass);\n\n const dragged = event.clientY - startY;\n if (dragged > pane.getBoundingClientRect().height * threshold) {\n pane.style.translate = '0 100%'; // continue le glissement jusqu'en bas, puis ferme\n opts.onDismiss();\n } else {\n pane.style.translate = ''; // snap-back : la transition CSS ramène à 0\n }\n pane = null;\n handle = null;\n };\n\n return {\n start(event: PointerEvent): void {\n if (event.button !== 0) return;\n const el = opts.pane();\n if (!el) return;\n handle = event.currentTarget as HTMLElement;\n // La poignée ne doit PAS voler le focus (sinon un close-on-blur fermerait prématurément).\n event.preventDefault();\n handle.setPointerCapture(event.pointerId);\n startY = event.clientY;\n pane = el;\n pane.classList.add(opts.draggingClass);\n handle.addEventListener('pointermove', onMove);\n handle.addEventListener('pointerup', onEnd);\n handle.addEventListener('pointercancel', onEnd);\n handle.addEventListener('lostpointercapture', onEnd);\n },\n destroy(): void {\n cleanupListeners();\n pane = null;\n handle = null;\n },\n };\n}\n","import { InjectionToken, Provider } from '@angular/core';\n\n/**\n * Valeur d'un palier de breakpoint, CONFIGURABLE PAR APPLICATION :\n * - un **nombre** = largeur (px) à partir de laquelle le palier commence ; la lib en dérive une\n * media query LARGEUR SEULE (bornes calculées par rapport au palier suivant) ;\n * - une **chaîne** = media query complète du palier, utilisée VERBATIM (échappatoire pour un critère\n * non-largeur : `orientation`, `pointer`, ratio…).\n */\nexport type KtBreakpointValue = number | string;\n\n/**\n * Seuils responsive de la lib, surchargeables par appli via `provideKtBreakpoints`. Sémantique\n * « plancher de palier » : chaque valeur indique où le palier COMMENCE → 3 bandes mutuellement\n * exclusives (`mobile` < `tablet` < `desktop`).\n */\nexport interface KtBreakpoints {\n /** Plancher du mobile (typiquement 0). */\n mobile: KtBreakpointValue;\n /** Largeur où commence la tablette (= plafond mobile). */\n tablet: KtBreakpointValue;\n /** Largeur où commence le desktop (= plafond tablette). */\n desktop: KtBreakpointValue;\n}\n\n/** Défauts : bascule bottom-sheet sous 600px (aligné sur l'ancien `MOBILE_QUERY`). */\nexport const KT_DEFAULT_BREAKPOINTS: KtBreakpoints = { mobile: 0, tablet: 600, desktop: 1024 };\n\n/** Token des seuils responsive. Défaut = `KT_DEFAULT_BREAKPOINTS` ; surchargé par `provideKtBreakpoints`. */\nexport const KT_BREAKPOINTS = new InjectionToken<KtBreakpoints>('KT_BREAKPOINTS', {\n providedIn: 'root',\n factory: () => KT_DEFAULT_BREAKPOINTS,\n});\n\n/**\n * À ajouter aux `providers` de `app.config.ts`. Fusionne avec les défauts → surcharge partielle OK.\n * @example provideKtBreakpoints({ tablet: 768, desktop: 1200 })\n * @example provideKtBreakpoints({ tablet: '(min-width: 768px) and (orientation: landscape)' })\n */\nexport function provideKtBreakpoints(overrides: Partial<KtBreakpoints>): Provider {\n return { provide: KT_BREAKPOINTS, useValue: { ...KT_DEFAULT_BREAKPOINTS, ...overrides } };\n}\n\n/** Trois chaînes de media queries résolues, prêtes pour `matchMedia` / `BreakpointObserver`. */\nexport interface KtBreakpointMedia {\n mobile: string;\n tablet: string;\n desktop: string;\n}\n\n/** Évite le chevauchement d'1px entre deux bandes adjacentes (max-width juste sous le plancher suivant). */\nconst EPSILON = 0.02;\n\nfunction minWidth(v: number): string {\n return `(min-width: ${v}px)`;\n}\n\nfunction maxWidth(nextFloor: number): string {\n return `(max-width: ${nextFloor - EPSILON}px)`;\n}\n\n/** `not all` ne matche jamais : repli sûr (= desktop) si une bande numérique se retrouve sans borne. */\nfunction join(...parts: string[]): string {\n return parts.filter(Boolean).join(' and ') || 'not all';\n}\n\n/**\n * Convertit les seuils (nombres et/ou chaînes) en 3 media queries. Un palier-chaîne est repris\n * verbatim ; un palier-nombre devient la bande `[plancher courant .. plancher suivant[`. Si le\n * plancher suivant est une chaîne (pas de borne numérique), la bande reste ouverte de ce côté.\n */\nexport function resolveKtBreakpointMedia(bp: KtBreakpoints): KtBreakpointMedia {\n const { mobile, tablet, desktop } = bp;\n\n return {\n mobile:\n typeof mobile === 'string'\n ? mobile\n : join(mobile > 0 ? minWidth(mobile) : '', typeof tablet === 'number' ? maxWidth(tablet) : ''),\n tablet:\n typeof tablet === 'string'\n ? tablet\n : join(minWidth(tablet), typeof desktop === 'number' ? maxWidth(desktop) : ''),\n desktop: typeof desktop === 'string' ? desktop : minWidth(desktop),\n };\n}\n","import { Injectable, Signal, inject } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { BreakpointObserver } from '@angular/cdk/layout';\nimport { map } from 'rxjs';\nimport { KT_BREAKPOINTS, resolveKtBreakpointMedia } from './breakpoints';\n\n/**\n * Expose l'état responsive courant en SIGNALS, dérivé des seuils configurés par l'appli\n * (`KT_BREAKPOINTS` / `provideKtBreakpoints`). Fondé sur `BreakpointObserver` (`@angular/cdk/layout`,\n * déjà dépendance) : SSR-safe (`matches: false` côté serveur → desktop par défaut) et nettoyage des\n * listeners géré par le CDK.\n *\n * À injecter aussi bien dans les composants de la lib (bascule bottom-sheet) que dans le code des\n * applis consommatrices pour leur propre layout responsive.\n */\n@Injectable({ providedIn: 'root' })\nexport class KtViewport {\n private readonly observer = inject(BreakpointObserver);\n\n /** Media queries résolues (largeur dérivée ou verbatim selon le token) — utile pour miroir CSS. */\n readonly media = resolveKtBreakpointMedia(inject(KT_BREAKPOINTS));\n\n /** Le viewport est dans la bande mobile. */\n readonly isMobile = this.matches(this.media.mobile);\n /** Le viewport est dans la bande tablette. */\n readonly isTablet = this.matches(this.media.tablet);\n /** Le viewport est dans la bande desktop. */\n readonly isDesktop = this.matches(this.media.desktop);\n /** Alias de `isMobile` : palier où les composants de la lib basculent en bottom-sheet. */\n readonly isCompact = this.isMobile;\n\n private matches(query: string): Signal<boolean> {\n return toSignal(this.observer.observe(query).pipe(map((state) => state.matches)), {\n initialValue: false,\n });\n }\n}\n","import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class KtIdGenerator {\n private readonly counters = new Map<string, number>();\n private nextGlobalId = 0;\n\n generateId(prefix?: string): number {\n if (!prefix) {\n return this.nextGlobalId++;\n }\n const current = this.counters.get(prefix) ?? 0;\n this.counters.set(prefix, current + 1);\n return current;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|