@starterlib/ui 0.1.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.
@@ -0,0 +1,1353 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, input, computed, ChangeDetectionStrategy, Component, ElementRef, DestroyRef, model, output, signal, LOCALE_ID, afterNextRender, isDevMode } from '@angular/core';
3
+ import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
+ import { masquerIban, masquerTelephone, masquerCarte, quatreDerniers, formaterIban, formaterMontant } from '@starterlib/util';
5
+
6
+ /**
7
+ * Chemin du sprite SVG des icones. Chaque SPA le remplace par son actif
8
+ * empreinte (`/assets/icones/sprite.<hash>.svg`) pour beneficier d'un cache
9
+ * immuable. Le defaut vise le chemin non empreinte, utilisable en
10
+ * developpement.
11
+ */
12
+ const CHEMIN_DU_SPRITE = new InjectionToken('st.chemin-du-sprite', {
13
+ providedIn: 'root',
14
+ factory: () => '/assets/icones/sprite.svg',
15
+ });
16
+ /**
17
+ * Marque la presence d'une region vivante possedee par un ancetre, en pratique
18
+ * la directive `stState`.
19
+ *
20
+ * Tout composant susceptible de porter un `role="status"` l'injecte en
21
+ * optionnel et **se tait** s'il en trouve un. Sans cela, un indicateur de
22
+ * chargement annonce dans une region qui annonce deja, et le lecteur d'ecran
23
+ * lit deux fois. Le defaut se voit seulement en usage reel, jamais en revue.
24
+ */
25
+ const REGION_VIVANTE_PARENTE = new InjectionToken('st.region-vivante-parente');
26
+ /**
27
+ * Fuseau employe pour rendre les dates. Explicite parce qu'un rendu serveur et
28
+ * un rendu client qui ne s'accordent pas sur le fuseau produisent deux textes
29
+ * differents, et l'hydratation casse. Le SSR est ecarte en version 1, mais la
30
+ * compatibilite reste gardee.
31
+ */
32
+ const FUSEAU_PAR_DEFAUT = new InjectionToken('st.fuseau-par-defaut', {
33
+ providedIn: 'root',
34
+ factory: () => 'Europe/Paris',
35
+ });
36
+
37
+ const ETATS_DE_BLOC = [
38
+ 'chargement',
39
+ 'erreur',
40
+ 'vide',
41
+ 'hors-ligne',
42
+ 'succes',
43
+ ];
44
+
45
+ /**
46
+ * Icone tiree d'un sprite SVG servi en meme origine.
47
+ *
48
+ * Pourquoi un sprite et une reference externe plutot qu'une insertion : poser
49
+ * du SVG dans le document demanderait `innerHTML`, interdit dans ce depot, et
50
+ * une police d'icones rend un caractere arbitraire aux lecteurs d'ecran quand
51
+ * la police ne charge pas.
52
+ *
53
+ * Une icone est **decorative par defaut**. Regle de tri : si la retirer ne
54
+ * retire aucune information, elle est decorative. Une icone posee a cote d'un
55
+ * texte qui dit la meme chose l'est toujours.
56
+ */
57
+ class StIcon {
58
+ cheminDuSprite = inject(CHEMIN_DU_SPRITE);
59
+ nom = input.required(/* @ts-ignore */
60
+ ...(ngDevMode ? [{ debugName: "nom" }] : /* istanbul ignore next */ []));
61
+ taille = input('md', /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
63
+ ton = input('courant', /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
65
+ /** `null` : l'icone est decorative et reste muette pour les lecteurs. */
66
+ etiquette = input(null, /* @ts-ignore */
67
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
68
+ estDecorative = computed(() => this.etiquette() === null, /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "estDecorative" }] : /* istanbul ignore next */ []));
70
+ reference = computed(() => `${this.cheminDuSprite}#${this.nom()}`, /* @ts-ignore */
71
+ ...(ngDevMode ? [{ debugName: "reference" }] : /* istanbul ignore next */ []));
72
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
73
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StIcon, isStandalone: true, selector: "st-icon", inputs: { nom: { classPropertyName: "nom", publicName: "nom", isSignal: true, isRequired: true, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-taille": "taille()", "attr.data-ton": "ton()" }, classAttribute: "st-icon" }, ngImport: i0, template: "<svg\n [attr.aria-hidden]=\"estDecorative() ? 'true' : null\"\n [attr.role]=\"estDecorative() ? null : 'img'\"\n [attr.aria-label]=\"etiquette()\"\n focusable=\"false\"\n>\n <use [attr.href]=\"reference()\" />\n</svg>\n", styles: [":host{display:inline-flex;flex:none;vertical-align:-.125em;color:var(--st-couleur-texte)}svg{inline-size:var(--st-icone-taille, 1.25em);block-size:var(--st-icone-taille, 1.25em)}:host([data-taille=xs]) svg{--st-icone-taille: .75em}:host([data-taille=sm]) svg{--st-icone-taille: 1em}:host([data-taille=lg]) svg{--st-icone-taille: 1.5em}:host([data-taille=xl]) svg{--st-icone-taille: 2em}:host([data-ton=discret]){color:var(--st-couleur-texte-discret)}:host([data-ton=primaire]){color:var(--st-couleur-primaire)}:host([data-ton=succes]){color:var(--st-couleur-succes)}:host([data-ton=avertissement]){color:var(--st-couleur-avertissement)}:host([data-ton=danger]){color:var(--st-couleur-danger)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
74
+ }
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StIcon, decorators: [{
76
+ type: Component,
77
+ args: [{ selector: 'st-icon', changeDetection: ChangeDetectionStrategy.OnPush, host: {
78
+ class: 'st-icon',
79
+ '[attr.data-taille]': 'taille()',
80
+ '[attr.data-ton]': 'ton()',
81
+ }, template: "<svg\n [attr.aria-hidden]=\"estDecorative() ? 'true' : null\"\n [attr.role]=\"estDecorative() ? null : 'img'\"\n [attr.aria-label]=\"etiquette()\"\n focusable=\"false\"\n>\n <use [attr.href]=\"reference()\" />\n</svg>\n", styles: [":host{display:inline-flex;flex:none;vertical-align:-.125em;color:var(--st-couleur-texte)}svg{inline-size:var(--st-icone-taille, 1.25em);block-size:var(--st-icone-taille, 1.25em)}:host([data-taille=xs]) svg{--st-icone-taille: .75em}:host([data-taille=sm]) svg{--st-icone-taille: 1em}:host([data-taille=lg]) svg{--st-icone-taille: 1.5em}:host([data-taille=xl]) svg{--st-icone-taille: 2em}:host([data-ton=discret]){color:var(--st-couleur-texte-discret)}:host([data-ton=primaire]){color:var(--st-couleur-primaire)}:host([data-ton=succes]){color:var(--st-couleur-succes)}:host([data-ton=avertissement]){color:var(--st-couleur-avertissement)}:host([data-ton=danger]){color:var(--st-couleur-danger)}\n"] }]
82
+ }], propDecorators: { nom: [{ type: i0.Input, args: [{ isSignal: true, alias: "nom", required: true }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }] } });
83
+
84
+ /**
85
+ * Noms du sprite, engendres depuis `assets/icones/sprite.svg`.
86
+ *
87
+ * Le type existe pour qu'une faute de frappe echoue a la compilation : une
88
+ * reference `use` vers un identifiant absent ne rend RIEN, sans erreur, et le
89
+ * defaut passe la revue.
90
+ */
91
+ const NOMS_DICONE = [
92
+ 'boite-vide',
93
+ 'cercle',
94
+ 'cercle-coche',
95
+ 'cercle-croix',
96
+ 'cercle-info',
97
+ 'chevron-bas',
98
+ 'chevron-droite',
99
+ 'chevron-gauche',
100
+ 'chevron-haut',
101
+ 'coche',
102
+ 'copie',
103
+ 'corbeille',
104
+ 'croix',
105
+ 'hors-ligne',
106
+ 'lien-externe',
107
+ 'menu',
108
+ 'oeil',
109
+ 'oeil-barre',
110
+ 'plus',
111
+ 'rafraichir',
112
+ 'recherche',
113
+ 'serveur',
114
+ 'triangle-alerte',
115
+ 'utilisateur',
116
+ ];
117
+
118
+ /**
119
+ * Bloc de texte de l'echelle typographique.
120
+ *
121
+ * L'hote EST la boite : aucun enveloppeur interne, et jamais
122
+ * `display: contents`, qui retire l'element de l'arbre d'accessibilite dans
123
+ * plusieurs navigateurs.
124
+ */
125
+ class StText {
126
+ variante = input('corps', /* @ts-ignore */
127
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
128
+ graisse = input('normale', /* @ts-ignore */
129
+ ...(ngDevMode ? [{ debugName: "graisse" }] : /* istanbul ignore next */ []));
130
+ enLigne = input(false, /* @ts-ignore */
131
+ ...(ngDevMode ? [{ debugName: "enLigne" }] : /* istanbul ignore next */ []));
132
+ /**
133
+ * Nombre de lignes au-dela duquel le texte est coupe.
134
+ *
135
+ * ⚠️ A ne jamais employer sur un message d'erreur, un statut ou un libelle
136
+ * d'action : une information tronquee y devient fausse.
137
+ */
138
+ tronquerA = input(null, /* @ts-ignore */
139
+ ...(ngDevMode ? [{ debugName: "tronquerA" }] : /* istanbul ignore next */ []));
140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StText, deps: [], target: i0.ɵɵFactoryTarget.Component });
141
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StText, isStandalone: true, selector: "st-text", inputs: { variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, graisse: { classPropertyName: "graisse", publicName: "graisse", isSignal: true, isRequired: false, transformFunction: null }, enLigne: { classPropertyName: "enLigne", publicName: "enLigne", isSignal: true, isRequired: false, transformFunction: null }, tronquerA: { classPropertyName: "tronquerA", publicName: "tronquerA", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variante": "variante()", "attr.data-graisse": "graisse()", "attr.data-en-ligne": "enLigne() ? \"\" : null", "attr.data-tronque": "tronquerA() === null ? null : \"\"", "style.--st-text-lignes": "tronquerA()" }, classAttribute: "st-text" }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:block;margin:0;font-family:var(--st-police-base);font-size:var(--st-taille-md);font-weight:var(--st-graisse-normale);line-height:var(--st-interligne-normal);color:var(--st-couleur-texte);max-inline-size:70ch}:host([data-en-ligne]){display:inline;max-inline-size:none}:host([data-variante=corps-sm]){font-size:var(--st-taille-sm)}:host([data-variante=discret]){color:var(--st-couleur-texte-discret)}:host([data-variante=legende]){font-size:var(--st-taille-xs);color:var(--st-couleur-texte-discret)}:host([data-variante=mono]){font-family:var(--st-police-mono);font-size:var(--st-taille-sm)}:host([data-graisse=moyenne]){font-weight:var(--st-graisse-moyenne)}:host([data-graisse=semi]){font-weight:var(--st-graisse-semi)}:host([data-tronque]){display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:var(--st-text-lignes, 2);line-clamp:var(--st-text-lignes, 2)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
142
+ }
143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StText, decorators: [{
144
+ type: Component,
145
+ args: [{ selector: 'st-text', changeDetection: ChangeDetectionStrategy.OnPush, host: {
146
+ class: 'st-text',
147
+ '[attr.data-variante]': 'variante()',
148
+ '[attr.data-graisse]': 'graisse()',
149
+ '[attr.data-en-ligne]': 'enLigne() ? "" : null',
150
+ '[attr.data-tronque]': 'tronquerA() === null ? null : ""',
151
+ '[style.--st-text-lignes]': 'tronquerA()',
152
+ }, template: "<ng-content />\n", styles: [":host{display:block;margin:0;font-family:var(--st-police-base);font-size:var(--st-taille-md);font-weight:var(--st-graisse-normale);line-height:var(--st-interligne-normal);color:var(--st-couleur-texte);max-inline-size:70ch}:host([data-en-ligne]){display:inline;max-inline-size:none}:host([data-variante=corps-sm]){font-size:var(--st-taille-sm)}:host([data-variante=discret]){color:var(--st-couleur-texte-discret)}:host([data-variante=legende]){font-size:var(--st-taille-xs);color:var(--st-couleur-texte-discret)}:host([data-variante=mono]){font-family:var(--st-police-mono);font-size:var(--st-taille-sm)}:host([data-graisse=moyenne]){font-weight:var(--st-graisse-moyenne)}:host([data-graisse=semi]){font-weight:var(--st-graisse-semi)}:host([data-tronque]){display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:var(--st-text-lignes, 2);line-clamp:var(--st-text-lignes, 2)}\n"] }]
153
+ }], propDecorators: { variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], graisse: [{ type: i0.Input, args: [{ isSignal: true, alias: "graisse", required: false }] }], enLigne: [{ type: i0.Input, args: [{ isSignal: true, alias: "enLigne", required: false }] }], tronquerA: [{ type: i0.Input, args: [{ isSignal: true, alias: "tronquerA", required: false }] }] } });
154
+
155
+ const TAILLE_PAR_NIVEAU = {
156
+ 1: '3xl',
157
+ 2: '2xl',
158
+ 3: 'xl',
159
+ 4: 'lg',
160
+ 5: 'lg',
161
+ 6: 'lg',
162
+ };
163
+ /**
164
+ * Titre de section.
165
+ *
166
+ * `niveau` decrit la STRUCTURE du document, `taille` son APPARENCE. Les deux
167
+ * sont volontairement dissocies : c'est ce qui empeche de choisir un niveau 4
168
+ * parce qu'on le trouve plus petit, en cassant au passage la table des
169
+ * matieres que les lecteurs d'ecran construisent.
170
+ *
171
+ * L'hote porte `role="heading"` et `aria-level` plutot qu'une bascule sur six
172
+ * balises : cela demanderait six projections de contenu, fragiles, pour un
173
+ * resultat que les lecteurs d'ecran traitent de facon identique.
174
+ */
175
+ class StHeading {
176
+ niveau = input.required(/* @ts-ignore */
177
+ ...(ngDevMode ? [{ debugName: "niveau" }] : /* istanbul ignore next */ []));
178
+ taille = input(null, /* @ts-ignore */
179
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
180
+ /** Cible d'un `aria-labelledby` : une region se nomme par son titre. */
181
+ identifiant = input(null, /* @ts-ignore */
182
+ ...(ngDevMode ? [{ debugName: "identifiant" }] : /* istanbul ignore next */ []));
183
+ tailleEffective = computed(() => this.taille() ?? TAILLE_PAR_NIVEAU[this.niveau()], /* @ts-ignore */
184
+ ...(ngDevMode ? [{ debugName: "tailleEffective" }] : /* istanbul ignore next */ []));
185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StHeading, deps: [], target: i0.ɵɵFactoryTarget.Component });
186
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StHeading, isStandalone: true, selector: "st-heading", inputs: { niveau: { classPropertyName: "niveau", publicName: "niveau", isSignal: true, isRequired: true, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, identifiant: { classPropertyName: "identifiant", publicName: "identifiant", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "heading" }, properties: { "attr.aria-level": "niveau()", "attr.id": "identifiant()", "attr.data-taille": "tailleEffective()" }, classAttribute: "st-heading" }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:block;margin:0 0 var(--st-espacement-3);font-family:var(--st-police-base);font-weight:var(--st-graisse-semi);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);text-wrap:balance}:host([data-taille=lg]){font-size:var(--st-taille-lg)}:host([data-taille=xl]){font-size:var(--st-taille-xl)}:host([data-taille=\"2xl\"]){font-size:var(--st-taille-2xl)}:host([data-taille=\"3xl\"]){font-size:var(--st-taille-3xl)}:host([data-taille=\"4xl\"]){font-size:var(--st-taille-4xl)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
187
+ }
188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StHeading, decorators: [{
189
+ type: Component,
190
+ args: [{ selector: 'st-heading', changeDetection: ChangeDetectionStrategy.OnPush, host: {
191
+ class: 'st-heading',
192
+ role: 'heading',
193
+ '[attr.aria-level]': 'niveau()',
194
+ '[attr.id]': 'identifiant()',
195
+ '[attr.data-taille]': 'tailleEffective()',
196
+ }, template: "<ng-content />\n", styles: [":host{display:block;margin:0 0 var(--st-espacement-3);font-family:var(--st-police-base);font-weight:var(--st-graisse-semi);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);text-wrap:balance}:host([data-taille=lg]){font-size:var(--st-taille-lg)}:host([data-taille=xl]){font-size:var(--st-taille-xl)}:host([data-taille=\"2xl\"]){font-size:var(--st-taille-2xl)}:host([data-taille=\"3xl\"]){font-size:var(--st-taille-3xl)}:host([data-taille=\"4xl\"]){font-size:var(--st-taille-4xl)}\n"] }]
197
+ }], propDecorators: { niveau: [{ type: i0.Input, args: [{ isSignal: true, alias: "niveau", required: true }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], identifiant: [{ type: i0.Input, args: [{ isSignal: true, alias: "identifiant", required: false }] }] } });
198
+
199
+ /**
200
+ * Indicateur d'activite indeterminee.
201
+ *
202
+ * ⚠️ Deux pieges tenus ici, et aucun des deux ne se voit en revue :
203
+ *
204
+ * 1. Il n'emploie AUCUNE duree de l'echelle de jetons. Sous
205
+ * `prefers-reduced-motion`, `--st-duree-*` passe a zero : un spinner cable
206
+ * dessus se figerait, et l'ecran passerait pour bloque. Il declare sa propre
207
+ * duree et bascule en pulsation d'opacite, qui n'a pas de composante
208
+ * vestibulaire.
209
+ * 2. Il ne s'annonce que s'il n'est PAS deja dans une region vivante. Sinon le
210
+ * lecteur d'ecran annonce deux fois le meme chargement.
211
+ */
212
+ class StSpinner {
213
+ regionParente = inject(REGION_VIVANTE_PARENTE, { optional: true });
214
+ taille = input('md', /* @ts-ignore */
215
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
216
+ ton = input('primaire', /* @ts-ignore */
217
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
218
+ etiquette = input('Chargement en cours', /* @ts-ignore */
219
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
220
+ sAnnonce = computed(() => this.regionParente === null, /* @ts-ignore */
221
+ ...(ngDevMode ? [{ debugName: "sAnnonce" }] : /* istanbul ignore next */ []));
222
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSpinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
223
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StSpinner, isStandalone: true, selector: "st-spinner", inputs: { taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-taille": "taille()", "attr.data-ton": "ton()", "attr.role": "sAnnonce() ? \"status\" : null", "attr.aria-label": "sAnnonce() ? etiquette() : null" }, classAttribute: "st-spinner" }, ngImport: i0, template: "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <circle class=\"st-spinner__piste\" cx=\"12\" cy=\"12\" r=\"10\" />\n <circle class=\"st-spinner__arc\" cx=\"12\" cy=\"12\" r=\"10\" />\n</svg>\n", styles: [":host{display:inline-flex;flex:none;color:var(--st-couleur-primaire)}svg{inline-size:var(--st-spinner-taille, 1.5rem);block-size:var(--st-spinner-taille, 1.5rem);fill:none;stroke-width:2.5;transform-origin:center;animation:st-spinner-rotation var(--st-anim-chargement, .9s) linear infinite}.st-spinner__piste{stroke:var(--st-couleur-bordure)}.st-spinner__arc{stroke:currentcolor;stroke-linecap:round;stroke-dasharray:16 47}@keyframes st-spinner-rotation{to{transform:rotate(1turn)}}@keyframes st-spinner-pulsation{0%,to{opacity:1}50%{opacity:.35}}@media(prefers-reduced-motion:reduce){svg{animation:st-spinner-pulsation 1.6s ease-in-out infinite}.st-spinner__arc{stroke-dasharray:none}}:host([data-taille=sm]) svg{--st-spinner-taille: 1rem}:host([data-taille=lg]) svg{--st-spinner-taille: 2.5rem}:host([data-ton=courant]){color:currentcolor}:host([data-ton=inverse]){color:var(--st-couleur-sur-primaire)}:host([data-ton=inverse]) .st-spinner__piste{stroke:color-mix(in srgb,var(--st-couleur-sur-primaire) 30%,transparent)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
224
+ }
225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSpinner, decorators: [{
226
+ type: Component,
227
+ args: [{ selector: 'st-spinner', changeDetection: ChangeDetectionStrategy.OnPush, host: {
228
+ class: 'st-spinner',
229
+ '[attr.data-taille]': 'taille()',
230
+ '[attr.data-ton]': 'ton()',
231
+ '[attr.role]': 'sAnnonce() ? "status" : null',
232
+ '[attr.aria-label]': 'sAnnonce() ? etiquette() : null',
233
+ }, template: "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <circle class=\"st-spinner__piste\" cx=\"12\" cy=\"12\" r=\"10\" />\n <circle class=\"st-spinner__arc\" cx=\"12\" cy=\"12\" r=\"10\" />\n</svg>\n", styles: [":host{display:inline-flex;flex:none;color:var(--st-couleur-primaire)}svg{inline-size:var(--st-spinner-taille, 1.5rem);block-size:var(--st-spinner-taille, 1.5rem);fill:none;stroke-width:2.5;transform-origin:center;animation:st-spinner-rotation var(--st-anim-chargement, .9s) linear infinite}.st-spinner__piste{stroke:var(--st-couleur-bordure)}.st-spinner__arc{stroke:currentcolor;stroke-linecap:round;stroke-dasharray:16 47}@keyframes st-spinner-rotation{to{transform:rotate(1turn)}}@keyframes st-spinner-pulsation{0%,to{opacity:1}50%{opacity:.35}}@media(prefers-reduced-motion:reduce){svg{animation:st-spinner-pulsation 1.6s ease-in-out infinite}.st-spinner__arc{stroke-dasharray:none}}:host([data-taille=sm]) svg{--st-spinner-taille: 1rem}:host([data-taille=lg]) svg{--st-spinner-taille: 2.5rem}:host([data-ton=courant]){color:currentcolor}:host([data-ton=inverse]){color:var(--st-couleur-sur-primaire)}:host([data-ton=inverse]) .st-spinner__piste{stroke:color-mix(in srgb,var(--st-couleur-sur-primaire) 30%,transparent)}\n"] }]
234
+ }], propDecorators: { taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }] } });
235
+
236
+ /**
237
+ * Barre de progression, determinee ou indeterminee.
238
+ *
239
+ * ⚠️ Elle n'est JAMAIS placee dans une region vivante : `role="progressbar"`
240
+ * avec `aria-valuenow` est deja expose aux technologies d'assistance, et une
241
+ * region vivante par-dessus ferait annoncer chaque pourcent.
242
+ */
243
+ class StProgress {
244
+ /** `null` : progression indeterminee. */
245
+ valeur = input(null, /* @ts-ignore */
246
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
247
+ max = input(100, /* @ts-ignore */
248
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
249
+ /** Obligatoire : une barre sans nom accessible n'apprend rien. */
250
+ etiquette = input.required(/* @ts-ignore */
251
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
252
+ ton = input('primaire', /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
254
+ /** Texte lu a la place du pourcentage quand celui-ci n'est pas parlant. */
255
+ texteDeValeur = input(null, /* @ts-ignore */
256
+ ...(ngDevMode ? [{ debugName: "texteDeValeur" }] : /* istanbul ignore next */ []));
257
+ estIndeterminee = computed(() => this.valeur() === null, /* @ts-ignore */
258
+ ...(ngDevMode ? [{ debugName: "estIndeterminee" }] : /* istanbul ignore next */ []));
259
+ /** Omis quand la progression est indeterminee : l'annoncer serait faux. */
260
+ valeurAnnoncee = computed(() => this.valeur(), /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "valeurAnnoncee" }] : /* istanbul ignore next */ []));
262
+ pourcentage = computed(() => {
263
+ const valeur = this.valeur();
264
+ if (valeur === null)
265
+ return 0;
266
+ const max = this.max();
267
+ if (max <= 0)
268
+ return 0;
269
+ // Borne : une valeur hors plage rendrait une barre plus large que sa piste.
270
+ return Math.min(100, Math.max(0, (valeur / max) * 100));
271
+ }, /* @ts-ignore */
272
+ ...(ngDevMode ? [{ debugName: "pourcentage" }] : /* istanbul ignore next */ []));
273
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StProgress, deps: [], target: i0.ɵɵFactoryTarget.Component });
274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StProgress, isStandalone: true, selector: "st-progress", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: true, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, texteDeValeur: { classPropertyName: "texteDeValeur", publicName: "texteDeValeur", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "progressbar" }, properties: { "attr.data-ton": "ton()", "attr.data-indetermine": "estIndeterminee() ? \"\" : null", "attr.aria-label": "etiquette()", "attr.aria-valuemin": "0", "attr.aria-valuemax": "max()", "attr.aria-valuenow": "valeurAnnoncee()", "attr.aria-valuetext": "texteDeValeur()", "style.--st-progress-part.%": "pourcentage()" }, classAttribute: "st-progress" }, ngImport: i0, template: "<span class=\"st-progress__barre\"></span>\n", styles: [":host{display:block;block-size:.5rem;overflow:hidden;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-pleine)}.st-progress__barre{display:block;block-size:100%;inline-size:calc(var(--st-progress-part, 0) * 1%);background:var(--st-couleur-primaire);border-radius:inherit;transition:inline-size var(--st-duree-normale) var(--st-courbe-standard)}:host([data-ton=succes]) .st-progress__barre{background:var(--st-couleur-succes)}:host([data-ton=danger]) .st-progress__barre{background:var(--st-couleur-danger)}:host([data-indetermine]) .st-progress__barre{inline-size:35%;animation:st-progress-va-et-vient var(--st-anim-chargement, 1.4s) ease-in-out infinite}@keyframes st-progress-va-et-vient{0%{transform:translate(-100%)}to{transform:translate(calc(100cqi + 100%))}}@keyframes st-progress-pulsation{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){:host([data-indetermine]) .st-progress__barre{inline-size:100%;transform:none;animation:st-progress-pulsation 1.6s ease-in-out infinite}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
275
+ }
276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StProgress, decorators: [{
277
+ type: Component,
278
+ args: [{ selector: 'st-progress', changeDetection: ChangeDetectionStrategy.OnPush, host: {
279
+ class: 'st-progress',
280
+ role: 'progressbar',
281
+ '[attr.data-ton]': 'ton()',
282
+ '[attr.data-indetermine]': 'estIndeterminee() ? "" : null',
283
+ '[attr.aria-label]': 'etiquette()',
284
+ '[attr.aria-valuemin]': '0',
285
+ '[attr.aria-valuemax]': 'max()',
286
+ '[attr.aria-valuenow]': 'valeurAnnoncee()',
287
+ '[attr.aria-valuetext]': 'texteDeValeur()',
288
+ '[style.--st-progress-part.%]': 'pourcentage()',
289
+ }, template: "<span class=\"st-progress__barre\"></span>\n", styles: [":host{display:block;block-size:.5rem;overflow:hidden;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-pleine)}.st-progress__barre{display:block;block-size:100%;inline-size:calc(var(--st-progress-part, 0) * 1%);background:var(--st-couleur-primaire);border-radius:inherit;transition:inline-size var(--st-duree-normale) var(--st-courbe-standard)}:host([data-ton=succes]) .st-progress__barre{background:var(--st-couleur-succes)}:host([data-ton=danger]) .st-progress__barre{background:var(--st-couleur-danger)}:host([data-indetermine]) .st-progress__barre{inline-size:35%;animation:st-progress-va-et-vient var(--st-anim-chargement, 1.4s) ease-in-out infinite}@keyframes st-progress-va-et-vient{0%{transform:translate(-100%)}to{transform:translate(calc(100cqi + 100%))}}@keyframes st-progress-pulsation{0%,to{opacity:1}50%{opacity:.4}}@media(prefers-reduced-motion:reduce){:host([data-indetermine]) .st-progress__barre{inline-size:100%;transform:none;animation:st-progress-pulsation 1.6s ease-in-out infinite}}\n"] }]
290
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: true }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], texteDeValeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "texteDeValeur", required: false }] }] } });
291
+
292
+ /**
293
+ * Silhouette du contenu en cours de chargement.
294
+ *
295
+ * Entierement masque aux technologies d'assistance : c'est `stState` qui
296
+ * annonce le chargement, une fois. Un squelette annonce ligne par ligne
297
+ * noierait le lecteur d'ecran.
298
+ *
299
+ * Il doit reproduire la BOITE du contenu reel, sinon l'arrivee des donnees
300
+ * fait sauter la mise en page.
301
+ */
302
+ class StSkeleton {
303
+ variante = input('texte', /* @ts-ignore */
304
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
305
+ lignes = input(3, /* @ts-ignore */
306
+ ...(ngDevMode ? [{ debugName: "lignes" }] : /* istanbul ignore next */ []));
307
+ colonnes = input(4, /* @ts-ignore */
308
+ ...(ngDevMode ? [{ debugName: "colonnes" }] : /* istanbul ignore next */ []));
309
+ anime = input(true, /* @ts-ignore */
310
+ ...(ngDevMode ? [{ debugName: "anime" }] : /* istanbul ignore next */ []));
311
+ indicesDeLigne = computed(() => Array.from({ length: Math.max(1, this.lignes()) }, (_, index) => index), /* @ts-ignore */
312
+ ...(ngDevMode ? [{ debugName: "indicesDeLigne" }] : /* istanbul ignore next */ []));
313
+ indicesDeColonne = computed(() => Array.from({ length: Math.max(1, this.colonnes()) }, (_, index) => index), /* @ts-ignore */
314
+ ...(ngDevMode ? [{ debugName: "indicesDeColonne" }] : /* istanbul ignore next */ []));
315
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
316
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StSkeleton, isStandalone: true, selector: "st-skeleton", inputs: { variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, lignes: { classPropertyName: "lignes", publicName: "lignes", isSignal: true, isRequired: false, transformFunction: null }, colonnes: { classPropertyName: "colonnes", publicName: "colonnes", isSignal: true, isRequired: false, transformFunction: null }, anime: { classPropertyName: "anime", publicName: "anime", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-variante": "variante()", "attr.data-anime": "anime() ? \"\" : null" }, classAttribute: "st-skeleton" }, ngImport: i0, template: "@switch (variante()) {\n @case ('tableau') {\n @for (ligne of indicesDeLigne(); track ligne) {\n <div class=\"st-skeleton__rangee\">\n @for (colonne of indicesDeColonne(); track colonne) {\n <span class=\"st-skeleton__cellule\"></span>\n }\n </div>\n }\n }\n @case ('carte') {\n <span class=\"st-skeleton__media\"></span>\n @for (ligne of indicesDeLigne(); track ligne) {\n <span class=\"st-skeleton__ligne\"></span>\n }\n }\n @default {\n @for (ligne of indicesDeLigne(); track ligne) {\n <span class=\"st-skeleton__ligne\"></span>\n }\n }\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--st-espacement-2)}.st-skeleton__media,.st-skeleton__cellule,.st-skeleton__ligne{display:block;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-sm)}.st-skeleton__ligne{block-size:calc(1em * var(--st-interligne-normal))}.st-skeleton__ligne:last-child{inline-size:60%}.st-skeleton__rangee{display:flex;gap:var(--st-espacement-3)}.st-skeleton__cellule{flex:1;block-size:calc(1em * var(--st-interligne-normal))}.st-skeleton__media{block-size:8rem;border-radius:var(--st-rayon-md)}:host([data-anime]) .st-skeleton__ligne,:host([data-anime]) .st-skeleton__cellule,:host([data-anime]) .st-skeleton__media{background:linear-gradient(90deg,var(--st-couleur-surface-enfoncee) 25%,var(--st-couleur-surface-survol) 50%,var(--st-couleur-surface-enfoncee) 75%);background-size:200% 100%;animation:st-skeleton-miroitement var(--st-anim-chargement, 1.4s) linear infinite}@keyframes st-skeleton-miroitement{to{background-position:-200% 0}}@keyframes st-skeleton-pulsation{0%,to{opacity:1}50%{opacity:.5}}@media(prefers-reduced-motion:reduce){:host([data-anime]) .st-skeleton__ligne,:host([data-anime]) .st-skeleton__cellule,:host([data-anime]) .st-skeleton__media{background:var(--st-couleur-surface-enfoncee);animation:st-skeleton-pulsation 1.8s ease-in-out infinite}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
317
+ }
318
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSkeleton, decorators: [{
319
+ type: Component,
320
+ args: [{ selector: 'st-skeleton', changeDetection: ChangeDetectionStrategy.OnPush, host: {
321
+ class: 'st-skeleton',
322
+ 'aria-hidden': 'true',
323
+ '[attr.data-variante]': 'variante()',
324
+ '[attr.data-anime]': 'anime() ? "" : null',
325
+ }, template: "@switch (variante()) {\n @case ('tableau') {\n @for (ligne of indicesDeLigne(); track ligne) {\n <div class=\"st-skeleton__rangee\">\n @for (colonne of indicesDeColonne(); track colonne) {\n <span class=\"st-skeleton__cellule\"></span>\n }\n </div>\n }\n }\n @case ('carte') {\n <span class=\"st-skeleton__media\"></span>\n @for (ligne of indicesDeLigne(); track ligne) {\n <span class=\"st-skeleton__ligne\"></span>\n }\n }\n @default {\n @for (ligne of indicesDeLigne(); track ligne) {\n <span class=\"st-skeleton__ligne\"></span>\n }\n }\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--st-espacement-2)}.st-skeleton__media,.st-skeleton__cellule,.st-skeleton__ligne{display:block;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-sm)}.st-skeleton__ligne{block-size:calc(1em * var(--st-interligne-normal))}.st-skeleton__ligne:last-child{inline-size:60%}.st-skeleton__rangee{display:flex;gap:var(--st-espacement-3)}.st-skeleton__cellule{flex:1;block-size:calc(1em * var(--st-interligne-normal))}.st-skeleton__media{block-size:8rem;border-radius:var(--st-rayon-md)}:host([data-anime]) .st-skeleton__ligne,:host([data-anime]) .st-skeleton__cellule,:host([data-anime]) .st-skeleton__media{background:linear-gradient(90deg,var(--st-couleur-surface-enfoncee) 25%,var(--st-couleur-surface-survol) 50%,var(--st-couleur-surface-enfoncee) 75%);background-size:200% 100%;animation:st-skeleton-miroitement var(--st-anim-chargement, 1.4s) linear infinite}@keyframes st-skeleton-miroitement{to{background-position:-200% 0}}@keyframes st-skeleton-pulsation{0%,to{opacity:1}50%{opacity:.5}}@media(prefers-reduced-motion:reduce){:host([data-anime]) .st-skeleton__ligne,:host([data-anime]) .st-skeleton__cellule,:host([data-anime]) .st-skeleton__media{background:var(--st-couleur-surface-enfoncee);animation:st-skeleton-pulsation 1.8s ease-in-out infinite}}\n"] }]
326
+ }], propDecorators: { variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], lignes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lignes", required: false }] }], colonnes: [{ type: i0.Input, args: [{ isSignal: true, alias: "colonnes", required: false }] }], anime: [{ type: i0.Input, args: [{ isSignal: true, alias: "anime", required: false }] }] } });
327
+
328
+ /**
329
+ * Bouton du socle, pose en ATTRIBUT sur un `<button>` natif.
330
+ *
331
+ * ⚠️ Pourquoi pas un element `<st-button>` qui enveloppe un bouton : l'enveloppe
332
+ * casse `type="submit"` (le bouton ne soumet plus le formulaire ancetre),
333
+ * l'attribut `form` ne porte plus, et l'hote devient un second arret de
334
+ * tabulation si quelqu'un lui pose un `tabindex`. Tout fonctionne en
335
+ * demonstration et casse au premier formulaire reel.
336
+ *
337
+ * ⚠️ Pendant le chargement, le bouton porte `aria-disabled` et NON `disabled` :
338
+ * l'attribut natif lui retire le focus, le lecteur d'ecran perd le controle
339
+ * qu'il avait sous le doigt et retombe sur le corps du document. Les clics
340
+ * sont interceptes par le composant.
341
+ */
342
+ class StButton {
343
+ variante = input('secondaire', /* @ts-ignore */
344
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
345
+ taille = input('md', /* @ts-ignore */
346
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
347
+ pleineLargeur = input(false, /* @ts-ignore */
348
+ ...(ngDevMode ? [{ debugName: "pleineLargeur" }] : /* istanbul ignore next */ []));
349
+ iconeAvant = input(null, /* @ts-ignore */
350
+ ...(ngDevMode ? [{ debugName: "iconeAvant" }] : /* istanbul ignore next */ []));
351
+ iconeApres = input(null, /* @ts-ignore */
352
+ ...(ngDevMode ? [{ debugName: "iconeApres" }] : /* istanbul ignore next */ []));
353
+ /**
354
+ * Action en cours. Tant qu'il vaut vrai, les clics sont avales : c'est le
355
+ * mecanisme d'anti double soumission. L'appelant doit le lever DES la
356
+ * soumission, pas a l'arrivee de la reponse.
357
+ */
358
+ chargement = input(false, /* @ts-ignore */
359
+ ...(ngDevMode ? [{ debugName: "chargement" }] : /* istanbul ignore next */ []));
360
+ constructor() {
361
+ const element = inject(ElementRef).nativeElement;
362
+ const garde = (evenement) => {
363
+ if (!this.chargement())
364
+ return;
365
+ evenement.preventDefault();
366
+ // `stopImmediatePropagation` et non `stopPropagation` : il faut barrer
367
+ // aussi les autres ecouteurs poses sur le MEME element.
368
+ evenement.stopImmediatePropagation();
369
+ };
370
+ // ⚠️ Pose a la construction, et non par `host: { '(click)': ... }`.
371
+ // Sur un meme element, les ecouteurs s'executent dans l'ordre
372
+ // d'ENREGISTREMENT, quelle que soit la phase. Un `(click)` ecrit par
373
+ // l'appelant dans son gabarit serait enregistre avant l'ecouteur d'hote,
374
+ // et la garde ne bloquerait rien. Le constructeur de la directive, lui,
375
+ // s'execute avant que le gabarit parent ne lie ses ecouteurs.
376
+ element.addEventListener('click', garde);
377
+ inject(DestroyRef).onDestroy(() => {
378
+ element.removeEventListener('click', garde);
379
+ });
380
+ }
381
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
382
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StButton, isStandalone: true, selector: "button[stButton]", inputs: { variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, pleineLargeur: { classPropertyName: "pleineLargeur", publicName: "pleineLargeur", isSignal: true, isRequired: false, transformFunction: null }, iconeAvant: { classPropertyName: "iconeAvant", publicName: "iconeAvant", isSignal: true, isRequired: false, transformFunction: null }, iconeApres: { classPropertyName: "iconeApres", publicName: "iconeApres", isSignal: true, isRequired: false, transformFunction: null }, chargement: { classPropertyName: "chargement", publicName: "chargement", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variante": "variante()", "attr.data-taille": "taille()", "attr.data-pleine-largeur": "pleineLargeur() ? \"\" : null", "attr.aria-disabled": "chargement() ? \"true\" : null", "attr.aria-busy": "chargement() ? \"true\" : null" }, classAttribute: "st-button" }, ngImport: i0, template: "@if (chargement()) {\n <st-spinner taille=\"sm\" ton=\"inverse\" />\n} @else if (iconeAvant(); as icone) {\n <st-icon [nom]=\"icone\" taille=\"sm\" />\n}\n\n<span class=\"st-button__libelle\"><ng-content /></span>\n\n@if (!chargement() && iconeApres()) {\n <st-icon [nom]=\"iconeApres()!\" taille=\"sm\" />\n}\n", styles: [".st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}:host{display:inline-flex;gap:var(--st-espacement-2);align-items:center;justify-content:center;min-block-size:var(--st-cible-tactile-min);padding-block:var(--st-espacement-2);padding-inline:var(--st-espacement-4);font-family:var(--st-police-base);font-size:var(--st-taille-md);font-weight:var(--st-graisse-moyenne);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);cursor:pointer;background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-md);transition:background-color var(--st-duree-rapide) var(--st-courbe-standard),border-color var(--st-duree-rapide) var(--st-courbe-standard)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-surface-survol)}:host:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;border-color:var(--st-couleur-bordure)}:host[aria-disabled=true]{cursor:progress}.st-button__libelle{white-space:normal}:host([data-variante=primaire]){color:var(--st-couleur-sur-primaire);background:var(--st-couleur-primaire);border-color:transparent}:host([data-variante=primaire]):focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit;box-shadow:0 0 0 2px var(--st-couleur-surface)}:host([data-variante=primaire]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-primaire-survol)}:host([data-variante=destructeur]){color:var(--st-couleur-sur-danger);background:var(--st-couleur-danger);border-color:transparent}:host([data-variante=destructeur]):focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit;box-shadow:0 0 0 2px var(--st-couleur-surface)}:host([data-variante=destructeur]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-danger-survol)}:host([data-variante=discret]){color:var(--st-couleur-primaire);background:transparent;border-color:transparent}:host([data-variante=discret]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-surface-survol)}:host([data-taille=sm]){min-block-size:2.25rem;padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm)}@media(pointer:coarse){:host([data-taille=sm]){min-block-size:var(--st-cible-tactile-min)}}:host([data-taille=lg]){min-block-size:3.25rem;padding-inline:var(--st-espacement-6);font-size:var(--st-taille-lg)}:host([data-pleine-largeur]){display:flex;inline-size:100%}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }, { kind: "component", type: StSpinner, selector: "st-spinner", inputs: ["taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
383
+ }
384
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StButton, decorators: [{
385
+ type: Component,
386
+ args: [{ selector: 'button[stButton]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon, StSpinner], host: {
387
+ class: 'st-button',
388
+ '[attr.data-variante]': 'variante()',
389
+ '[attr.data-taille]': 'taille()',
390
+ '[attr.data-pleine-largeur]': 'pleineLargeur() ? "" : null',
391
+ '[attr.aria-disabled]': 'chargement() ? "true" : null',
392
+ '[attr.aria-busy]': 'chargement() ? "true" : null',
393
+ }, template: "@if (chargement()) {\n <st-spinner taille=\"sm\" ton=\"inverse\" />\n} @else if (iconeAvant(); as icone) {\n <st-icon [nom]=\"icone\" taille=\"sm\" />\n}\n\n<span class=\"st-button__libelle\"><ng-content /></span>\n\n@if (!chargement() && iconeApres()) {\n <st-icon [nom]=\"iconeApres()!\" taille=\"sm\" />\n}\n", styles: [".st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}:host{display:inline-flex;gap:var(--st-espacement-2);align-items:center;justify-content:center;min-block-size:var(--st-cible-tactile-min);padding-block:var(--st-espacement-2);padding-inline:var(--st-espacement-4);font-family:var(--st-police-base);font-size:var(--st-taille-md);font-weight:var(--st-graisse-moyenne);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);cursor:pointer;background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-md);transition:background-color var(--st-duree-rapide) var(--st-courbe-standard),border-color var(--st-duree-rapide) var(--st-courbe-standard)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-surface-survol)}:host:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;border-color:var(--st-couleur-bordure)}:host[aria-disabled=true]{cursor:progress}.st-button__libelle{white-space:normal}:host([data-variante=primaire]){color:var(--st-couleur-sur-primaire);background:var(--st-couleur-primaire);border-color:transparent}:host([data-variante=primaire]):focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit;box-shadow:0 0 0 2px var(--st-couleur-surface)}:host([data-variante=primaire]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-primaire-survol)}:host([data-variante=destructeur]){color:var(--st-couleur-sur-danger);background:var(--st-couleur-danger);border-color:transparent}:host([data-variante=destructeur]):focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit;box-shadow:0 0 0 2px var(--st-couleur-surface)}:host([data-variante=destructeur]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-danger-survol)}:host([data-variante=discret]){color:var(--st-couleur-primaire);background:transparent;border-color:transparent}:host([data-variante=discret]):hover:not([aria-disabled=true],:disabled){background:var(--st-couleur-surface-survol)}:host([data-taille=sm]){min-block-size:2.25rem;padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm)}@media(pointer:coarse){:host([data-taille=sm]){min-block-size:var(--st-cible-tactile-min)}}:host([data-taille=lg]){min-block-size:3.25rem;padding-inline:var(--st-espacement-6);font-size:var(--st-taille-lg)}:host([data-pleine-largeur]){display:flex;inline-size:100%}\n"] }]
394
+ }], ctorParameters: () => [], propDecorators: { variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], pleineLargeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "pleineLargeur", required: false }] }], iconeAvant: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconeAvant", required: false }] }], iconeApres: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconeApres", required: false }] }], chargement: [{ type: i0.Input, args: [{ isSignal: true, alias: "chargement", required: false }] }] } });
395
+
396
+ /**
397
+ * Bouton reduit a une icone, pose en attribut sur un `<button>` natif.
398
+ *
399
+ * L'etiquette est OBLIGATOIRE : une icone seule n'a pas de nom accessible, et
400
+ * `title` n'en tient pas lieu — il n'apparait pas au toucher et n'est pas lu
401
+ * de facon fiable.
402
+ */
403
+ class StIconButton {
404
+ icone = input.required(/* @ts-ignore */
405
+ ...(ngDevMode ? [{ debugName: "icone" }] : /* istanbul ignore next */ []));
406
+ /** Nom accessible du controle. Sans lui, l'icone ne dit rien. */
407
+ etiquette = input.required(/* @ts-ignore */
408
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
409
+ variante = input('discret', /* @ts-ignore */
410
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
411
+ taille = input('md', /* @ts-ignore */
412
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
413
+ /** `null` : le bouton n'est pas a deux etats et n'annonce aucune pression. */
414
+ enfonce = model(null, /* @ts-ignore */
415
+ ...(ngDevMode ? [{ debugName: "enfonce" }] : /* istanbul ignore next */ []));
416
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StIconButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
417
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StIconButton, isStandalone: true, selector: "button[stIconButton]", inputs: { icone: { classPropertyName: "icone", publicName: "icone", isSignal: true, isRequired: true, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: true, transformFunction: null }, variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, enfonce: { classPropertyName: "enfonce", publicName: "enfonce", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { enfonce: "enfonceChange" }, host: { properties: { "attr.data-variante": "variante()", "attr.data-taille": "taille()", "attr.aria-label": "etiquette()", "attr.aria-pressed": "enfonce() === null ? null : enfonce()" }, classAttribute: "st-icon-button" }, ngImport: i0, template: "<st-icon [nom]=\"icone()\" [taille]=\"taille() === 'sm' ? 'sm' : 'md'\" />\n", styles: [".st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}:host{display:inline-flex;flex:none;align-items:center;justify-content:center;min-inline-size:var(--st-cible-tactile-min);min-block-size:var(--st-cible-tactile-min);color:var(--st-couleur-texte);cursor:pointer;background:transparent;border:1px solid transparent;border-radius:var(--st-rayon-md);transition:background-color var(--st-duree-rapide) var(--st-courbe-standard)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover:not(:disabled){background:var(--st-couleur-surface-survol)}:host:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed}:host[aria-pressed=true]{background:var(--st-couleur-surface-enfoncee)}:host([data-variante=secondaire]){background:var(--st-couleur-surface);border-color:var(--st-couleur-bordure-forte)}:host([data-variante=destructeur]){color:var(--st-couleur-danger)}:host([data-variante=destructeur]):hover:not(:disabled){background:var(--st-couleur-danger-discret)}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
418
+ }
419
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StIconButton, decorators: [{
420
+ type: Component,
421
+ args: [{ selector: 'button[stIconButton]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon], host: {
422
+ class: 'st-icon-button',
423
+ '[attr.data-variante]': 'variante()',
424
+ '[attr.data-taille]': 'taille()',
425
+ '[attr.aria-label]': 'etiquette()',
426
+ '[attr.aria-pressed]': 'enfonce() === null ? null : enfonce()',
427
+ }, template: "<st-icon [nom]=\"icone()\" [taille]=\"taille() === 'sm' ? 'sm' : 'md'\" />\n", styles: [".st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}:host{display:inline-flex;flex:none;align-items:center;justify-content:center;min-inline-size:var(--st-cible-tactile-min);min-block-size:var(--st-cible-tactile-min);color:var(--st-couleur-texte);cursor:pointer;background:transparent;border:1px solid transparent;border-radius:var(--st-rayon-md);transition:background-color var(--st-duree-rapide) var(--st-courbe-standard)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover:not(:disabled){background:var(--st-couleur-surface-survol)}:host:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed}:host[aria-pressed=true]{background:var(--st-couleur-surface-enfoncee)}:host([data-variante=secondaire]){background:var(--st-couleur-surface);border-color:var(--st-couleur-bordure-forte)}:host([data-variante=destructeur]){color:var(--st-couleur-danger)}:host([data-variante=destructeur]):hover:not(:disabled){background:var(--st-couleur-danger-discret)}\n"] }]
428
+ }], propDecorators: { icone: [{ type: i0.Input, args: [{ isSignal: true, alias: "icone", required: true }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: true }] }], variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], enfonce: [{ type: i0.Input, args: [{ isSignal: true, alias: "enfonce", required: false }] }, { type: i0.Output, args: ["enfonceChange"] }] } });
429
+
430
+ /**
431
+ * Lien, pose en attribut sur un `<a>` natif.
432
+ *
433
+ * ⚠️ Un `<a>` sans `href` est interdit : il n'est ni focalisable ni annonce
434
+ * comme lien. Une action qui ne navigue pas est un bouton.
435
+ *
436
+ * Les liens visites ne sont volontairement PAS distingues : sur une interface
437
+ * bancaire, la couleur de visite laisse fuir l'historique de navigation a
438
+ * quiconque regarde l'ecran.
439
+ */
440
+ class StLink {
441
+ variante = input('texte', /* @ts-ignore */
442
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
443
+ /** Ouvre dans un nouvel onglet, et l'annonce dans le nom accessible. */
444
+ externe = input(false, /* @ts-ignore */
445
+ ...(ngDevMode ? [{ debugName: "externe" }] : /* istanbul ignore next */ []));
446
+ /** Mention lue par les lecteurs d'ecran quand le lien sort du site. */
447
+ mentionExterne = input('(nouvelle fenetre)', /* @ts-ignore */
448
+ ...(ngDevMode ? [{ debugName: "mentionExterne" }] : /* istanbul ignore next */ []));
449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StLink, isStandalone: true, selector: "a[stLink]", inputs: { variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, externe: { classPropertyName: "externe", publicName: "externe", isSignal: true, isRequired: false, transformFunction: null }, mentionExterne: { classPropertyName: "mentionExterne", publicName: "mentionExterne", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-variante": "variante()", "attr.target": "externe() ? \"_blank\" : null", "attr.rel": "externe() ? \"noopener noreferrer\" : null" }, classAttribute: "st-link" }, ngImport: i0, template: "<ng-content />\n@if (externe()) {\n <st-icon nom=\"lien-externe\" taille=\"xs\" />\n <span class=\"st-vh\">{{ mentionExterne() }}</span>\n}\n", styles: [":host{display:inline;gap:var(--st-espacement-1);color:var(--st-couleur-primaire);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;border-radius:var(--st-rayon-xs)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover{color:var(--st-couleur-primaire-survol)}:host([data-variante=discret]){color:var(--st-couleur-texte-discret)}:host([data-variante=autonome]){display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:var(--st-cible-tactile-min);text-decoration:none}:host([data-variante=autonome]):hover{text-decoration:underline}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
451
+ }
452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StLink, decorators: [{
453
+ type: Component,
454
+ args: [{ selector: 'a[stLink]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon], host: {
455
+ class: 'st-link',
456
+ '[attr.data-variante]': 'variante()',
457
+ '[attr.target]': 'externe() ? "_blank" : null',
458
+ '[attr.rel]': 'externe() ? "noopener noreferrer" : null',
459
+ }, template: "<ng-content />\n@if (externe()) {\n <st-icon nom=\"lien-externe\" taille=\"xs\" />\n <span class=\"st-vh\">{{ mentionExterne() }}</span>\n}\n", styles: [":host{display:inline;gap:var(--st-espacement-1);color:var(--st-couleur-primaire);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;border-radius:var(--st-rayon-xs)}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px;border-radius:inherit}:host:hover{color:var(--st-couleur-primaire-survol)}:host([data-variante=discret]){color:var(--st-couleur-texte-discret)}:host([data-variante=autonome]){display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:var(--st-cible-tactile-min);text-decoration:none}:host([data-variante=autonome]):hover{text-decoration:underline}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"] }]
460
+ }], propDecorators: { variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], externe: [{ type: i0.Input, args: [{ isSignal: true, alias: "externe", required: false }] }], mentionExterne: [{ type: i0.Input, args: [{ isSignal: true, alias: "mentionExterne", required: false }] }] } });
461
+
462
+ /**
463
+ * Regroupe des actions liees.
464
+ *
465
+ * ⚠️ `role="group"` et NON `role="toolbar"` : une barre d'outils exige un
466
+ * `tabindex` glissant et la navigation par fleches. Annoncer « barre d'outils »
467
+ * sans en fournir le comportement desoriente plus qu'un groupe simple. Le
468
+ * motif complet viendra avec les primitives d'`@angular/aria`.
469
+ */
470
+ class StButtonGroup {
471
+ /** Nom du groupe : sans lui, le regroupement n'apprend rien a l'oral. */
472
+ etiquette = input.required(/* @ts-ignore */
473
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
474
+ orientation = input('horizontale', /* @ts-ignore */
475
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
476
+ /** Boutons jointifs, rayons fusionnes. */
477
+ attache = input(false, /* @ts-ignore */
478
+ ...(ngDevMode ? [{ debugName: "attache" }] : /* istanbul ignore next */ []));
479
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StButtonGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
480
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StButtonGroup, isStandalone: true, selector: "st-button-group", inputs: { etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: true, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, attache: { classPropertyName: "attache", publicName: "attache", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "etiquette()", "attr.data-orientation": "orientation()", "attr.data-attache": "attache() ? \"\" : null" }, classAttribute: "st-button-group" }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}:host([data-orientation=verticale]){flex-direction:column;align-items:stretch}:host([data-attache]){flex-wrap:nowrap;gap:0}:host([data-attache]) ::ng-deep>.st-button:not(:first-child){border-start-start-radius:0;border-end-start-radius:0}:host([data-attache]) ::ng-deep>.st-button:not(:last-child){border-start-end-radius:0;border-end-end-radius:0;border-inline-end-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
481
+ }
482
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StButtonGroup, decorators: [{
483
+ type: Component,
484
+ args: [{ selector: 'st-button-group', changeDetection: ChangeDetectionStrategy.OnPush, host: {
485
+ class: 'st-button-group',
486
+ role: 'group',
487
+ '[attr.aria-label]': 'etiquette()',
488
+ '[attr.data-orientation]': 'orientation()',
489
+ '[attr.data-attache]': 'attache() ? "" : null',
490
+ }, template: "<ng-content />\n", styles: [":host{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}:host([data-orientation=verticale]){flex-direction:column;align-items:stretch}:host([data-attache]){flex-wrap:nowrap;gap:0}:host([data-attache]) ::ng-deep>.st-button:not(:first-child){border-start-start-radius:0;border-end-start-radius:0}:host([data-attache]) ::ng-deep>.st-button:not(:last-child){border-start-end-radius:0;border-end-end-radius:0;border-inline-end-width:0}\n"] }]
491
+ }], propDecorators: { etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: true }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], attache: [{ type: i0.Input, args: [{ isSignal: true, alias: "attache", required: false }] }] } });
492
+
493
+ /**
494
+ * Valeur technique courte et copiable : identifiant de trace, reference
495
+ * d'operation, code d'erreur.
496
+ *
497
+ * ⚠️ La confirmation de copie passe par une petite region vivante interne, et
498
+ * le LIBELLE DU BOUTON NE CHANGE PAS. Remplacer « Copier » par « Copie » ferait
499
+ * changer le nom accessible du controle que l'utilisateur a sous le doigt :
500
+ * le lecteur d'ecran annonce alors un autre bouton que celui qu'il croyait.
501
+ */
502
+ class StCode {
503
+ document = inject(DOCUMENT);
504
+ destruction = inject(DestroyRef);
505
+ valeur = input.required(/* @ts-ignore */
506
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
507
+ etiquette = input('Identifiant de trace', /* @ts-ignore */
508
+ ...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
509
+ copiable = input(true, /* @ts-ignore */
510
+ ...(ngDevMode ? [{ debugName: "copiable" }] : /* istanbul ignore next */ []));
511
+ libelleCopier = input('Copier', /* @ts-ignore */
512
+ ...(ngDevMode ? [{ debugName: "libelleCopier" }] : /* istanbul ignore next */ []));
513
+ messageDeConfirmation = input('Copie', /* @ts-ignore */
514
+ ...(ngDevMode ? [{ debugName: "messageDeConfirmation" }] : /* istanbul ignore next */ []));
515
+ copie = output();
516
+ confirmation = signal('', /* @ts-ignore */
517
+ ...(ngDevMode ? [{ debugName: "confirmation" }] : /* istanbul ignore next */ []));
518
+ minuterie = null;
519
+ texteDeConfirmation = this.confirmation.asReadonly();
520
+ /**
521
+ * Le bouton n'apparait que si le navigateur sait copier. Sans presse-papiers,
522
+ * un bouton qui ne fait rien vaut moins que la selection manuelle, que la
523
+ * feuille garde possible (`user-select: all`).
524
+ */
525
+ copiePossible = computed(() => {
526
+ if (!this.copiable())
527
+ return false;
528
+ const fenetre = this.document.defaultView;
529
+ return fenetre?.navigator.clipboard !== undefined;
530
+ }, /* @ts-ignore */
531
+ ...(ngDevMode ? [{ debugName: "copiePossible" }] : /* istanbul ignore next */ []));
532
+ async auClicDeCopie() {
533
+ const presse = this.document.defaultView?.navigator.clipboard;
534
+ if (presse === undefined)
535
+ return;
536
+ try {
537
+ await presse.writeText(this.valeur());
538
+ }
539
+ catch {
540
+ // Le refus du presse-papiers n'est pas une erreur applicative : la
541
+ // valeur reste selectionnable a la main.
542
+ return;
543
+ }
544
+ this.copie.emit(this.valeur());
545
+ this.confirmation.set(this.messageDeConfirmation());
546
+ if (this.minuterie !== null)
547
+ clearTimeout(this.minuterie);
548
+ this.minuterie = setTimeout(() => {
549
+ this.confirmation.set('');
550
+ this.minuterie = null;
551
+ }, 3000);
552
+ this.destruction.onDestroy(() => {
553
+ if (this.minuterie !== null)
554
+ clearTimeout(this.minuterie);
555
+ });
556
+ }
557
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCode, deps: [], target: i0.ɵɵFactoryTarget.Component });
558
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StCode, isStandalone: true, selector: "st-code", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null }, copiable: { classPropertyName: "copiable", publicName: "copiable", isSignal: true, isRequired: false, transformFunction: null }, libelleCopier: { classPropertyName: "libelleCopier", publicName: "libelleCopier", isSignal: true, isRequired: false, transformFunction: null }, messageDeConfirmation: { classPropertyName: "messageDeConfirmation", publicName: "messageDeConfirmation", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { copie: "copie" }, host: { classAttribute: "st-code" }, ngImport: i0, template: "<code [attr.aria-label]=\"etiquette() + ' ' + valeur()\">{{ valeur() }}</code>\n\n@if (copiePossible()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n icone=\"copie\"\n taille=\"sm\"\n [etiquette]=\"libelleCopier()\"\n (click)=\"auClicDeCopie()\"\n ></button>\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ texteDeConfirmation() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-1);align-items:center}code{padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-2);font-family:var(--st-police-mono);font-size:var(--st-taille-sm);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-sm);overflow-wrap:anywhere;-webkit-user-select:all;user-select:all}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: StIconButton, selector: "button[stIconButton]", inputs: ["icone", "etiquette", "variante", "taille", "enfonce"], outputs: ["enfonceChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
559
+ }
560
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCode, decorators: [{
561
+ type: Component,
562
+ args: [{ selector: 'st-code', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIconButton], host: { class: 'st-code' }, template: "<code [attr.aria-label]=\"etiquette() + ' ' + valeur()\">{{ valeur() }}</code>\n\n@if (copiePossible()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n icone=\"copie\"\n taille=\"sm\"\n [etiquette]=\"libelleCopier()\"\n (click)=\"auClicDeCopie()\"\n ></button>\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ texteDeConfirmation() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-1);align-items:center}code{padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-2);font-family:var(--st-police-mono);font-size:var(--st-taille-sm);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-sm);overflow-wrap:anywhere;-webkit-user-select:all;user-select:all}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"] }]
563
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }], copiable: [{ type: i0.Input, args: [{ isSignal: true, alias: "copiable", required: false }] }], libelleCopier: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelleCopier", required: false }] }], messageDeConfirmation: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageDeConfirmation", required: false }] }], copie: [{ type: i0.Output, args: ["copie"] }] } });
564
+
565
+ const LIBELLE_DU_TYPE = {
566
+ iban: 'IBAN',
567
+ carte: 'carte',
568
+ telephone: 'numero',
569
+ };
570
+ /**
571
+ * Identifiant sensible, masque par defaut.
572
+ *
573
+ * ⚠️ La valeur masquee porte un `aria-label` en toutes lettres (« IBAN se
574
+ * terminant par 2606 ») : plusieurs lecteurs d'ecran epellent une suite de
575
+ * puces caractere par caractere, ce qui est a la fois illisible et interminable.
576
+ *
577
+ * La revelation est un EVENEMENT METIER : elle se journalise cote appelant par
578
+ * la sortie `revelation`. La valeur revelee, elle, ne part jamais dans un
579
+ * journal.
580
+ */
581
+ class StMasked {
582
+ destruction = inject(DestroyRef);
583
+ valeur = input.required(/* @ts-ignore */
584
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
585
+ type = input('iban', /* @ts-ignore */
586
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
587
+ revele = model(false, /* @ts-ignore */
588
+ ...(ngDevMode ? [{ debugName: "revele" }] : /* istanbul ignore next */ []));
589
+ revelationAutorisee = input(false, /* @ts-ignore */
590
+ ...(ngDevMode ? [{ debugName: "revelationAutorisee" }] : /* istanbul ignore next */ []));
591
+ dureeDeRevelationEnSecondes = input(30, /* @ts-ignore */
592
+ ...(ngDevMode ? [{ debugName: "dureeDeRevelationEnSecondes" }] : /* istanbul ignore next */ []));
593
+ libelleAfficher = input('Afficher', /* @ts-ignore */
594
+ ...(ngDevMode ? [{ debugName: "libelleAfficher" }] : /* istanbul ignore next */ []));
595
+ libelleMasquer = input('Masquer', /* @ts-ignore */
596
+ ...(ngDevMode ? [{ debugName: "libelleMasquer" }] : /* istanbul ignore next */ []));
597
+ revelation = output();
598
+ annonce = signal('', /* @ts-ignore */
599
+ ...(ngDevMode ? [{ debugName: "annonce" }] : /* istanbul ignore next */ []));
600
+ texteDAnnonce = this.annonce.asReadonly();
601
+ minuterie = null;
602
+ valeurMasquee = computed(() => {
603
+ const valeur = this.valeur();
604
+ switch (this.type()) {
605
+ case 'carte':
606
+ return masquerCarte(valeur);
607
+ case 'telephone':
608
+ return masquerTelephone(valeur);
609
+ default:
610
+ return masquerIban(valeur);
611
+ }
612
+ }, /* @ts-ignore */
613
+ ...(ngDevMode ? [{ debugName: "valeurMasquee" }] : /* istanbul ignore next */ []));
614
+ valeurAffichee = computed(() => this.revele() ? this.valeurLisible() : this.valeurMasquee(), /* @ts-ignore */
615
+ ...(ngDevMode ? [{ debugName: "valeurAffichee" }] : /* istanbul ignore next */ []));
616
+ nomAccessible = computed(() => {
617
+ const nature = LIBELLE_DU_TYPE[this.type()];
618
+ if (this.revele())
619
+ return `${nature} ${this.valeurLisible()}`;
620
+ return `${nature} se terminant par ${quatreDerniers(this.valeur())}`;
621
+ }, /* @ts-ignore */
622
+ ...(ngDevMode ? [{ debugName: "nomAccessible" }] : /* istanbul ignore next */ []));
623
+ libelleDuBouton = computed(() => this.revele() ? this.libelleMasquer() : this.libelleAfficher(), /* @ts-ignore */
624
+ ...(ngDevMode ? [{ debugName: "libelleDuBouton" }] : /* istanbul ignore next */ []));
625
+ valeurLisible() {
626
+ return this.type() === 'iban' ? formaterIban(this.valeur()) : this.valeur();
627
+ }
628
+ basculer() {
629
+ if (this.revele()) {
630
+ this.masquer();
631
+ return;
632
+ }
633
+ this.revele.set(true);
634
+ this.revelation.emit({ type: this.type() });
635
+ this.annonce.set('');
636
+ // Re-masquage automatique : une valeur revelee puis oubliee a l'ecran est
637
+ // lisible par quiconque passe derriere.
638
+ if (this.minuterie !== null)
639
+ clearTimeout(this.minuterie);
640
+ this.minuterie = setTimeout(() => {
641
+ this.masquer();
642
+ }, this.dureeDeRevelationEnSecondes() * 1000);
643
+ this.destruction.onDestroy(() => {
644
+ if (this.minuterie !== null)
645
+ clearTimeout(this.minuterie);
646
+ });
647
+ }
648
+ masquer() {
649
+ if (this.minuterie !== null) {
650
+ clearTimeout(this.minuterie);
651
+ this.minuterie = null;
652
+ }
653
+ if (!this.revele())
654
+ return;
655
+ this.revele.set(false);
656
+ this.annonce.set('Valeur masquee a nouveau');
657
+ }
658
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StMasked, deps: [], target: i0.ɵɵFactoryTarget.Component });
659
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StMasked, isStandalone: true, selector: "st-masked", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, revele: { classPropertyName: "revele", publicName: "revele", isSignal: true, isRequired: false, transformFunction: null }, revelationAutorisee: { classPropertyName: "revelationAutorisee", publicName: "revelationAutorisee", isSignal: true, isRequired: false, transformFunction: null }, dureeDeRevelationEnSecondes: { classPropertyName: "dureeDeRevelationEnSecondes", publicName: "dureeDeRevelationEnSecondes", isSignal: true, isRequired: false, transformFunction: null }, libelleAfficher: { classPropertyName: "libelleAfficher", publicName: "libelleAfficher", isSignal: true, isRequired: false, transformFunction: null }, libelleMasquer: { classPropertyName: "libelleMasquer", publicName: "libelleMasquer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { revele: "reveleChange", revelation: "revelation" }, host: { properties: { "attr.data-type": "type()" }, classAttribute: "st-masked" }, ngImport: i0, template: "<span class=\"st-masked__valeur\" [attr.aria-label]=\"nomAccessible()\">{{ valeurAffichee() }}</span>\n\n@if (revelationAutorisee()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n taille=\"sm\"\n [icone]=\"revele() ? 'oeil-barre' : 'oeil'\"\n [etiquette]=\"libelleDuBouton()\"\n [enfonce]=\"revele()\"\n (click)=\"basculer()\"\n ></button>\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ texteDAnnonce() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-2);align-items:center}.st-masked__valeur{font-family:var(--st-police-mono);color:var(--st-couleur-texte);letter-spacing:.02em}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: StIconButton, selector: "button[stIconButton]", inputs: ["icone", "etiquette", "variante", "taille", "enfonce"], outputs: ["enfonceChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
660
+ }
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StMasked, decorators: [{
662
+ type: Component,
663
+ args: [{ selector: 'st-masked', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIconButton], host: { class: 'st-masked', '[attr.data-type]': 'type()' }, template: "<span class=\"st-masked__valeur\" [attr.aria-label]=\"nomAccessible()\">{{ valeurAffichee() }}</span>\n\n@if (revelationAutorisee()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n taille=\"sm\"\n [icone]=\"revele() ? 'oeil-barre' : 'oeil'\"\n [etiquette]=\"libelleDuBouton()\"\n [enfonce]=\"revele()\"\n (click)=\"basculer()\"\n ></button>\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ texteDAnnonce() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-2);align-items:center}.st-masked__valeur{font-family:var(--st-police-mono);color:var(--st-couleur-texte);letter-spacing:.02em}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"] }]
664
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], revele: [{ type: i0.Input, args: [{ isSignal: true, alias: "revele", required: false }] }, { type: i0.Output, args: ["reveleChange"] }], revelationAutorisee: [{ type: i0.Input, args: [{ isSignal: true, alias: "revelationAutorisee", required: false }] }], dureeDeRevelationEnSecondes: [{ type: i0.Input, args: [{ isSignal: true, alias: "dureeDeRevelationEnSecondes", required: false }] }], libelleAfficher: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelleAfficher", required: false }] }], libelleMasquer: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelleMasquer", required: false }] }], revelation: [{ type: i0.Output, args: ["revelation"] }] } });
665
+
666
+ /**
667
+ * Somme d'argent.
668
+ *
669
+ * Le montant entre sous forme d'unites mineures entieres : un nombre a virgule
670
+ * flottante ne represente pas exactement 0,10 EUR, et les erreurs s'accumulent.
671
+ * La conversion n'a lieu qu'a l'instant du rendu.
672
+ *
673
+ * ⚠️ Le signe negatif est TOUJOURS rendu en texte. La couleur ne fait que le
674
+ * renforcer : un daltonien, une impression en noir et blanc ou un theme a fort
675
+ * contraste ne doivent jamais transformer un debit en credit.
676
+ */
677
+ class StMoney {
678
+ locale = inject(LOCALE_ID);
679
+ valeur = input.required(/* @ts-ignore */
680
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
681
+ signeExplicite = input(false, /* @ts-ignore */
682
+ ...(ngDevMode ? [{ debugName: "signeExplicite" }] : /* istanbul ignore next */ []));
683
+ sansDevise = input(false, /* @ts-ignore */
684
+ ...(ngDevMode ? [{ debugName: "sansDevise" }] : /* istanbul ignore next */ []));
685
+ taille = input('md', /* @ts-ignore */
686
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
687
+ signe = computed(() => {
688
+ const unites = this.valeur().unitesMineures;
689
+ if (unites < 0n)
690
+ return 'negatif';
691
+ if (unites > 0n)
692
+ return 'positif';
693
+ return 'nul';
694
+ }, /* @ts-ignore */
695
+ ...(ngDevMode ? [{ debugName: "signe" }] : /* istanbul ignore next */ []));
696
+ texte = computed(() => formaterMontant(this.valeur(), {
697
+ locale: this.locale,
698
+ signeExplicite: this.signeExplicite(),
699
+ sansDevise: this.sansDevise(),
700
+ }), /* @ts-ignore */
701
+ ...(ngDevMode ? [{ debugName: "texte" }] : /* istanbul ignore next */ []));
702
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StMoney, deps: [], target: i0.ɵɵFactoryTarget.Component });
703
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StMoney, isStandalone: true, selector: "st-money", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, signeExplicite: { classPropertyName: "signeExplicite", publicName: "signeExplicite", isSignal: true, isRequired: false, transformFunction: null }, sansDevise: { classPropertyName: "sansDevise", publicName: "sansDevise", isSignal: true, isRequired: false, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-taille": "taille()", "attr.data-signe": "signe()" }, classAttribute: "st-money" }, ngImport: i0, template: "{{ texte() }}\n", styles: [":host{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-moyenne);color:var(--st-couleur-texte);white-space:nowrap}:host([data-signe=negatif]){color:var(--st-couleur-danger)}:host([data-taille=sm]){font-size:var(--st-taille-sm)}:host([data-taille=lg]){font-size:var(--st-taille-lg)}:host([data-taille=xl]){font-size:var(--st-taille-xl)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
704
+ }
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StMoney, decorators: [{
706
+ type: Component,
707
+ args: [{ selector: 'st-money', changeDetection: ChangeDetectionStrategy.OnPush, host: {
708
+ class: 'st-money',
709
+ '[attr.data-taille]': 'taille()',
710
+ '[attr.data-signe]': 'signe()',
711
+ }, template: "{{ texte() }}\n", styles: [":host{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-moyenne);color:var(--st-couleur-texte);white-space:nowrap}:host([data-signe=negatif]){color:var(--st-couleur-danger)}:host([data-taille=sm]){font-size:var(--st-taille-sm)}:host([data-taille=lg]){font-size:var(--st-taille-lg)}:host([data-taille=xl]){font-size:var(--st-taille-xl)}\n"] }]
712
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], signeExplicite: [{ type: i0.Input, args: [{ isSignal: true, alias: "signeExplicite", required: false }] }], sansDevise: [{ type: i0.Input, args: [{ isSignal: true, alias: "sansDevise", required: false }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }] } });
713
+
714
+ const OPTIONS = {
715
+ court: { day: '2-digit', month: '2-digit', year: 'numeric' },
716
+ long: { day: 'numeric', month: 'long', year: 'numeric' },
717
+ 'date-heure': {
718
+ day: '2-digit',
719
+ month: '2-digit',
720
+ year: 'numeric',
721
+ hour: '2-digit',
722
+ minute: '2-digit',
723
+ },
724
+ heure: { hour: '2-digit', minute: '2-digit' },
725
+ };
726
+ /**
727
+ * Date rendue dans la locale et le fuseau de l'application.
728
+ *
729
+ * ⚠️ La locale et le fuseau sont EXPLICITES, jamais deduits de l'environnement
730
+ * d'execution. Un rendu serveur et un rendu navigateur qui ne s'accordent pas
731
+ * produisent deux textes differents, et l'hydratation casse. Le rendu serveur
732
+ * est ecarte en version 1, mais la compatibilite reste gardee a cout nul.
733
+ *
734
+ * Le format court garde un nom accessible en toutes lettres : « 12/09/26 » est
735
+ * ambigu a l'oral, et l'ordre jour-mois n'est pas universel.
736
+ */
737
+ class StDate {
738
+ locale = inject(LOCALE_ID);
739
+ fuseauParDefaut = inject(FUSEAU_PAR_DEFAUT);
740
+ valeur = input.required(/* @ts-ignore */
741
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
742
+ format = input('court', /* @ts-ignore */
743
+ ...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
744
+ fuseau = input(null, /* @ts-ignore */
745
+ ...(ngDevMode ? [{ debugName: "fuseau" }] : /* istanbul ignore next */ []));
746
+ date = computed(() => {
747
+ const valeur = this.valeur();
748
+ return valeur instanceof Date ? valeur : new Date(valeur);
749
+ }, /* @ts-ignore */
750
+ ...(ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []));
751
+ estValide = computed(() => !Number.isNaN(this.date().getTime()), /* @ts-ignore */
752
+ ...(ngDevMode ? [{ debugName: "estValide" }] : /* istanbul ignore next */ []));
753
+ iso = computed(() => (this.estValide() ? this.date().toISOString() : null), /* @ts-ignore */
754
+ ...(ngDevMode ? [{ debugName: "iso" }] : /* istanbul ignore next */ []));
755
+ texte = computed(() => {
756
+ if (!this.estValide())
757
+ return '';
758
+ return new Intl.DateTimeFormat(this.locale, {
759
+ ...OPTIONS[this.format()],
760
+ timeZone: this.fuseau() ?? this.fuseauParDefaut,
761
+ }).format(this.date());
762
+ }, /* @ts-ignore */
763
+ ...(ngDevMode ? [{ debugName: "texte" }] : /* istanbul ignore next */ []));
764
+ /** Forme longue, lue par les technologies d'assistance. */
765
+ texteAccessible = computed(() => {
766
+ if (!this.estValide())
767
+ return '';
768
+ if (this.format() === 'court') {
769
+ return new Intl.DateTimeFormat(this.locale, {
770
+ ...OPTIONS.long,
771
+ timeZone: this.fuseau() ?? this.fuseauParDefaut,
772
+ }).format(this.date());
773
+ }
774
+ return null;
775
+ }, /* @ts-ignore */
776
+ ...(ngDevMode ? [{ debugName: "texteAccessible" }] : /* istanbul ignore next */ []));
777
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDate, deps: [], target: i0.ɵɵFactoryTarget.Component });
778
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StDate, isStandalone: true, selector: "st-date", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, fuseau: { classPropertyName: "fuseau", publicName: "fuseau", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "st-date" }, ngImport: i0, template: "@if (estValide()) {\n <time [attr.datetime]=\"iso()\" [attr.aria-label]=\"texteAccessible()\">{{ texte() }}</time>\n}\n", styles: [":host{font-variant-numeric:tabular-nums;color:var(--st-couleur-texte);white-space:nowrap}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
779
+ }
780
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDate, decorators: [{
781
+ type: Component,
782
+ args: [{ selector: 'st-date', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'st-date' }, template: "@if (estValide()) {\n <time [attr.datetime]=\"iso()\" [attr.aria-label]=\"texteAccessible()\">{{ texte() }}</time>\n}\n", styles: [":host{font-variant-numeric:tabular-nums;color:var(--st-couleur-texte);white-space:nowrap}\n"] }]
783
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], fuseau: [{ type: i0.Input, args: [{ isSignal: true, alias: "fuseau", required: false }] }] } });
784
+
785
+ const MINUTE = 60_000;
786
+ const HEURE = 60 * MINUTE;
787
+ const JOUR = 24 * HEURE;
788
+ /**
789
+ * Temps ecoule, exprime relativement (« il y a 5 minutes »).
790
+ *
791
+ * ⚠️ Ce composant ne doit JAMAIS etre place dans une region vivante : son texte
792
+ * se reecrit tout seul, et une region vivante le ferait annoncer en boucle.
793
+ * Son nom accessible porte la date absolue, qui elle ne bouge pas.
794
+ *
795
+ * La cadence de rafraichissement s'adapte : rafraichir chaque seconde une date
796
+ * vieille de trois jours reveille l'onglet pour rien.
797
+ */
798
+ class StRelativeTime {
799
+ locale = inject(LOCALE_ID);
800
+ fuseau = inject(FUSEAU_PAR_DEFAUT);
801
+ valeur = input.required(/* @ts-ignore */
802
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
803
+ rafraichir = input(true, /* @ts-ignore */
804
+ ...(ngDevMode ? [{ debugName: "rafraichir" }] : /* istanbul ignore next */ []));
805
+ /** Au-dela de ce nombre de jours, la date absolue est plus parlante. */
806
+ seuilAbsoluEnJours = input(7, /* @ts-ignore */
807
+ ...(ngDevMode ? [{ debugName: "seuilAbsoluEnJours" }] : /* istanbul ignore next */ []));
808
+ destruction = inject(DestroyRef);
809
+ maintenant = signal(Date.now(), /* @ts-ignore */
810
+ ...(ngDevMode ? [{ debugName: "maintenant" }] : /* istanbul ignore next */ []));
811
+ constructor() {
812
+ // `afterNextRender` : aucune minuterie ne demarre a la construction, ce qui
813
+ // garde le composant compatible avec un rendu serveur.
814
+ afterNextRender(() => {
815
+ if (!this.rafraichir())
816
+ return;
817
+ let minuterie;
818
+ const planifier = () => {
819
+ const ecart = Math.abs(Date.now() - this.dateEnMillisecondes());
820
+ // Sous une minute, la valeur bouge vite ; au-dela, une fois par minute
821
+ // suffit et l'onglet se rendort.
822
+ const cadence = ecart < MINUTE ? 10_000 : MINUTE;
823
+ minuterie = setTimeout(() => {
824
+ this.maintenant.set(Date.now());
825
+ planifier();
826
+ }, cadence);
827
+ };
828
+ planifier();
829
+ this.destruction.onDestroy(() => {
830
+ clearTimeout(minuterie);
831
+ });
832
+ });
833
+ }
834
+ dateEnMillisecondes() {
835
+ const valeur = this.valeur();
836
+ return (valeur instanceof Date ? valeur : new Date(valeur)).getTime();
837
+ }
838
+ estValide = computed(() => !Number.isNaN(this.dateEnMillisecondes()), /* @ts-ignore */
839
+ ...(ngDevMode ? [{ debugName: "estValide" }] : /* istanbul ignore next */ []));
840
+ iso = computed(() => this.estValide() ? new Date(this.dateEnMillisecondes()).toISOString() : null, /* @ts-ignore */
841
+ ...(ngDevMode ? [{ debugName: "iso" }] : /* istanbul ignore next */ []));
842
+ depasseLeSeuil = computed(() => {
843
+ const ecart = Math.abs(this.maintenant() - this.dateEnMillisecondes());
844
+ return ecart > this.seuilAbsoluEnJours() * JOUR;
845
+ }, /* @ts-ignore */
846
+ ...(ngDevMode ? [{ debugName: "depasseLeSeuil" }] : /* istanbul ignore next */ []));
847
+ texte = computed(() => {
848
+ if (!this.estValide())
849
+ return '';
850
+ const cible = this.dateEnMillisecondes();
851
+ if (this.depasseLeSeuil()) {
852
+ return new Intl.DateTimeFormat(this.locale, {
853
+ day: 'numeric',
854
+ month: 'long',
855
+ year: 'numeric',
856
+ timeZone: this.fuseau,
857
+ }).format(new Date(cible));
858
+ }
859
+ const ecart = cible - this.maintenant();
860
+ const absolu = Math.abs(ecart);
861
+ // Sous dix secondes, « il y a 0 seconde » est plus deroutant qu'utile.
862
+ if (absolu < 10_000)
863
+ return "a l'instant";
864
+ const format = new Intl.RelativeTimeFormat(this.locale, { numeric: 'auto' });
865
+ if (absolu < MINUTE)
866
+ return format.format(Math.round(ecart / 1000), 'second');
867
+ if (absolu < HEURE)
868
+ return format.format(Math.round(ecart / MINUTE), 'minute');
869
+ if (absolu < JOUR)
870
+ return format.format(Math.round(ecart / HEURE), 'hour');
871
+ return format.format(Math.round(ecart / JOUR), 'day');
872
+ }, /* @ts-ignore */
873
+ ...(ngDevMode ? [{ debugName: "texte" }] : /* istanbul ignore next */ []));
874
+ texteAccessible = computed(() => {
875
+ if (!this.estValide())
876
+ return '';
877
+ return new Intl.DateTimeFormat(this.locale, {
878
+ day: 'numeric',
879
+ month: 'long',
880
+ year: 'numeric',
881
+ hour: '2-digit',
882
+ minute: '2-digit',
883
+ timeZone: this.fuseau,
884
+ }).format(new Date(this.dateEnMillisecondes()));
885
+ }, /* @ts-ignore */
886
+ ...(ngDevMode ? [{ debugName: "texteAccessible" }] : /* istanbul ignore next */ []));
887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StRelativeTime, deps: [], target: i0.ɵɵFactoryTarget.Component });
888
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StRelativeTime, isStandalone: true, selector: "st-relative-time", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, rafraichir: { classPropertyName: "rafraichir", publicName: "rafraichir", isSignal: true, isRequired: false, transformFunction: null }, seuilAbsoluEnJours: { classPropertyName: "seuilAbsoluEnJours", publicName: "seuilAbsoluEnJours", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "st-relative-time" }, ngImport: i0, template: "@if (estValide()) {\n <time [attr.datetime]=\"iso()\" [attr.aria-label]=\"texteAccessible()\">{{ texte() }}</time>\n}\n", styles: [":host{font-size:var(--st-taille-sm);color:var(--st-couleur-texte-discret)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
889
+ }
890
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StRelativeTime, decorators: [{
891
+ type: Component,
892
+ args: [{ selector: 'st-relative-time', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'st-relative-time' }, template: "@if (estValide()) {\n <time [attr.datetime]=\"iso()\" [attr.aria-label]=\"texteAccessible()\">{{ texte() }}</time>\n}\n", styles: [":host{font-size:var(--st-taille-sm);color:var(--st-couleur-texte-discret)}\n"] }]
893
+ }], ctorParameters: () => [], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], rafraichir: [{ type: i0.Input, args: [{ isSignal: true, alias: "rafraichir", required: false }] }], seuilAbsoluEnJours: [{ type: i0.Input, args: [{ isSignal: true, alias: "seuilAbsoluEnJours", required: false }] }] } });
894
+
895
+ /**
896
+ * Nombre dans le flux du texte.
897
+ *
898
+ * Distinct de `st-badge`, qui est une pastille superposee a un autre element.
899
+ *
900
+ * ⚠️ Quand la valeur est abregee (« 1,2 k »), le nom accessible porte le nombre
901
+ * COMPLET : l'abreviation est une commodite visuelle, pas une perte
902
+ * d'information pour qui ecoute la page.
903
+ */
904
+ class StCount {
905
+ locale = inject(LOCALE_ID);
906
+ valeur = input.required(/* @ts-ignore */
907
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
908
+ ton = input('courant', /* @ts-ignore */
909
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
910
+ /** `null` : jamais d'abreviation. */
911
+ abregerAuDela = input(1000, /* @ts-ignore */
912
+ ...(ngDevMode ? [{ debugName: "abregerAuDela" }] : /* istanbul ignore next */ []));
913
+ estAbrege = computed(() => {
914
+ const seuil = this.abregerAuDela();
915
+ return seuil !== null && Math.abs(this.valeur()) >= seuil;
916
+ }, /* @ts-ignore */
917
+ ...(ngDevMode ? [{ debugName: "estAbrege" }] : /* istanbul ignore next */ []));
918
+ texteComplet = computed(() => new Intl.NumberFormat(this.locale).format(this.valeur()), /* @ts-ignore */
919
+ ...(ngDevMode ? [{ debugName: "texteComplet" }] : /* istanbul ignore next */ []));
920
+ texte = computed(() => {
921
+ if (!this.estAbrege())
922
+ return this.texteComplet();
923
+ return new Intl.NumberFormat(this.locale, {
924
+ notation: 'compact',
925
+ maximumFractionDigits: 1,
926
+ }).format(this.valeur());
927
+ }, /* @ts-ignore */
928
+ ...(ngDevMode ? [{ debugName: "texte" }] : /* istanbul ignore next */ []));
929
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCount, deps: [], target: i0.ɵɵFactoryTarget.Component });
930
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StCount, isStandalone: true, selector: "st-count", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: true, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, abregerAuDela: { classPropertyName: "abregerAuDela", publicName: "abregerAuDela", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-ton": "ton()", "attr.aria-label": "estAbrege() ? texteComplet() : null" }, classAttribute: "st-count" }, ngImport: i0, template: "{{ texte() }}\n", styles: [":host{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-moyenne);color:var(--st-couleur-texte);white-space:nowrap}:host([data-ton=discret]){color:var(--st-couleur-texte-discret)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
931
+ }
932
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCount, decorators: [{
933
+ type: Component,
934
+ args: [{ selector: 'st-count', changeDetection: ChangeDetectionStrategy.OnPush, host: {
935
+ class: 'st-count',
936
+ '[attr.data-ton]': 'ton()',
937
+ '[attr.aria-label]': 'estAbrege() ? texteComplet() : null',
938
+ }, template: "{{ texte() }}\n", styles: [":host{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-moyenne);color:var(--st-couleur-texte);white-space:nowrap}:host([data-ton=discret]){color:var(--st-couleur-texte-discret)}\n"] }]
939
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: true }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], abregerAuDela: [{ type: i0.Input, args: [{ isSignal: true, alias: "abregerAuDela", required: false }] }] } });
940
+
941
+ /**
942
+ * Pastille de statut.
943
+ *
944
+ * ⚠️ Le libelle ET l'icone sont OBLIGATOIRES, et l'icone doit differer par sa
945
+ * FORME d'un ton a l'autre (coche, triangle, croix, cercle). Un statut porte
946
+ * par la seule couleur est illisible pour une personne daltonienne, a
947
+ * l'impression, et sous un theme a fort contraste.
948
+ *
949
+ * ⚠️ Aucun role ARIA, malgre le nom du composant : `role="status"` en ferait
950
+ * une region vivante, et une liste de vingt lignes ferait annoncer vingt
951
+ * statuts a chaque rendu.
952
+ */
953
+ class StStatusPill {
954
+ /** Code machine du statut : sert aux tests et aux selecteurs, jamais a l'affichage. */
955
+ statut = input.required(/* @ts-ignore */
956
+ ...(ngDevMode ? [{ debugName: "statut" }] : /* istanbul ignore next */ []));
957
+ /** Texte deja traduit par l'appelant : le socle ne traduit rien. */
958
+ libelle = input.required(/* @ts-ignore */
959
+ ...(ngDevMode ? [{ debugName: "libelle" }] : /* istanbul ignore next */ []));
960
+ icone = input.required(/* @ts-ignore */
961
+ ...(ngDevMode ? [{ debugName: "icone" }] : /* istanbul ignore next */ []));
962
+ ton = input('neutre', /* @ts-ignore */
963
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
964
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StStatusPill, deps: [], target: i0.ɵɵFactoryTarget.Component });
965
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StStatusPill, isStandalone: true, selector: "st-status-pill", inputs: { statut: { classPropertyName: "statut", publicName: "statut", isSignal: true, isRequired: true, transformFunction: null }, libelle: { classPropertyName: "libelle", publicName: "libelle", isSignal: true, isRequired: true, transformFunction: null }, icone: { classPropertyName: "icone", publicName: "icone", isSignal: true, isRequired: true, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-ton": "ton()", "attr.data-statut": "statut()" }, classAttribute: "st-status-pill" }, ngImport: i0, template: "<st-icon [nom]=\"icone()\" taille=\"sm\" />\n<span class=\"st-status-pill__libelle\">{{ libelle() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-2);align-items:center;min-block-size:1.5rem;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm);font-weight:var(--st-graisse-moyenne);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-pleine)}:host([data-ton=info]){color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret)}:host([data-ton=succes]){color:var(--st-couleur-sur-succes-discret);background:var(--st-couleur-succes-discret)}:host([data-ton=avertissement]){color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret)}:host([data-ton=danger]){color:var(--st-couleur-sur-danger-discret);background:var(--st-couleur-danger-discret)}.st-status-pill__libelle{white-space:normal}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
966
+ }
967
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StStatusPill, decorators: [{
968
+ type: Component,
969
+ args: [{ selector: 'st-status-pill', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon], host: {
970
+ class: 'st-status-pill',
971
+ '[attr.data-ton]': 'ton()',
972
+ '[attr.data-statut]': 'statut()',
973
+ }, template: "<st-icon [nom]=\"icone()\" taille=\"sm\" />\n<span class=\"st-status-pill__libelle\">{{ libelle() }}</span>\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-2);align-items:center;min-block-size:1.5rem;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm);font-weight:var(--st-graisse-moyenne);line-height:var(--st-interligne-serre);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-pleine)}:host([data-ton=info]){color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret)}:host([data-ton=succes]){color:var(--st-couleur-sur-succes-discret);background:var(--st-couleur-succes-discret)}:host([data-ton=avertissement]){color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret)}:host([data-ton=danger]){color:var(--st-couleur-sur-danger-discret);background:var(--st-couleur-danger-discret)}.st-status-pill__libelle{white-space:normal}\n"] }]
974
+ }], propDecorators: { statut: [{ type: i0.Input, args: [{ isSignal: true, alias: "statut", required: true }] }], libelle: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelle", required: true }] }], icone: [{ type: i0.Input, args: [{ isSignal: true, alias: "icone", required: true }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }] } });
975
+
976
+ /**
977
+ * Pastille de comptage superposee a un autre element.
978
+ *
979
+ * ⚠️ Elle est TOUJOURS `aria-hidden`. Un « 5 » flottant, lu hors contexte,
980
+ * n'apprend rien. Le compte doit entrer dans le nom accessible de l'element
981
+ * porteur : « Notifications, 5 non lues », compose par l'appelant.
982
+ *
983
+ * L'element porteur doit declarer la classe `st-badge-ancre`.
984
+ */
985
+ class StBadge {
986
+ valeur = input(null, /* @ts-ignore */
987
+ ...(ngDevMode ? [{ debugName: "valeur" }] : /* istanbul ignore next */ []));
988
+ max = input(99, /* @ts-ignore */
989
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
990
+ ton = input('danger', /* @ts-ignore */
991
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
992
+ /** Point seul, sans chiffre : signale une nouveaute sans la quantifier. */
993
+ point = input(false, /* @ts-ignore */
994
+ ...(ngDevMode ? [{ debugName: "point" }] : /* istanbul ignore next */ []));
995
+ texte = computed(() => {
996
+ if (this.point())
997
+ return '';
998
+ const valeur = this.valeur();
999
+ if (valeur === null)
1000
+ return '';
1001
+ if (typeof valeur === 'string')
1002
+ return valeur;
1003
+ return valeur > this.max() ? `${this.max().toString()}+` : valeur.toString();
1004
+ }, /* @ts-ignore */
1005
+ ...(ngDevMode ? [{ debugName: "texte" }] : /* istanbul ignore next */ []));
1006
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StBadge, deps: [], target: i0.ɵɵFactoryTarget.Component });
1007
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StBadge, isStandalone: true, selector: "st-badge", inputs: { valeur: { classPropertyName: "valeur", publicName: "valeur", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, point: { classPropertyName: "point", publicName: "point", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-ton": "ton()", "attr.data-point": "point() ? \"\" : null" }, classAttribute: "st-badge" }, ngImport: i0, template: "{{ texte() }}\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;min-inline-size:1.25rem;block-size:1.25rem;padding-inline:var(--st-espacement-1);font-size:var(--st-taille-xs);font-weight:var(--st-graisse-semi);line-height:1;color:var(--st-couleur-sur-danger);background:var(--st-couleur-danger);border:2px solid var(--st-couleur-surface);border-radius:var(--st-rayon-pleine)}:host([data-point]){inline-size:.5rem;min-inline-size:0;block-size:.5rem;padding:0}:host([data-ton=neutre]){color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee)}:host([data-ton=primaire]){color:var(--st-couleur-sur-primaire);background:var(--st-couleur-primaire)}:host([data-ton=succes]){color:var(--st-couleur-sur-succes);background:var(--st-couleur-succes)}:host([data-ton=avertissement]){color:var(--st-couleur-sur-avertissement);background:var(--st-couleur-avertissement)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1008
+ }
1009
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StBadge, decorators: [{
1010
+ type: Component,
1011
+ args: [{ selector: 'st-badge', changeDetection: ChangeDetectionStrategy.OnPush, host: {
1012
+ class: 'st-badge',
1013
+ 'aria-hidden': 'true',
1014
+ '[attr.data-ton]': 'ton()',
1015
+ '[attr.data-point]': 'point() ? "" : null',
1016
+ }, template: "{{ texte() }}\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;min-inline-size:1.25rem;block-size:1.25rem;padding-inline:var(--st-espacement-1);font-size:var(--st-taille-xs);font-weight:var(--st-graisse-semi);line-height:1;color:var(--st-couleur-sur-danger);background:var(--st-couleur-danger);border:2px solid var(--st-couleur-surface);border-radius:var(--st-rayon-pleine)}:host([data-point]){inline-size:.5rem;min-inline-size:0;block-size:.5rem;padding:0}:host([data-ton=neutre]){color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee)}:host([data-ton=primaire]){color:var(--st-couleur-sur-primaire);background:var(--st-couleur-primaire)}:host([data-ton=succes]){color:var(--st-couleur-sur-succes);background:var(--st-couleur-succes)}:host([data-ton=avertissement]){color:var(--st-couleur-sur-avertissement);background:var(--st-couleur-avertissement)}\n"] }]
1017
+ }], propDecorators: { valeur: [{ type: i0.Input, args: [{ isSignal: true, alias: "valeur", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], point: [{ type: i0.Input, args: [{ isSignal: true, alias: "point", required: false }] }] } });
1018
+
1019
+ /**
1020
+ * Etiquette, eventuellement retirable.
1021
+ *
1022
+ * ⚠️ Le retrait DEPLACE LE FOCUS avant de s'annoncer. Sans cela, le bouton
1023
+ * disparait sous le doigt de l'utilisateur clavier, le focus retombe sur le
1024
+ * corps du document, et la navigation repart du haut de la page. C'est l'un
1025
+ * des defauts d'accessibilite les plus frequents des listes d'etiquettes.
1026
+ */
1027
+ class StTag {
1028
+ element = inject(ElementRef);
1029
+ libelle = input.required(/* @ts-ignore */
1030
+ ...(ngDevMode ? [{ debugName: "libelle" }] : /* istanbul ignore next */ []));
1031
+ supprimable = input(false, /* @ts-ignore */
1032
+ ...(ngDevMode ? [{ debugName: "supprimable" }] : /* istanbul ignore next */ []));
1033
+ ton = input('neutre', /* @ts-ignore */
1034
+ ...(ngDevMode ? [{ debugName: "ton" }] : /* istanbul ignore next */ []));
1035
+ prefixeDeSuppression = input('Retirer', /* @ts-ignore */
1036
+ ...(ngDevMode ? [{ debugName: "prefixeDeSuppression" }] : /* istanbul ignore next */ []));
1037
+ supprime = output();
1038
+ libelleDeSuppression = computed(() => `${this.prefixeDeSuppression()} ${this.libelle()}`, /* @ts-ignore */
1039
+ ...(ngDevMode ? [{ debugName: "libelleDeSuppression" }] : /* istanbul ignore next */ []));
1040
+ auRetrait() {
1041
+ this.deplacerLeFocus();
1042
+ this.supprime.emit();
1043
+ }
1044
+ /**
1045
+ * Cible, dans l'ordre : le bouton de l'etiquette suivante, celui de la
1046
+ * precedente, puis le conteneur s'il est focalisable par script.
1047
+ */
1048
+ deplacerLeFocus() {
1049
+ const hote = this.element.nativeElement;
1050
+ const suivant = hote.nextElementSibling?.querySelector('button');
1051
+ if (suivant instanceof HTMLElement) {
1052
+ suivant.focus();
1053
+ return;
1054
+ }
1055
+ const precedent = hote.previousElementSibling?.querySelector('button');
1056
+ if (precedent instanceof HTMLElement) {
1057
+ precedent.focus();
1058
+ return;
1059
+ }
1060
+ const parent = hote.parentElement;
1061
+ if (parent?.hasAttribute('tabindex') === true)
1062
+ parent.focus();
1063
+ }
1064
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StTag, deps: [], target: i0.ɵɵFactoryTarget.Component });
1065
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StTag, isStandalone: true, selector: "st-tag", inputs: { libelle: { classPropertyName: "libelle", publicName: "libelle", isSignal: true, isRequired: true, transformFunction: null }, supprimable: { classPropertyName: "supprimable", publicName: "supprimable", isSignal: true, isRequired: false, transformFunction: null }, ton: { classPropertyName: "ton", publicName: "ton", isSignal: true, isRequired: false, transformFunction: null }, prefixeDeSuppression: { classPropertyName: "prefixeDeSuppression", publicName: "prefixeDeSuppression", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { supprime: "supprime" }, host: { properties: { "attr.data-ton": "ton()" }, classAttribute: "st-tag" }, ngImport: i0, template: "<span class=\"st-tag__libelle\">{{ libelle() }}</span>\n\n@if (supprimable()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n icone=\"croix\"\n taille=\"sm\"\n [etiquette]=\"libelleDeSuppression()\"\n (click)=\"auRetrait()\"\n ></button>\n}\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:2rem;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-sm)}:host([data-ton=primaire]){color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret);border-color:transparent}:host ::ng-deep .st-icon-button{min-inline-size:1.5rem;min-block-size:1.5rem}@media(pointer:coarse){:host ::ng-deep .st-icon-button{min-inline-size:var(--st-cible-tactile-min);min-block-size:var(--st-cible-tactile-min)}}\n"], dependencies: [{ kind: "component", type: StIconButton, selector: "button[stIconButton]", inputs: ["icone", "etiquette", "variante", "taille", "enfonce"], outputs: ["enfonceChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1066
+ }
1067
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StTag, decorators: [{
1068
+ type: Component,
1069
+ args: [{ selector: 'st-tag', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIconButton], host: {
1070
+ class: 'st-tag',
1071
+ '[attr.data-ton]': 'ton()',
1072
+ }, template: "<span class=\"st-tag__libelle\">{{ libelle() }}</span>\n\n@if (supprimable()) {\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton. Le nom accessible est\n impose par le type (`etiquette` est requis) et verifie par axe. -->\n <button\n stIconButton\n type=\"button\"\n icone=\"croix\"\n taille=\"sm\"\n [etiquette]=\"libelleDeSuppression()\"\n (click)=\"auRetrait()\"\n ></button>\n}\n", styles: [":host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:2rem;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3);font-size:var(--st-taille-sm);color:var(--st-couleur-texte);background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-sm)}:host([data-ton=primaire]){color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret);border-color:transparent}:host ::ng-deep .st-icon-button{min-inline-size:1.5rem;min-block-size:1.5rem}@media(pointer:coarse){:host ::ng-deep .st-icon-button{min-inline-size:var(--st-cible-tactile-min);min-block-size:var(--st-cible-tactile-min)}}\n"] }]
1073
+ }], propDecorators: { libelle: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelle", required: true }] }], supprimable: [{ type: i0.Input, args: [{ isSignal: true, alias: "supprimable", required: false }] }], ton: [{ type: i0.Input, args: [{ isSignal: true, alias: "ton", required: false }] }], prefixeDeSuppression: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixeDeSuppression", required: false }] }], supprime: [{ type: i0.Output, args: ["supprime"] }] } });
1074
+
1075
+ /**
1076
+ * Premier grapheme d'un mot. Ni `mot[0]` ni `[...mot][0]` ne conviennent : le
1077
+ * premier rend une demi-paire de substitution, le second casse les emoji
1078
+ * composes et les lettres a marque combinante.
1079
+ */
1080
+ function premierGrapheme(mot) {
1081
+ const segmenteur = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
1082
+ for (const { segment } of segmenteur.segment(mot))
1083
+ return segment;
1084
+ return '';
1085
+ }
1086
+ /**
1087
+ * Representation d'une personne.
1088
+ *
1089
+ * ⚠️ Aucune couleur derivee d'un hachage du nom. Une palette engendree a
1090
+ * l'execution n'offre AUCUNE garantie de contraste, ce qui annulerait la
1091
+ * verification des paires semantiques : un nom malchanceux produirait un
1092
+ * avatar illisible, et aucun test ne le verrait.
1093
+ */
1094
+ class StAvatar {
1095
+ nom = input.required(/* @ts-ignore */
1096
+ ...(ngDevMode ? [{ debugName: "nom" }] : /* istanbul ignore next */ []));
1097
+ sourceImage = input(null, /* @ts-ignore */
1098
+ ...(ngDevMode ? [{ debugName: "sourceImage" }] : /* istanbul ignore next */ []));
1099
+ taille = input('md', /* @ts-ignore */
1100
+ ...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
1101
+ forme = input('cercle', /* @ts-ignore */
1102
+ ...(ngDevMode ? [{ debugName: "forme" }] : /* istanbul ignore next */ []));
1103
+ /**
1104
+ * `true` quand le nom figure deja a cote de l'avatar, ce qui est le cas
1105
+ * courant : l'avatar devient alors decoratif et se tait.
1106
+ */
1107
+ nomAdjacent = input(true, /* @ts-ignore */
1108
+ ...(ngDevMode ? [{ debugName: "nomAdjacent" }] : /* istanbul ignore next */ []));
1109
+ imageEchouee = signal(false, /* @ts-ignore */
1110
+ ...(ngDevMode ? [{ debugName: "imageEchouee" }] : /* istanbul ignore next */ []));
1111
+ afficheLImage = computed(() => this.sourceImage() !== null && !this.imageEchouee(), /* @ts-ignore */
1112
+ ...(ngDevMode ? [{ debugName: "afficheLImage" }] : /* istanbul ignore next */ []));
1113
+ initiales = computed(() => this.nom()
1114
+ .trim()
1115
+ .split(/\s+/)
1116
+ .slice(0, 2)
1117
+ .map((mot) => premierGrapheme(mot))
1118
+ .join('')
1119
+ .toUpperCase(), /* @ts-ignore */
1120
+ ...(ngDevMode ? [{ debugName: "initiales" }] : /* istanbul ignore next */ []));
1121
+ /** Vide quand le nom est deja lisible a cote : eviter la double lecture. */
1122
+ texteAlternatif = computed(() => (this.nomAdjacent() ? '' : this.nom()), /* @ts-ignore */
1123
+ ...(ngDevMode ? [{ debugName: "texteAlternatif" }] : /* istanbul ignore next */ []));
1124
+ auxEchecsDImage() {
1125
+ this.imageEchouee.set(true);
1126
+ }
1127
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component });
1128
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StAvatar, isStandalone: true, selector: "st-avatar", inputs: { nom: { classPropertyName: "nom", publicName: "nom", isSignal: true, isRequired: true, transformFunction: null }, sourceImage: { classPropertyName: "sourceImage", publicName: "sourceImage", isSignal: true, isRequired: false, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, forme: { classPropertyName: "forme", publicName: "forme", isSignal: true, isRequired: false, transformFunction: null }, nomAdjacent: { classPropertyName: "nomAdjacent", publicName: "nomAdjacent", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-taille": "taille()", "attr.data-forme": "forme()" }, classAttribute: "st-avatar" }, ngImport: i0, template: "@if (afficheLImage()) {\n <!-- eslint-disable-next-line @angular-eslint/template/prefer-ngsrc --\n NgOptimizedImage exige des dimensions statiques ou un chargeur configure.\n Une photo de profil vient d'une URL tierce de dimensions inconnues, et\n optimiser une image de 40 px ne rapporte rien. -->\n <img [src]=\"sourceImage()\" [alt]=\"texteAlternatif()\" (error)=\"auxEchecsDImage()\" />\n} @else {\n <span class=\"st-avatar__initiales\" [attr.aria-hidden]=\"nomAdjacent() ? 'true' : null\">{{\n initiales()\n }}</span>\n @if (!nomAdjacent()) {\n <span class=\"st-vh\">{{ nom() }}</span>\n }\n}\n", styles: [":host{display:inline-flex;flex:none;align-items:center;justify-content:center;inline-size:var(--st-avatar-taille, 2.5rem);block-size:var(--st-avatar-taille, 2.5rem);overflow:hidden;color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret);border-radius:var(--st-rayon-pleine)}img{inline-size:100%;block-size:100%;object-fit:cover}.st-avatar__initiales{font-size:.4em;font-weight:var(--st-graisse-semi);line-height:1;letter-spacing:.02em}:host([data-forme=carre]){border-radius:var(--st-rayon-md)}:host([data-taille=xs]){--st-avatar-taille: 1.5rem}:host([data-taille=sm]){--st-avatar-taille: 2rem}:host([data-taille=lg]){--st-avatar-taille: 4rem}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1129
+ }
1130
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StAvatar, decorators: [{
1131
+ type: Component,
1132
+ args: [{ selector: 'st-avatar', changeDetection: ChangeDetectionStrategy.OnPush, host: {
1133
+ class: 'st-avatar',
1134
+ '[attr.data-taille]': 'taille()',
1135
+ '[attr.data-forme]': 'forme()',
1136
+ }, template: "@if (afficheLImage()) {\n <!-- eslint-disable-next-line @angular-eslint/template/prefer-ngsrc --\n NgOptimizedImage exige des dimensions statiques ou un chargeur configure.\n Une photo de profil vient d'une URL tierce de dimensions inconnues, et\n optimiser une image de 40 px ne rapporte rien. -->\n <img [src]=\"sourceImage()\" [alt]=\"texteAlternatif()\" (error)=\"auxEchecsDImage()\" />\n} @else {\n <span class=\"st-avatar__initiales\" [attr.aria-hidden]=\"nomAdjacent() ? 'true' : null\">{{\n initiales()\n }}</span>\n @if (!nomAdjacent()) {\n <span class=\"st-vh\">{{ nom() }}</span>\n }\n}\n", styles: [":host{display:inline-flex;flex:none;align-items:center;justify-content:center;inline-size:var(--st-avatar-taille, 2.5rem);block-size:var(--st-avatar-taille, 2.5rem);overflow:hidden;color:var(--st-couleur-sur-primaire-discret);background:var(--st-couleur-primaire-discret);border-radius:var(--st-rayon-pleine)}img{inline-size:100%;block-size:100%;object-fit:cover}.st-avatar__initiales{font-size:.4em;font-weight:var(--st-graisse-semi);line-height:1;letter-spacing:.02em}:host([data-forme=carre]){border-radius:var(--st-rayon-md)}:host([data-taille=xs]){--st-avatar-taille: 1.5rem}:host([data-taille=sm]){--st-avatar-taille: 2rem}:host([data-taille=lg]){--st-avatar-taille: 4rem}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"] }]
1137
+ }], propDecorators: { nom: [{ type: i0.Input, args: [{ isSignal: true, alias: "nom", required: true }] }], sourceImage: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceImage", required: false }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }], forme: [{ type: i0.Input, args: [{ isSignal: true, alias: "forme", required: false }] }], nomAdjacent: [{ type: i0.Input, args: [{ isSignal: true, alias: "nomAdjacent", required: false }] }] } });
1138
+
1139
+ /**
1140
+ * Etat vide : il n'y a rien a montrer, et ce n'est pas une erreur.
1141
+ *
1142
+ * Le niveau du titre est fourni par l'APPELANT : un bloc vide place au milieu
1143
+ * d'une page ne connait pas sa profondeur dans le document, et un niveau devine
1144
+ * casserait la table des matieres que les lecteurs d'ecran construisent.
1145
+ */
1146
+ class StEmpty {
1147
+ titre = input.required(/* @ts-ignore */
1148
+ ...(ngDevMode ? [{ debugName: "titre" }] : /* istanbul ignore next */ []));
1149
+ description = input(null, /* @ts-ignore */
1150
+ ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
1151
+ icone = input('boite-vide', /* @ts-ignore */
1152
+ ...(ngDevMode ? [{ debugName: "icone" }] : /* istanbul ignore next */ []));
1153
+ niveauDuTitre = input(3, /* @ts-ignore */
1154
+ ...(ngDevMode ? [{ debugName: "niveauDuTitre" }] : /* istanbul ignore next */ []));
1155
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StEmpty, deps: [], target: i0.ɵɵFactoryTarget.Component });
1156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StEmpty, isStandalone: true, selector: "st-empty", inputs: { titre: { classPropertyName: "titre", publicName: "titre", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, icone: { classPropertyName: "icone", publicName: "icone", isSignal: true, isRequired: false, transformFunction: null }, niveauDuTitre: { classPropertyName: "niveauDuTitre", publicName: "niveauDuTitre", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "st-empty" }, ngImport: i0, template: "<st-icon [nom]=\"icone()\" taille=\"xl\" ton=\"discret\" />\n<st-heading [niveau]=\"niveauDuTitre()\" taille=\"lg\">{{ titre() }}</st-heading>\n@if (description(); as texte) {\n <st-text variante=\"discret\">{{ texte }}</st-text>\n}\n<div class=\"st-empty__action\"><ng-content /></div>\n", styles: [":host{display:flex;flex-direction:column;gap:var(--st-espacement-3);align-items:center;justify-content:center;min-block-size:12rem;padding:var(--st-espacement-8);text-align:center}.st-empty__action:empty{display:none}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }, { kind: "component", type: StHeading, selector: "st-heading", inputs: ["niveau", "taille", "identifiant"] }, { kind: "component", type: StText, selector: "st-text", inputs: ["variante", "graisse", "enLigne", "tronquerA"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1157
+ }
1158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StEmpty, decorators: [{
1159
+ type: Component,
1160
+ args: [{ selector: 'st-empty', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon, StHeading, StText], host: { class: 'st-empty' }, template: "<st-icon [nom]=\"icone()\" taille=\"xl\" ton=\"discret\" />\n<st-heading [niveau]=\"niveauDuTitre()\" taille=\"lg\">{{ titre() }}</st-heading>\n@if (description(); as texte) {\n <st-text variante=\"discret\">{{ texte }}</st-text>\n}\n<div class=\"st-empty__action\"><ng-content /></div>\n", styles: [":host{display:flex;flex-direction:column;gap:var(--st-espacement-3);align-items:center;justify-content:center;min-block-size:12rem;padding:var(--st-espacement-8);text-align:center}.st-empty__action:empty{display:none}\n"] }]
1161
+ }], propDecorators: { titre: [{ type: i0.Input, args: [{ isSignal: true, alias: "titre", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], icone: [{ type: i0.Input, args: [{ isSignal: true, alias: "icone", required: false }] }], niveauDuTitre: [{ type: i0.Input, args: [{ isSignal: true, alias: "niveauDuTitre", required: false }] }] } });
1162
+
1163
+ const ICONE = {
1164
+ 'hors-ligne': 'hors-ligne',
1165
+ serveur: 'serveur',
1166
+ client: 'triangle-alerte',
1167
+ };
1168
+ /**
1169
+ * Etat d'erreur d'un bloc.
1170
+ *
1171
+ * ⚠️ `titre` et `message` sont deja TRADUITS par l'appelant. Le socle backend
1172
+ * n'emet jamais de phrase destinee a l'utilisateur : il rend un code stable, et
1173
+ * la traduction appartient au client. Le `code` est affiche comme donnee
1174
+ * technique, jamais comme explication.
1175
+ *
1176
+ * ⚠️ `role="alert"` seulement hors d'une region vivante existante : c'est le
1177
+ * seul etat assertif du systeme, et il ne doit pas doubler l'annonce de
1178
+ * `st-state`.
1179
+ */
1180
+ class StError {
1181
+ regionParente = inject(REGION_VIVANTE_PARENTE, { optional: true });
1182
+ variante = input('serveur', /* @ts-ignore */
1183
+ ...(ngDevMode ? [{ debugName: "variante" }] : /* istanbul ignore next */ []));
1184
+ /** Code machine stable, tel que rendu par le backend. */
1185
+ code = input.required(/* @ts-ignore */
1186
+ ...(ngDevMode ? [{ debugName: "code" }] : /* istanbul ignore next */ []));
1187
+ titre = input.required(/* @ts-ignore */
1188
+ ...(ngDevMode ? [{ debugName: "titre" }] : /* istanbul ignore next */ []));
1189
+ message = input.required(/* @ts-ignore */
1190
+ ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
1191
+ traceId = input(null, /* @ts-ignore */
1192
+ ...(ngDevMode ? [{ debugName: "traceId" }] : /* istanbul ignore next */ []));
1193
+ niveauDuTitre = input(3, /* @ts-ignore */
1194
+ ...(ngDevMode ? [{ debugName: "niveauDuTitre" }] : /* istanbul ignore next */ []));
1195
+ rejouable = input(true, /* @ts-ignore */
1196
+ ...(ngDevMode ? [{ debugName: "rejouable" }] : /* istanbul ignore next */ []));
1197
+ libelleRejeu = input('Reessayer', /* @ts-ignore */
1198
+ ...(ngDevMode ? [{ debugName: "libelleRejeu" }] : /* istanbul ignore next */ []));
1199
+ rejeuEnCours = input(false, /* @ts-ignore */
1200
+ ...(ngDevMode ? [{ debugName: "rejeuEnCours" }] : /* istanbul ignore next */ []));
1201
+ etiquetteDeTrace = input('Identifiant de trace', /* @ts-ignore */
1202
+ ...(ngDevMode ? [{ debugName: "etiquetteDeTrace" }] : /* istanbul ignore next */ []));
1203
+ rejeu = output();
1204
+ icone = computed(() => ICONE[this.variante()], /* @ts-ignore */
1205
+ ...(ngDevMode ? [{ debugName: "icone" }] : /* istanbul ignore next */ []));
1206
+ sAnnonce = computed(() => this.regionParente === null, /* @ts-ignore */
1207
+ ...(ngDevMode ? [{ debugName: "sAnnonce" }] : /* istanbul ignore next */ []));
1208
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StError, deps: [], target: i0.ɵɵFactoryTarget.Component });
1209
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StError, isStandalone: true, selector: "st-error", inputs: { variante: { classPropertyName: "variante", publicName: "variante", isSignal: true, isRequired: false, transformFunction: null }, code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: true, transformFunction: null }, titre: { classPropertyName: "titre", publicName: "titre", isSignal: true, isRequired: true, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, traceId: { classPropertyName: "traceId", publicName: "traceId", isSignal: true, isRequired: false, transformFunction: null }, niveauDuTitre: { classPropertyName: "niveauDuTitre", publicName: "niveauDuTitre", isSignal: true, isRequired: false, transformFunction: null }, rejouable: { classPropertyName: "rejouable", publicName: "rejouable", isSignal: true, isRequired: false, transformFunction: null }, libelleRejeu: { classPropertyName: "libelleRejeu", publicName: "libelleRejeu", isSignal: true, isRequired: false, transformFunction: null }, rejeuEnCours: { classPropertyName: "rejeuEnCours", publicName: "rejeuEnCours", isSignal: true, isRequired: false, transformFunction: null }, etiquetteDeTrace: { classPropertyName: "etiquetteDeTrace", publicName: "etiquetteDeTrace", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rejeu: "rejeu" }, host: { properties: { "attr.data-variante": "variante()", "attr.role": "sAnnonce() ? \"alert\" : null" }, classAttribute: "st-error" }, ngImport: i0, template: "<st-icon [nom]=\"icone()\" taille=\"lg\" />\n\n<div class=\"st-error__corps\">\n <st-heading [niveau]=\"niveauDuTitre()\" taille=\"lg\">{{ titre() }}</st-heading>\n <st-text variante=\"corps-sm\">{{ message() }}</st-text>\n\n <p class=\"st-error__technique\">\n <st-code [valeur]=\"code()\" etiquette=\"Code\" [copiable]=\"false\" />\n @if (traceId(); as trace) {\n <st-code [valeur]=\"trace\" [etiquette]=\"etiquetteDeTrace()\" />\n }\n </p>\n\n @if (rejouable()) {\n <button\n stButton\n type=\"button\"\n variante=\"secondaire\"\n [chargement]=\"rejeuEnCours()\"\n (click)=\"rejeu.emit()\"\n >\n {{ libelleRejeu() }}\n </button>\n }\n</div>\n", styles: [":host{display:flex;gap:var(--st-espacement-3);align-items:flex-start;padding:var(--st-espacement-4);color:var(--st-couleur-sur-danger-discret);background:var(--st-couleur-danger-discret);border-inline-start:3px solid var(--st-couleur-danger);border-radius:var(--st-rayon-md)}:host([data-variante=hors-ligne]){color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret);border-inline-start-color:var(--st-couleur-avertissement)}.st-error__corps{display:flex;flex:1;flex-direction:column;gap:var(--st-espacement-2);align-items:flex-start}.st-error__technique{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);margin:0}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }, { kind: "component", type: StHeading, selector: "st-heading", inputs: ["niveau", "taille", "identifiant"] }, { kind: "component", type: StText, selector: "st-text", inputs: ["variante", "graisse", "enLigne", "tronquerA"] }, { kind: "component", type: StButton, selector: "button[stButton]", inputs: ["variante", "taille", "pleineLargeur", "iconeAvant", "iconeApres", "chargement"] }, { kind: "component", type: StCode, selector: "st-code", inputs: ["valeur", "etiquette", "copiable", "libelleCopier", "messageDeConfirmation"], outputs: ["copie"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1210
+ }
1211
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StError, decorators: [{
1212
+ type: Component,
1213
+ args: [{ selector: 'st-error', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon, StHeading, StText, StButton, StCode], host: {
1214
+ class: 'st-error',
1215
+ '[attr.data-variante]': 'variante()',
1216
+ '[attr.role]': 'sAnnonce() ? "alert" : null',
1217
+ }, template: "<st-icon [nom]=\"icone()\" taille=\"lg\" />\n\n<div class=\"st-error__corps\">\n <st-heading [niveau]=\"niveauDuTitre()\" taille=\"lg\">{{ titre() }}</st-heading>\n <st-text variante=\"corps-sm\">{{ message() }}</st-text>\n\n <p class=\"st-error__technique\">\n <st-code [valeur]=\"code()\" etiquette=\"Code\" [copiable]=\"false\" />\n @if (traceId(); as trace) {\n <st-code [valeur]=\"trace\" [etiquette]=\"etiquetteDeTrace()\" />\n }\n </p>\n\n @if (rejouable()) {\n <button\n stButton\n type=\"button\"\n variante=\"secondaire\"\n [chargement]=\"rejeuEnCours()\"\n (click)=\"rejeu.emit()\"\n >\n {{ libelleRejeu() }}\n </button>\n }\n</div>\n", styles: [":host{display:flex;gap:var(--st-espacement-3);align-items:flex-start;padding:var(--st-espacement-4);color:var(--st-couleur-sur-danger-discret);background:var(--st-couleur-danger-discret);border-inline-start:3px solid var(--st-couleur-danger);border-radius:var(--st-rayon-md)}:host([data-variante=hors-ligne]){color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret);border-inline-start-color:var(--st-couleur-avertissement)}.st-error__corps{display:flex;flex:1;flex-direction:column;gap:var(--st-espacement-2);align-items:flex-start}.st-error__technique{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);margin:0}\n"] }]
1218
+ }], propDecorators: { variante: [{ type: i0.Input, args: [{ isSignal: true, alias: "variante", required: false }] }], code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: true }] }], titre: [{ type: i0.Input, args: [{ isSignal: true, alias: "titre", required: true }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], traceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "traceId", required: false }] }], niveauDuTitre: [{ type: i0.Input, args: [{ isSignal: true, alias: "niveauDuTitre", required: false }] }], rejouable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rejouable", required: false }] }], libelleRejeu: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelleRejeu", required: false }] }], rejeuEnCours: [{ type: i0.Input, args: [{ isSignal: true, alias: "rejeuEnCours", required: false }] }], etiquetteDeTrace: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquetteDeTrace", required: false }] }], rejeu: [{ type: i0.Output, args: ["rejeu"] }] } });
1219
+
1220
+ /**
1221
+ * Banniere de perte de connexion.
1222
+ *
1223
+ * ⚠️ `role="status"` (poli) et NON `role="alert"`. Une perte de reseau n'est
1224
+ * pas actionnable dans la seconde ; une annonce assertive interromprait une
1225
+ * saisie en cours. La banniere est persistante, elle sera lue.
1226
+ *
1227
+ * ⚠️ Aucun bouton de fermeture : l'utilisateur ne peut pas fermer un fait.
1228
+ *
1229
+ * ⚠️ `sticky` et non `fixed` : a 200 % d'echelle, un element fixe recouvre le
1230
+ * contenu qu'il annonce.
1231
+ */
1232
+ class StOfflineBanner {
1233
+ message = input.required(/* @ts-ignore */
1234
+ ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
1235
+ libelleRejeu = input(null, /* @ts-ignore */
1236
+ ...(ngDevMode ? [{ debugName: "libelleRejeu" }] : /* istanbul ignore next */ []));
1237
+ rejeuEnCours = input(false, /* @ts-ignore */
1238
+ ...(ngDevMode ? [{ debugName: "rejeuEnCours" }] : /* istanbul ignore next */ []));
1239
+ rejeu = output();
1240
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StOfflineBanner, deps: [], target: i0.ɵɵFactoryTarget.Component });
1241
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StOfflineBanner, isStandalone: true, selector: "st-offline-banner", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, libelleRejeu: { classPropertyName: "libelleRejeu", publicName: "libelleRejeu", isSignal: true, isRequired: false, transformFunction: null }, rejeuEnCours: { classPropertyName: "rejeuEnCours", publicName: "rejeuEnCours", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rejeu: "rejeu" }, host: { attributes: { "role": "status" }, classAttribute: "st-offline-banner" }, ngImport: i0, template: "<st-icon nom=\"hors-ligne\" taille=\"sm\" />\n<span class=\"st-offline-banner__message\">{{ message() }}</span>\n\n@if (libelleRejeu(); as libelle) {\n <button\n stButton\n type=\"button\"\n variante=\"discret\"\n taille=\"sm\"\n [chargement]=\"rejeuEnCours()\"\n (click)=\"rejeu.emit()\"\n >\n {{ libelle }}\n </button>\n}\n", styles: [":host{position:sticky;inset-block-start:0;z-index:var(--st-plan-entete);display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center;min-block-size:2.5rem;padding-block:var(--st-espacement-2);padding-inline:var(--st-espacement-4);color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret);border-block-end:1px solid var(--st-couleur-avertissement)}.st-offline-banner__message{flex:1;font-size:var(--st-taille-sm)}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }, { kind: "component", type: StButton, selector: "button[stButton]", inputs: ["variante", "taille", "pleineLargeur", "iconeAvant", "iconeApres", "chargement"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1242
+ }
1243
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StOfflineBanner, decorators: [{
1244
+ type: Component,
1245
+ args: [{ selector: 'st-offline-banner', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon, StButton], host: {
1246
+ class: 'st-offline-banner',
1247
+ role: 'status',
1248
+ }, template: "<st-icon nom=\"hors-ligne\" taille=\"sm\" />\n<span class=\"st-offline-banner__message\">{{ message() }}</span>\n\n@if (libelleRejeu(); as libelle) {\n <button\n stButton\n type=\"button\"\n variante=\"discret\"\n taille=\"sm\"\n [chargement]=\"rejeuEnCours()\"\n (click)=\"rejeu.emit()\"\n >\n {{ libelle }}\n </button>\n}\n", styles: [":host{position:sticky;inset-block-start:0;z-index:var(--st-plan-entete);display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center;min-block-size:2.5rem;padding-block:var(--st-espacement-2);padding-inline:var(--st-espacement-4);color:var(--st-couleur-sur-avertissement-discret);background:var(--st-couleur-avertissement-discret);border-block-end:1px solid var(--st-couleur-avertissement)}.st-offline-banner__message{flex:1;font-size:var(--st-taille-sm)}\n"] }]
1249
+ }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], libelleRejeu: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelleRejeu", required: false }] }], rejeuEnCours: [{ type: i0.Input, args: [{ isSignal: true, alias: "rejeuEnCours", required: false }] }], rejeu: [{ type: i0.Output, args: ["rejeu"] }] } });
1250
+
1251
+ const ANNONCES_PAR_DEFAUT = {
1252
+ chargement: 'Chargement en cours',
1253
+ erreur: 'Une erreur est survenue',
1254
+ vide: 'Aucun resultat',
1255
+ 'hors-ligne': 'Hors connexion',
1256
+ succes: '',
1257
+ };
1258
+ /**
1259
+ * Impose les cinq etats a un bloc qui consomme l'API, et centralise leur
1260
+ * annonce.
1261
+ *
1262
+ * ⚠️ L'hote ne porte PAS `role="status"`, contrairement a ce qu'on attendrait :
1263
+ * un remplacement de contenu A L'INTERIEUR d'une region vivante fait relire
1264
+ * tout le sous-arbre par le lecteur d'ecran a chaque bascule d'etat. Seule une
1265
+ * petite zone masquee porte l'annonce, qui tient en quelques mots. L'hote se
1266
+ * contente d'`aria-busy`.
1267
+ *
1268
+ * Il fournit `REGION_VIVANTE_PARENTE` : les composants qu'il rend (indicateur
1269
+ * de chargement, erreur) detectent sa presence et se taisent, au lieu
1270
+ * d'annoncer une deuxieme fois le meme fait.
1271
+ */
1272
+ class StState {
1273
+ etat = input.required(/* @ts-ignore */
1274
+ ...(ngDevMode ? [{ debugName: "etat" }] : /* istanbul ignore next */ []));
1275
+ /** Le chemin nominal. Obligatoire : un bloc sans succes n'a pas de raison d'etre. */
1276
+ succes = input.required(/* @ts-ignore */
1277
+ ...(ngDevMode ? [{ debugName: "succes" }] : /* istanbul ignore next */ []));
1278
+ chargement = input(null, /* @ts-ignore */
1279
+ ...(ngDevMode ? [{ debugName: "chargement" }] : /* istanbul ignore next */ []));
1280
+ erreur = input(null, /* @ts-ignore */
1281
+ ...(ngDevMode ? [{ debugName: "erreur" }] : /* istanbul ignore next */ []));
1282
+ vide = input(null, /* @ts-ignore */
1283
+ ...(ngDevMode ? [{ debugName: "vide" }] : /* istanbul ignore next */ []));
1284
+ horsLigne = input(null, /* @ts-ignore */
1285
+ ...(ngDevMode ? [{ debugName: "horsLigne" }] : /* istanbul ignore next */ []));
1286
+ /** Silhouette par defaut du chargement. `null` : un indicateur tournant. */
1287
+ squelette = input('texte', /* @ts-ignore */
1288
+ ...(ngDevMode ? [{ debugName: "squelette" }] : /* istanbul ignore next */ []));
1289
+ lignesDeSquelette = input(3, /* @ts-ignore */
1290
+ ...(ngDevMode ? [{ debugName: "lignesDeSquelette" }] : /* istanbul ignore next */ []));
1291
+ annonces = input(ANNONCES_PAR_DEFAUT, /* @ts-ignore */
1292
+ ...(ngDevMode ? [{ debugName: "annonces" }] : /* istanbul ignore next */ []));
1293
+ annonce = computed(() => this.annonces()[this.etat()], /* @ts-ignore */
1294
+ ...(ngDevMode ? [{ debugName: "annonce" }] : /* istanbul ignore next */ []));
1295
+ /**
1296
+ * Les etats degrades n'ont pas de rendu par defaut : afficher un texte
1297
+ * generique a la place d'un vrai ecran vide ou d'une vraie erreur donnerait
1298
+ * l'illusion que les cinq etats sont traites. Il vaut mieux que le manque se
1299
+ * voie, en developpement.
1300
+ */
1301
+ gabaritDeLEtat = computed(() => {
1302
+ switch (this.etat()) {
1303
+ case 'erreur':
1304
+ return this.signalerSiAbsent(this.erreur(), 'erreur');
1305
+ case 'vide':
1306
+ return this.signalerSiAbsent(this.vide(), 'vide');
1307
+ case 'hors-ligne':
1308
+ return this.signalerSiAbsent(this.horsLigne() ?? this.erreur(), 'hors-ligne');
1309
+ case 'chargement':
1310
+ return this.chargement();
1311
+ default:
1312
+ return this.succes();
1313
+ }
1314
+ }, /* @ts-ignore */
1315
+ ...(ngDevMode ? [{ debugName: "gabaritDeLEtat" }] : /* istanbul ignore next */ []));
1316
+ montreLaSilhouetteParDefaut = computed(() => this.etat() === 'chargement' && this.chargement() === null, /* @ts-ignore */
1317
+ ...(ngDevMode ? [{ debugName: "montreLaSilhouetteParDefaut" }] : /* istanbul ignore next */ []));
1318
+ signalerSiAbsent(gabarit, nom) {
1319
+ if (gabarit === null && isDevMode()) {
1320
+ // Volontairement un avertissement et non une exception : casser l'ecran
1321
+ // entier pour un etat degrade non traite aggraverait la panne en
1322
+ // production. En developpement, le message est impossible a manquer.
1323
+ console.warn(`[st-state] Etat « ${nom} » atteint sans gabarit. Les cinq etats doivent etre traites : ` +
1324
+ 'fournir [erreur], [vide] et [horsLigne].');
1325
+ }
1326
+ return gabarit;
1327
+ }
1328
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StState, deps: [], target: i0.ɵɵFactoryTarget.Component });
1329
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StState, isStandalone: true, selector: "st-state", inputs: { etat: { classPropertyName: "etat", publicName: "etat", isSignal: true, isRequired: true, transformFunction: null }, succes: { classPropertyName: "succes", publicName: "succes", isSignal: true, isRequired: true, transformFunction: null }, chargement: { classPropertyName: "chargement", publicName: "chargement", isSignal: true, isRequired: false, transformFunction: null }, erreur: { classPropertyName: "erreur", publicName: "erreur", isSignal: true, isRequired: false, transformFunction: null }, vide: { classPropertyName: "vide", publicName: "vide", isSignal: true, isRequired: false, transformFunction: null }, horsLigne: { classPropertyName: "horsLigne", publicName: "horsLigne", isSignal: true, isRequired: false, transformFunction: null }, squelette: { classPropertyName: "squelette", publicName: "squelette", isSignal: true, isRequired: false, transformFunction: null }, lignesDeSquelette: { classPropertyName: "lignesDeSquelette", publicName: "lignesDeSquelette", isSignal: true, isRequired: false, transformFunction: null }, annonces: { classPropertyName: "annonces", publicName: "annonces", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-etat": "etat()", "attr.aria-busy": "etat() === \"chargement\" ? \"true\" : null" }, classAttribute: "st-state" }, providers: [{ provide: REGION_VIVANTE_PARENTE, useValue: true }], ngImport: i0, template: "@if (montreLaSilhouetteParDefaut()) {\n @if (squelette(); as variante) {\n <st-skeleton [variante]=\"variante\" [lignes]=\"lignesDeSquelette()\" />\n } @else {\n <st-spinner />\n }\n} @else if (gabaritDeLEtat(); as gabarit) {\n <ng-container [ngTemplateOutlet]=\"gabarit\" />\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce() }}</span>\n", styles: [":host{display:block}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StSkeleton, selector: "st-skeleton", inputs: ["variante", "lignes", "colonnes", "anime"] }, { kind: "component", type: StSpinner, selector: "st-spinner", inputs: ["taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1330
+ }
1331
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StState, decorators: [{
1332
+ type: Component,
1333
+ args: [{ selector: 'st-state', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, StSkeleton, StSpinner], providers: [{ provide: REGION_VIVANTE_PARENTE, useValue: true }], host: {
1334
+ class: 'st-state',
1335
+ '[attr.data-etat]': 'etat()',
1336
+ '[attr.aria-busy]': 'etat() === "chargement" ? "true" : null',
1337
+ }, template: "@if (montreLaSilhouetteParDefaut()) {\n @if (squelette(); as variante) {\n <st-skeleton [variante]=\"variante\" [lignes]=\"lignesDeSquelette()\" />\n } @else {\n <st-spinner />\n }\n} @else if (gabaritDeLEtat(); as gabarit) {\n <ng-container [ngTemplateOutlet]=\"gabarit\" />\n}\n\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce() }}</span>\n", styles: [":host{display:block}.st-vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}\n"] }]
1338
+ }], propDecorators: { etat: [{ type: i0.Input, args: [{ isSignal: true, alias: "etat", required: true }] }], succes: [{ type: i0.Input, args: [{ isSignal: true, alias: "succes", required: true }] }], chargement: [{ type: i0.Input, args: [{ isSignal: true, alias: "chargement", required: false }] }], erreur: [{ type: i0.Input, args: [{ isSignal: true, alias: "erreur", required: false }] }], vide: [{ type: i0.Input, args: [{ isSignal: true, alias: "vide", required: false }] }], horsLigne: [{ type: i0.Input, args: [{ isSignal: true, alias: "horsLigne", required: false }] }], squelette: [{ type: i0.Input, args: [{ isSignal: true, alias: "squelette", required: false }] }], lignesDeSquelette: [{ type: i0.Input, args: [{ isSignal: true, alias: "lignesDeSquelette", required: false }] }], annonces: [{ type: i0.Input, args: [{ isSignal: true, alias: "annonces", required: false }] }] } });
1339
+
1340
+ /**
1341
+ * @starterlib/ui — bibliotheque de composants du socle frontal.
1342
+ *
1343
+ * Tout composant s'emploie sans enveloppe : les elements interactifs sont des
1344
+ * directives posees sur les elements natifs (`button[stButton]`), jamais des
1345
+ * balises qui en enrobent un.
1346
+ */
1347
+
1348
+ /**
1349
+ * Generated bundle index. Do not edit.
1350
+ */
1351
+
1352
+ export { CHEMIN_DU_SPRITE, ETATS_DE_BLOC, FUSEAU_PAR_DEFAUT, NOMS_DICONE, REGION_VIVANTE_PARENTE, StAvatar, StBadge, StButton, StButtonGroup, StCode, StCount, StDate, StEmpty, StError, StHeading, StIcon, StIconButton, StLink, StMasked, StMoney, StOfflineBanner, StProgress, StRelativeTime, StSkeleton, StSpinner, StState, StStatusPill, StTag, StText };
1353
+ //# sourceMappingURL=starterlib-ui.mjs.map