@starterlib/ui 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -8
- package/assets/icones/sprite.svg +1 -0
- package/fesm2022/starterlib-ui-interactions.mjs +59 -2
- package/fesm2022/starterlib-ui-interactions.mjs.map +1 -1
- package/fesm2022/starterlib-ui.mjs +695 -12
- package/fesm2022/starterlib-ui.mjs.map +1 -1
- package/package.json +3 -3
- package/types/starterlib-ui-interactions.d.ts +49 -2
- package/types/starterlib-ui-surfaces.d.ts +1 -1
- package/types/starterlib-ui.d.ts +458 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, LOCALE_ID, signal, input, computed, ChangeDetectionStrategy, Component, booleanAttribute, ElementRef, DestroyRef, output, model, afterNextRender, viewChild, Directive, effect, Injector, linkedSignal, TemplateRef, contentChild, isDevMode } from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, LOCALE_ID, signal, input, computed, ChangeDetectionStrategy, Component, booleanAttribute, ElementRef, DestroyRef, output, model, afterNextRender, viewChild, Directive, effect, Injector, linkedSignal, TemplateRef, contentChildren, contentChild, isDevMode } from '@angular/core';
|
|
3
3
|
import { masquerIban, masquerTelephone, masquerCarte, quatreDerniers, formaterIban, formaterMontant, formaterTaille, formaterDateLongue, montant, decimalesDe, motifDeDate, formaterDateSaisie, ibanEstValide, normaliserTelephone, typeDeFichierAccepte, normaliserIban, bornerDate, premierJourDeLaSemaine, ajouterJours, semainesDuMois, ajouterMois, jourDeLaSemaine, CHOIX_DE_THEME, LANGUES } from '@starterlib/util';
|
|
4
4
|
export { ETATS_DE_BLOC } from '@starterlib/util';
|
|
5
5
|
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
@@ -116,6 +116,27 @@ const LIBELLES_FR = {
|
|
|
116
116
|
tropDeFichiers: (nombre) => `Choisissez au plus ${String(nombre)} fichier${nombre > 1 ? 's' : ''}.`,
|
|
117
117
|
typesAcceptes: (types) => `Types accept\u00e9s\u00a0: ${types}.`,
|
|
118
118
|
tailleMaximale: (taille) => `${taille} au plus par fichier.`,
|
|
119
|
+
pagination: 'Pagination',
|
|
120
|
+
pagePrecedente: 'Page pr\u00e9c\u00e9dente',
|
|
121
|
+
pageSuivante: 'Page suivante',
|
|
122
|
+
pageNumero: (numero) => `Page ${String(numero)}`,
|
|
123
|
+
afficherPlus: 'Afficher plus',
|
|
124
|
+
lignesParPage: 'Lignes par page',
|
|
125
|
+
selectionnerLaPage: (nombre) => `S\u00e9lectionner les ${String(nombre)} lignes de la page`,
|
|
126
|
+
selectionnerLaLigne: (ligne) => `S\u00e9lectionner ${ligne}`,
|
|
127
|
+
lignesSelectionnees: (nombre) => nombre === 0
|
|
128
|
+
? 'Aucune ligne s\u00e9lectionn\u00e9e'
|
|
129
|
+
: `${String(nombre)} ligne${nombre > 1 ? 's' : ''} s\u00e9lectionn\u00e9e${nombre > 1 ? 's' : ''}`,
|
|
130
|
+
trieCroissant: (colonne) => `Tri\u00e9 par ${colonne}, ordre croissant`,
|
|
131
|
+
trieDecroissant: (colonne) => `Tri\u00e9 par ${colonne}, ordre d\u00e9croissant`,
|
|
132
|
+
tableauVide: 'Aucune ligne \u00e0 afficher.',
|
|
133
|
+
nonRenseigne: 'Non renseign\u00e9',
|
|
134
|
+
filtres: 'Filtres',
|
|
135
|
+
filtresActifs: 'Filtres actifs',
|
|
136
|
+
retirerLeFiltre: (filtre) => `Retirer le filtre ${filtre}`,
|
|
137
|
+
effacerLesFiltres: 'Effacer les filtres',
|
|
138
|
+
filtreRetire: (filtre) => `Filtre ${filtre} retir\u00e9`,
|
|
139
|
+
filtresEffaces: 'Filtres effac\u00e9s',
|
|
119
140
|
confirmer: 'Confirmer',
|
|
120
141
|
annuler: 'Annuler',
|
|
121
142
|
recopierPourConfirmer: (texte) => `Pour confirmer, recopiez \u00ab\u202f${texte}\u202f\u00bb`,
|
|
@@ -210,6 +231,25 @@ const LIBELLES_EN = {
|
|
|
210
231
|
tropDeFichiers: (nombre) => `Choose no more than ${String(nombre)} file${nombre > 1 ? 's' : ''}.`,
|
|
211
232
|
typesAcceptes: (types) => `Accepted types: ${types}.`,
|
|
212
233
|
tailleMaximale: (taille) => `${taille} maximum per file.`,
|
|
234
|
+
pagination: 'Pagination',
|
|
235
|
+
pagePrecedente: 'Previous page',
|
|
236
|
+
pageSuivante: 'Next page',
|
|
237
|
+
pageNumero: (numero) => `Page ${String(numero)}`,
|
|
238
|
+
afficherPlus: 'Show more',
|
|
239
|
+
lignesParPage: 'Rows per page',
|
|
240
|
+
selectionnerLaPage: (nombre) => `Select the ${String(nombre)} rows on this page`,
|
|
241
|
+
selectionnerLaLigne: (ligne) => `Select ${ligne}`,
|
|
242
|
+
lignesSelectionnees: (nombre) => nombre === 0 ? 'No rows selected' : `${String(nombre)} row${nombre > 1 ? 's' : ''} selected`,
|
|
243
|
+
trieCroissant: (colonne) => `Sorted by ${colonne}, ascending`,
|
|
244
|
+
trieDecroissant: (colonne) => `Sorted by ${colonne}, descending`,
|
|
245
|
+
tableauVide: 'No rows to display.',
|
|
246
|
+
nonRenseigne: 'Not provided',
|
|
247
|
+
filtres: 'Filters',
|
|
248
|
+
filtresActifs: 'Active filters',
|
|
249
|
+
retirerLeFiltre: (filtre) => `Remove filter ${filtre}`,
|
|
250
|
+
effacerLesFiltres: 'Clear filters',
|
|
251
|
+
filtreRetire: (filtre) => `Filter ${filtre} removed`,
|
|
252
|
+
filtresEffaces: 'Filters cleared',
|
|
213
253
|
confirmer: 'Confirm',
|
|
214
254
|
annuler: 'Cancel',
|
|
215
255
|
recopierPourConfirmer: (texte) => `To confirm, type \u201c${texte}\u201d`,
|
|
@@ -507,6 +547,7 @@ const NOMS_DICONE = [
|
|
|
507
547
|
'recherche',
|
|
508
548
|
'serveur',
|
|
509
549
|
'televerser',
|
|
550
|
+
'tri',
|
|
510
551
|
'triangle-alerte',
|
|
511
552
|
'utilisateur',
|
|
512
553
|
];
|
|
@@ -2506,7 +2547,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
2506
2547
|
type: Directive
|
|
2507
2548
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
|
|
2508
2549
|
|
|
2509
|
-
let compteur$
|
|
2550
|
+
let compteur$8 = 0;
|
|
2510
2551
|
/** Elements qu'un `label for` etiquette. */
|
|
2511
2552
|
const ELEMENTS_ETIQUETABLES = new Set([
|
|
2512
2553
|
'INPUT',
|
|
@@ -2574,7 +2615,7 @@ class StFormField {
|
|
|
2574
2615
|
libelleMasque = input(false, { ...(ngDevMode ? { debugName: "libelleMasque" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2575
2616
|
/** Identifiants du champ, lus par `stControleDeChamp` et par le gabarit. */
|
|
2576
2617
|
identifiants = (() => {
|
|
2577
|
-
const base = `st-champ-${(compteur$
|
|
2618
|
+
const base = `st-champ-${(compteur$8 += 1).toString()}`;
|
|
2578
2619
|
return {
|
|
2579
2620
|
controle: base,
|
|
2580
2621
|
libelle: `${base}-libelle`,
|
|
@@ -2777,7 +2818,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
2777
2818
|
args: [{ selector: 'st-textarea', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StFormField, StControleDeChamp], host: { class: 'st-textarea' }, template: "<st-form-field\n [libelle]=\"libelle()\"\n [aide]=\"aide()\"\n [complement]=\"compteur()\"\n [erreurs]=\"errors()\"\n [afficherErreurs]=\"touched()\"\n [obligatoire]=\"required()\"\n>\n <textarea\n #champ\n class=\"st-textarea__champ\"\n stControleDeChamp\n [rows]=\"lignes()\"\n [value]=\"value()\"\n [attr.name]=\"name() || null\"\n [disabled]=\"disabled()\"\n [readOnly]=\"readonly()\"\n (input)=\"surSaisie($event)\"\n (blur)=\"touch.emit()\"\n ></textarea>\n</st-form-field>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-textarea__champ:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-textarea__champ{box-sizing:border-box;inline-size:100%;min-block-size:var(--st-cible-tactile-min);padding:var(--st-espacement-2) var(--st-espacement-3);font:inherit;color:var(--st-couleur-texte);background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-sm)}.st-textarea__champ[aria-invalid=true]{border-color:var(--st-couleur-danger);box-shadow:inset 0 0 0 1px var(--st-couleur-danger)}.st-textarea__champ:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;background:var(--st-couleur-surface-enfoncee)}.st-textarea__champ:read-only:not(:disabled){background:var(--st-couleur-surface-enfoncee)}.st-textarea__champ{resize:vertical}\n"] }]
|
|
2778
2819
|
}], ctorParameters: () => [], propDecorators: { lignes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lignes", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }] } });
|
|
2779
2820
|
|
|
2780
|
-
let compteur$
|
|
2821
|
+
let compteur$7 = 0;
|
|
2781
2822
|
/**
|
|
2782
2823
|
* Groupe de controles, nomme par sa QUESTION.
|
|
2783
2824
|
*
|
|
@@ -2805,7 +2846,7 @@ class StFieldset {
|
|
|
2805
2846
|
...(ngDevMode ? [{ debugName: "erreurs" }] : /* istanbul ignore next */ []));
|
|
2806
2847
|
afficherErreurs = input(false, { ...(ngDevMode ? { debugName: "afficherErreurs" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2807
2848
|
obligatoire = input(false, { ...(ngDevMode ? { debugName: "obligatoire" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2808
|
-
base = `st-groupe-${(compteur$
|
|
2849
|
+
base = `st-groupe-${(compteur$7 += 1).toString()}`;
|
|
2809
2850
|
identifiantAide = `${this.base}-aide`;
|
|
2810
2851
|
identifiantErreur = `${this.base}-erreur`;
|
|
2811
2852
|
libelleObligatoire = computed(() => this.libelles().obligatoire, /* @ts-ignore */
|
|
@@ -2890,7 +2931,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
2890
2931
|
}, template: "<st-form-field\n disposition=\"case\"\n [libelle]=\"libelle()\"\n [aide]=\"aide()\"\n [erreurs]=\"errors()\"\n [afficherErreurs]=\"touched()\"\n [obligatoire]=\"required()\"\n>\n <input\n #champ\n class=\"st-checkbox__case\"\n type=\"checkbox\"\n stControleDeChamp\n [checked]=\"checked()\"\n [attr.name]=\"name() || null\"\n [disabled]=\"disabled()\"\n (change)=\"surChangement($event)\"\n (blur)=\"touch.emit()\"\n />\n</st-form-field>\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([hidden]){display:none!important}:host{display:block}.st-checkbox__case:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-checkbox__case{inline-size:1.5rem;block-size:1.5rem;margin:0;cursor:pointer;accent-color:var(--st-couleur-primaire)}.st-checkbox__case:disabled{cursor:not-allowed}\n"] }]
|
|
2891
2932
|
}], propDecorators: { libelle: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelle", required: true }] }], aide: [{ type: i0.Input, args: [{ isSignal: true, alias: "aide", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], champ: [{ type: i0.ViewChild, args: ['champ', { isSignal: true }] }] } });
|
|
2892
2933
|
|
|
2893
|
-
let compteur$
|
|
2934
|
+
let compteur$6 = 0;
|
|
2894
2935
|
/**
|
|
2895
2936
|
* Groupe de boutons radio, integre a Signal Forms.
|
|
2896
2937
|
*
|
|
@@ -2930,7 +2971,7 @@ class StRadioGroup {
|
|
|
2930
2971
|
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
2931
2972
|
touch = output();
|
|
2932
2973
|
/** Nom partage par les options : sans lui, chaque radio forme son propre groupe. */
|
|
2933
|
-
nomParDefaut = `st-radio-${(compteur$
|
|
2974
|
+
nomParDefaut = `st-radio-${(compteur$6 += 1).toString()}`;
|
|
2934
2975
|
nomDuGroupe() {
|
|
2935
2976
|
return this.name() === '' ? this.nomParDefaut : this.name();
|
|
2936
2977
|
}
|
|
@@ -3017,7 +3058,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
3017
3058
|
}, template: "<st-form-field disposition=\"case\" [libelle]=\"libelle()\" [aide]=\"aide()\">\n <input\n #champ\n class=\"st-switch__interrupteur\"\n type=\"checkbox\"\n role=\"switch\"\n stControleDeChamp\n [checked]=\"checked()\"\n [attr.aria-checked]=\"checked()\"\n [attr.name]=\"name() || null\"\n [disabled]=\"disabled()\"\n (change)=\"surChangement($event)\"\n (blur)=\"touch.emit()\"\n />\n</st-form-field>\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([hidden]){display:none!important}:host{display:block}.st-switch__interrupteur:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-switch__interrupteur{position:relative;inline-size:2.75rem;block-size:1.5rem;margin:0;cursor:pointer;background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-pleine);appearance:none}.st-switch__interrupteur:after{position:absolute;inset-block-start:50%;inset-inline-start:.125rem;inline-size:1.125rem;block-size:1.125rem;content:\"\";background:var(--st-couleur-texte-discret);border-radius:var(--st-rayon-pleine);transition:inset-inline-start var(--st-duree-rapide) ease;transform:translateY(-50%)}.st-switch__interrupteur:checked{background:var(--st-couleur-primaire);border-color:var(--st-couleur-primaire)}.st-switch__interrupteur:checked:after{inset-inline-start:calc(100% - 1.25rem);background:var(--st-couleur-sur-primaire)}.st-switch__interrupteur:disabled{cursor:not-allowed;opacity:.6}@media(forced-colors:active){.st-switch__interrupteur{border-color:CanvasText}.st-switch__interrupteur:after{background:CanvasText}.st-switch__interrupteur:checked:after{background:Highlight}}\n"] }]
|
|
3018
3059
|
}], propDecorators: { libelle: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelle", required: true }] }], aide: [{ type: i0.Input, args: [{ isSignal: true, alias: "aide", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], champ: [{ type: i0.ViewChild, args: ['champ', { isSignal: true }] }] } });
|
|
3019
3060
|
|
|
3020
|
-
let compteur$
|
|
3061
|
+
let compteur$5 = 0;
|
|
3021
3062
|
/**
|
|
3022
3063
|
* Recapitulatif des erreurs d'un formulaire, a l'envoi.
|
|
3023
3064
|
*
|
|
@@ -3057,7 +3098,7 @@ class StErrorSummary {
|
|
|
3057
3098
|
titre = input(null, /* @ts-ignore */
|
|
3058
3099
|
...(ngDevMode ? [{ debugName: "titre" }] : /* istanbul ignore next */ []));
|
|
3059
3100
|
/** Unique par instance : deux recapitulatifs sur une page partageraient leur titre. */
|
|
3060
|
-
identifiantDuTitre = `st-recapitulatif-${(compteur$
|
|
3101
|
+
identifiantDuTitre = `st-recapitulatif-${(compteur$5 += 1).toString()}`;
|
|
3061
3102
|
lignes = computed(() => {
|
|
3062
3103
|
const libelles = this.libelles();
|
|
3063
3104
|
const locale = this.locale();
|
|
@@ -3467,7 +3508,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
3467
3508
|
args: [{ selector: 'st-select', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StFormField, StControleDeChamp, StIcon], host: { class: 'st-select' }, template: "<st-form-field\n [libelle]=\"libelle()\"\n [aide]=\"aide()\"\n [erreurs]=\"errors()\"\n [afficherErreurs]=\"touched()\"\n [obligatoire]=\"required()\"\n>\n <div class=\"st-select__cadre\">\n <select\n #champ\n class=\"st-select__champ\"\n stControleDeChamp\n [attr.name]=\"name() || null\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.aria-readonly]=\"readonly() ? 'true' : null\"\n [disabled]=\"disabled()\"\n (change)=\"surChoix($event)\"\n (blur)=\"touch.emit()\"\n >\n <option value=\"\" [selected]=\"value() === ''\">{{ texteVide() ?? libelles().choisir }}</option>\n @for (bloc of blocs(); track $index) {\n @if (bloc.groupe === null) {\n @for (option of bloc.options; track option.valeur) {\n <option\n [value]=\"option.valeur\"\n [selected]=\"option.valeur === value()\"\n [disabled]=\"option.desactivee === true\"\n >\n {{ option.libelle }}\n </option>\n }\n } @else {\n <optgroup [label]=\"bloc.groupe\">\n @for (option of bloc.options; track option.valeur) {\n <option\n [value]=\"option.valeur\"\n [selected]=\"option.valeur === value()\"\n [disabled]=\"option.desactivee === true\"\n >\n {{ option.libelle }}\n </option>\n }\n </optgroup>\n }\n }\n </select>\n <st-icon class=\"st-select__chevron\" nom=\"chevron-bas\" taille=\"sm\" />\n </div>\n</st-form-field>\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([hidden]){display:none!important}:host{display:block}.st-select__cadre{position:relative}.st-select__champ:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-select__champ{box-sizing:border-box;inline-size:100%;min-block-size:var(--st-cible-tactile-min);padding:var(--st-espacement-2) var(--st-espacement-3);font:inherit;color:var(--st-couleur-texte);background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-sm)}.st-select__champ[aria-invalid=true]{border-color:var(--st-couleur-danger);box-shadow:inset 0 0 0 1px var(--st-couleur-danger)}.st-select__champ:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;background:var(--st-couleur-surface-enfoncee)}.st-select__champ:read-only:not(:disabled){background:var(--st-couleur-surface-enfoncee)}.st-select__champ{padding-inline-end:calc(var(--st-espacement-3) * 2 + 1rem);cursor:pointer;appearance:none}.st-select__chevron{position:absolute;inset-block-start:50%;inset-inline-end:var(--st-espacement-3);color:var(--st-couleur-texte-discret);pointer-events:none;transform:translateY(-50%)}@media(forced-colors:active){.st-select__chevron{color:CanvasText}}\n"] }]
|
|
3468
3509
|
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], texteVide: [{ type: i0.Input, args: [{ isSignal: true, alias: "texteVide", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }] } });
|
|
3469
3510
|
|
|
3470
|
-
let compteur$
|
|
3511
|
+
let compteur$4 = 0;
|
|
3471
3512
|
/** Un lundi connu, pour nommer les jours de la semaine sans horloge. */
|
|
3472
3513
|
const LUNDI_DE_REFERENCE = '2006-01-02';
|
|
3473
3514
|
/** « Aujourd'hui » dans un fuseau : la date du poste n'est pas celle de la personne. */
|
|
@@ -3522,7 +3563,7 @@ class StCalendar {
|
|
|
3522
3563
|
/** Echap : le conteneur decide de fermer, et ou rendre le focus. */
|
|
3523
3564
|
fermeture = output();
|
|
3524
3565
|
identifiants = (() => {
|
|
3525
|
-
const base = `st-calendrier-${(compteur$
|
|
3566
|
+
const base = `st-calendrier-${(compteur$4 += 1).toString()}`;
|
|
3526
3567
|
return { titre: `${base}-titre`, aide: `${base}-aide` };
|
|
3527
3568
|
})();
|
|
3528
3569
|
/** La date qui porte le focus. Suit la selection, bornee. */
|
|
@@ -3781,6 +3822,648 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
3781
3822
|
}, template: "<st-form-field\n [libelle]=\"libelle()\"\n [aide]=\"aideLue()\"\n [erreurs]=\"errors()\"\n [afficherErreurs]=\"touched()\"\n [obligatoire]=\"required()\"\n>\n <div\n class=\"st-file-upload__zone\"\n [attr.data-survol]=\"survol() ? '' : null\"\n [attr.data-desactive]=\"disabled() ? '' : null\"\n (dragover)=\"surSurvol($event)\"\n (dragleave)=\"survol.set(false)\"\n (drop)=\"surDepot($event)\"\n >\n <input\n #champ\n class=\"st-file-upload__natif\"\n stControleDeChamp\n type=\"file\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [attr.name]=\"name() || null\"\n [disabled]=\"disabled() || readonly()\"\n (change)=\"surChoix($event)\"\n />\n <!-- Voie de la souris vers le champ natif. Masque au lecteur d'ecran et hors\n tabulation : le clavier et le lecteur d'ecran passent par le champ natif,\n deja nomme par l'etiquette du champ. -->\n <span class=\"st-file-upload__bouton\" aria-hidden=\"true\" (click)=\"champ.click()\">\n <st-icon nom=\"televerser\" taille=\"sm\" />\n {{ multiple() ? libelles().choisirDesFichiers : libelles().choisirUnFichier }}\n </span>\n @if (multiple()) {\n <span class=\"st-file-upload__depot\" aria-hidden=\"true\">{{ libelles().ouDeposerIci }}</span>\n }\n </div>\n\n @if (lignes().length > 0) {\n <ul class=\"st-file-upload__liste\">\n @for (ligne of lignes(); track ligne.cle; let indice = $index) {\n <li class=\"st-file-upload__fichier\">\n <st-icon nom=\"fichier\" taille=\"sm\" />\n <span class=\"st-file-upload__nom\">{{ ligne.fichier.name }}</span>\n <span class=\"st-file-upload__taille\">{{ ligne.taille }}</span>\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stIconButton, et l'etiquette est requise. -->\n <button\n class=\"st-file-upload__retrait\"\n stIconButton\n type=\"button\"\n icone=\"corbeille\"\n taille=\"sm\"\n [etiquette]=\"libelles().retirerLeFichier(ligne.fichier.name)\"\n [disabled]=\"disabled() || readonly()\"\n (click)=\"retirer(indice)\"\n ></button>\n </li>\n }\n </ul>\n }\n</st-form-field>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-file-upload__zone{position:relative;display:flex;flex-wrap:wrap;gap:var(--st-espacement-2) var(--st-espacement-3);align-items:center;padding:var(--st-espacement-3);border:1px dashed var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-md)}.st-file-upload__zone[data-survol]{background:var(--st-couleur-surface-enfoncee);border-style:solid}.st-file-upload__natif{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.st-file-upload__bouton{display:inline-flex;gap:var(--st-espacement-2);align-items:center;min-block-size:var(--st-cible-tactile-min);padding:var(--st-espacement-2) var(--st-espacement-4);font-weight:var(--st-graisse-semi);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-sm)}.st-file-upload__natif:focus-visible+.st-file-upload__bouton{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}[aria-invalid=true]+.st-file-upload__bouton{border-color:var(--st-couleur-danger);box-shadow:inset 0 0 0 1px var(--st-couleur-danger)}.st-file-upload__natif:disabled+.st-file-upload__bouton{color:var(--st-couleur-texte-desactive);cursor:not-allowed;background:var(--st-couleur-surface-enfoncee)}.st-file-upload__depot{color:var(--st-couleur-texte-discret)}.st-file-upload__liste{display:grid;gap:var(--st-espacement-1);padding:0;margin:var(--st-espacement-2) 0 0;list-style:none}.st-file-upload__fichier{display:flex;gap:var(--st-espacement-2);align-items:center}.st-file-upload__nom{min-inline-size:0;overflow-wrap:anywhere}.st-file-upload__taille{flex:none;color:var(--st-couleur-texte-discret);font-variant-numeric:tabular-nums}\n"] }]
|
|
3782
3823
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], tailleMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "tailleMax", required: false }] }] } });
|
|
3783
3824
|
|
|
3825
|
+
/** Plafond du backend : au-dela, la requete est refusee (SEC-7 de la vague 4). */
|
|
3826
|
+
const TAILLE_MAXIMALE = 100;
|
|
3827
|
+
/**
|
|
3828
|
+
* Pagination d'une liste par curseur.
|
|
3829
|
+
*
|
|
3830
|
+
* ```html
|
|
3831
|
+
* <st-data-table #tableau … />
|
|
3832
|
+
* <st-pagination
|
|
3833
|
+
* [numero]="navigation.numeroDePage()"
|
|
3834
|
+
* [precedenteDisponible]="navigation.precedenteDisponible()"
|
|
3835
|
+
* [suivanteDisponible]="page.value()?.hasMore ?? false"
|
|
3836
|
+
* [chargement]="page.isLoading()"
|
|
3837
|
+
* [cible]="tableau"
|
|
3838
|
+
* (precedente)="navigation.precedente()"
|
|
3839
|
+
* (suivante)="navigation.suivante(page.value())"
|
|
3840
|
+
* />
|
|
3841
|
+
* ```
|
|
3842
|
+
*
|
|
3843
|
+
* ⚠️ **Ni total, ni numeros cliquables** : le backend ne compte pas et ne va que
|
|
3844
|
+
* vers l'avant (audit de la vague 4, V4-2). « Page 3 » se dit ; « page 3 sur 12 »
|
|
3845
|
+
* serait invente.
|
|
3846
|
+
*
|
|
3847
|
+
* ⚠️ **Boutons indisponibles, jamais desactives** : sur la derniere page, « Page
|
|
3848
|
+
* suivante » garde le focus qu'il avait. Desactive, il le perdrait, et le lecteur
|
|
3849
|
+
* d'ecran retomberait sur le debut du document.
|
|
3850
|
+
*
|
|
3851
|
+
* ⚠️ **Apres un changement de page, le focus va a la `cible`** — la legende du
|
|
3852
|
+
* tableau — et « Page 2 » s'annonce (UX-13). Sans cela, la personne au clavier
|
|
3853
|
+
* reste en bas, sur « Page suivante », sans savoir que le contenu au-dessus a
|
|
3854
|
+
* change.
|
|
3855
|
+
*/
|
|
3856
|
+
class StPagination {
|
|
3857
|
+
libelles = inject(ST_LIBELLES);
|
|
3858
|
+
injecteur = inject(Injector);
|
|
3859
|
+
mode = input('pages', /* @ts-ignore */
|
|
3860
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
3861
|
+
/** A partir de 1. */
|
|
3862
|
+
numero = input(1, /* @ts-ignore */
|
|
3863
|
+
...(ngDevMode ? [{ debugName: "numero" }] : /* istanbul ignore next */ []));
|
|
3864
|
+
precedenteDisponible = input(false, { ...(ngDevMode ? { debugName: "precedenteDisponible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3865
|
+
suivanteDisponible = input(false, { ...(ngDevMode ? { debugName: "suivanteDisponible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3866
|
+
chargement = input(false, { ...(ngDevMode ? { debugName: "chargement" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3867
|
+
/** Nom de la navigation, quand une page en porte plusieurs. */
|
|
3868
|
+
etiquette = input(null, /* @ts-ignore */
|
|
3869
|
+
...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
|
|
3870
|
+
/** Tailles proposees ; vide, pas de choix. Plafonnees a 100. */
|
|
3871
|
+
taillesDePage = input([], /* @ts-ignore */
|
|
3872
|
+
...(ngDevMode ? [{ debugName: "taillesDePage" }] : /* istanbul ignore next */ []));
|
|
3873
|
+
taille = model(20, /* @ts-ignore */
|
|
3874
|
+
...(ngDevMode ? [{ debugName: "taille" }] : /* istanbul ignore next */ []));
|
|
3875
|
+
/** Recoit le focus apres un changement de page. */
|
|
3876
|
+
cible = input(null, /* @ts-ignore */
|
|
3877
|
+
...(ngDevMode ? [{ debugName: "cible" }] : /* istanbul ignore next */ []));
|
|
3878
|
+
precedente = output();
|
|
3879
|
+
suivante = output();
|
|
3880
|
+
annonce = new AnnonceLocale(inject(DestroyRef));
|
|
3881
|
+
tailles = computed(() => this.taillesDePage().filter((taille) => taille >= 1 && taille <= TAILLE_MAXIMALE), /* @ts-ignore */
|
|
3882
|
+
...(ngDevMode ? [{ debugName: "tailles" }] : /* istanbul ignore next */ []));
|
|
3883
|
+
etiquetteLue = computed(() => this.etiquette() ?? this.libelles().pagination, /* @ts-ignore */
|
|
3884
|
+
...(ngDevMode ? [{ debugName: "etiquetteLue" }] : /* istanbul ignore next */ []));
|
|
3885
|
+
constructor() {
|
|
3886
|
+
// Le focus attend la FIN du chargement : amene sur la cible pendant que les
|
|
3887
|
+
// anciennes lignes sont encore la, il ferait relire la page quittee.
|
|
3888
|
+
let dernier = null;
|
|
3889
|
+
let enAttente = false;
|
|
3890
|
+
effect(() => {
|
|
3891
|
+
const numero = this.numero();
|
|
3892
|
+
const chargement = this.chargement();
|
|
3893
|
+
if (dernier === null) {
|
|
3894
|
+
dernier = numero;
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
if (numero !== dernier) {
|
|
3898
|
+
dernier = numero;
|
|
3899
|
+
enAttente = true;
|
|
3900
|
+
if (this.mode() === 'pages')
|
|
3901
|
+
this.annonce.annoncer(this.libelles().pageNumero(numero));
|
|
3902
|
+
}
|
|
3903
|
+
if (!enAttente || chargement)
|
|
3904
|
+
return;
|
|
3905
|
+
enAttente = false;
|
|
3906
|
+
afterNextRender(() => {
|
|
3907
|
+
const cible = this.cible();
|
|
3908
|
+
const element = typeof cible === 'function' ? cible() : cible;
|
|
3909
|
+
element?.focus();
|
|
3910
|
+
}, { injector: this.injecteur });
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3913
|
+
surTaille(evenement) {
|
|
3914
|
+
this.taille.set(Number(evenement.target.value));
|
|
3915
|
+
}
|
|
3916
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StPagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3917
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StPagination, isStandalone: true, selector: "st-pagination", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, numero: { classPropertyName: "numero", publicName: "numero", isSignal: true, isRequired: false, transformFunction: null }, precedenteDisponible: { classPropertyName: "precedenteDisponible", publicName: "precedenteDisponible", isSignal: true, isRequired: false, transformFunction: null }, suivanteDisponible: { classPropertyName: "suivanteDisponible", publicName: "suivanteDisponible", isSignal: true, isRequired: false, transformFunction: null }, chargement: { classPropertyName: "chargement", publicName: "chargement", isSignal: true, isRequired: false, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null }, taillesDePage: { classPropertyName: "taillesDePage", publicName: "taillesDePage", isSignal: true, isRequired: false, transformFunction: null }, taille: { classPropertyName: "taille", publicName: "taille", isSignal: true, isRequired: false, transformFunction: null }, cible: { classPropertyName: "cible", publicName: "cible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { taille: "tailleChange", precedente: "precedente", suivante: "suivante" }, host: { classAttribute: "st-pagination" }, ngImport: i0, template: "<nav class=\"st-pagination__navigation\" [attr.aria-label]=\"etiquetteLue()\">\n @if (mode() === 'pages') {\n <button\n stButton\n type=\"button\"\n iconeAvant=\"chevron-gauche\"\n [indisponible]=\"!precedenteDisponible() || chargement()\"\n (click)=\"precedente.emit()\"\n >\n {{ libelles().pagePrecedente }}\n </button>\n <span class=\"st-pagination__numero\" aria-current=\"page\">\n {{ libelles().pageNumero(numero()) }}\n </span>\n <button\n stButton\n type=\"button\"\n iconeApres=\"chevron-droite\"\n [indisponible]=\"!suivanteDisponible()\"\n [chargement]=\"chargement()\"\n (click)=\"suivante.emit()\"\n >\n {{ libelles().pageSuivante }}\n </button>\n } @else if (suivanteDisponible()) {\n <button stButton type=\"button\" [chargement]=\"chargement()\" (click)=\"suivante.emit()\">\n {{ libelles().afficherPlus }}\n </button>\n }\n\n @if (tailles().length > 0) {\n <label class=\"st-pagination__taille\">\n <span>{{ libelles().lignesParPage }}</span>\n <select class=\"st-pagination__choix\" (change)=\"surTaille($event)\">\n @for (proposee of tailles(); track proposee) {\n <option [value]=\"proposee\" [selected]=\"proposee === taille()\">{{ proposee }}</option>\n }\n </select>\n </label>\n }\n</nav>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-pagination__navigation{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2) var(--st-espacement-3);align-items:center}.st-pagination__numero{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-semi)}.st-pagination__taille{display:inline-flex;gap:var(--st-espacement-2);align-items:center;margin-inline-start:auto}.st-pagination__choix:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-pagination__choix{box-sizing:border-box;inline-size:100%;min-block-size:var(--st-cible-tactile-min);padding:var(--st-espacement-2) var(--st-espacement-3);font:inherit;color:var(--st-couleur-texte);background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-sm)}.st-pagination__choix[aria-invalid=true]{border-color:var(--st-couleur-danger);box-shadow:inset 0 0 0 1px var(--st-couleur-danger)}.st-pagination__choix:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;background:var(--st-couleur-surface-enfoncee)}.st-pagination__choix:read-only:not(:disabled){background:var(--st-couleur-surface-enfoncee)}.st-pagination__choix{inline-size:auto}\n"], dependencies: [{ kind: "component", type: StButton, selector: "button[stButton]", inputs: ["variante", "taille", "pleineLargeur", "iconeAvant", "iconeApres", "chargement", "indisponible"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3918
|
+
}
|
|
3919
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StPagination, decorators: [{
|
|
3920
|
+
type: Component,
|
|
3921
|
+
args: [{ selector: 'st-pagination', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StButton], host: { class: 'st-pagination' }, template: "<nav class=\"st-pagination__navigation\" [attr.aria-label]=\"etiquetteLue()\">\n @if (mode() === 'pages') {\n <button\n stButton\n type=\"button\"\n iconeAvant=\"chevron-gauche\"\n [indisponible]=\"!precedenteDisponible() || chargement()\"\n (click)=\"precedente.emit()\"\n >\n {{ libelles().pagePrecedente }}\n </button>\n <span class=\"st-pagination__numero\" aria-current=\"page\">\n {{ libelles().pageNumero(numero()) }}\n </span>\n <button\n stButton\n type=\"button\"\n iconeApres=\"chevron-droite\"\n [indisponible]=\"!suivanteDisponible()\"\n [chargement]=\"chargement()\"\n (click)=\"suivante.emit()\"\n >\n {{ libelles().pageSuivante }}\n </button>\n } @else if (suivanteDisponible()) {\n <button stButton type=\"button\" [chargement]=\"chargement()\" (click)=\"suivante.emit()\">\n {{ libelles().afficherPlus }}\n </button>\n }\n\n @if (tailles().length > 0) {\n <label class=\"st-pagination__taille\">\n <span>{{ libelles().lignesParPage }}</span>\n <select class=\"st-pagination__choix\" (change)=\"surTaille($event)\">\n @for (proposee of tailles(); track proposee) {\n <option [value]=\"proposee\" [selected]=\"proposee === taille()\">{{ proposee }}</option>\n }\n </select>\n </label>\n }\n</nav>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-pagination__navigation{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2) var(--st-espacement-3);align-items:center}.st-pagination__numero{font-variant-numeric:tabular-nums;font-weight:var(--st-graisse-semi)}.st-pagination__taille{display:inline-flex;gap:var(--st-espacement-2);align-items:center;margin-inline-start:auto}.st-pagination__choix:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-pagination__choix{box-sizing:border-box;inline-size:100%;min-block-size:var(--st-cible-tactile-min);padding:var(--st-espacement-2) var(--st-espacement-3);font:inherit;color:var(--st-couleur-texte);background:var(--st-couleur-surface);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-sm)}.st-pagination__choix[aria-invalid=true]{border-color:var(--st-couleur-danger);box-shadow:inset 0 0 0 1px var(--st-couleur-danger)}.st-pagination__choix:disabled{color:var(--st-couleur-texte-desactive);cursor:not-allowed;background:var(--st-couleur-surface-enfoncee)}.st-pagination__choix:read-only:not(:disabled){background:var(--st-couleur-surface-enfoncee)}.st-pagination__choix{inline-size:auto}\n"] }]
|
|
3922
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], numero: [{ type: i0.Input, args: [{ isSignal: true, alias: "numero", required: false }] }], precedenteDisponible: [{ type: i0.Input, args: [{ isSignal: true, alias: "precedenteDisponible", required: false }] }], suivanteDisponible: [{ type: i0.Input, args: [{ isSignal: true, alias: "suivanteDisponible", required: false }] }], chargement: [{ type: i0.Input, args: [{ isSignal: true, alias: "chargement", required: false }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }], taillesDePage: [{ type: i0.Input, args: [{ isSignal: true, alias: "taillesDePage", required: false }] }], taille: [{ type: i0.Input, args: [{ isSignal: true, alias: "taille", required: false }] }, { type: i0.Output, args: ["tailleChange"] }], cible: [{ type: i0.Input, args: [{ isSignal: true, alias: "cible", required: false }] }], precedente: [{ type: i0.Output, args: ["precedente"] }], suivante: [{ type: i0.Output, args: ["suivante"] }] } });
|
|
3923
|
+
|
|
3924
|
+
/**
|
|
3925
|
+
* Valeur riche d'un element : montant, date, valeur masquee.
|
|
3926
|
+
*
|
|
3927
|
+
* ```html
|
|
3928
|
+
* <ng-template stDescription="iban" let-element>
|
|
3929
|
+
* <st-masked nature="iban" [valeur]="compte().iban" />
|
|
3930
|
+
* </ng-template>
|
|
3931
|
+
* ```
|
|
3932
|
+
*/
|
|
3933
|
+
class StDescription {
|
|
3934
|
+
cle = input.required({ ...(ngDevMode ? { debugName: "cle" } : /* istanbul ignore next */ {}), alias: 'stDescription' });
|
|
3935
|
+
gabarit = inject(TemplateRef);
|
|
3936
|
+
static ngTemplateContextGuard(_directive, _contexte) {
|
|
3937
|
+
return true;
|
|
3938
|
+
}
|
|
3939
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3940
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: StDescription, isStandalone: true, selector: "ng-template[stDescription]", inputs: { cle: { classPropertyName: "cle", publicName: "stDescription", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
3941
|
+
}
|
|
3942
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDescription, decorators: [{
|
|
3943
|
+
type: Directive,
|
|
3944
|
+
args: [{ selector: 'ng-template[stDescription]' }]
|
|
3945
|
+
}], propDecorators: { cle: [{ type: i0.Input, args: [{ isSignal: true, alias: "stDescription", required: true }] }] } });
|
|
3946
|
+
/**
|
|
3947
|
+
* Liste de descriptions : les proprietes d'un objet, sur une page de detail.
|
|
3948
|
+
*
|
|
3949
|
+
* ```html
|
|
3950
|
+
* <st-description-list [elements]="[
|
|
3951
|
+
* { terme: 'Reference', description: commande().reference },
|
|
3952
|
+
* { cle: 'montant', terme: 'Montant', description: null },
|
|
3953
|
+
* ]">
|
|
3954
|
+
* <ng-template stDescription="montant"><st-money [valeur]="commande().montant" /></ng-template>
|
|
3955
|
+
* </st-description-list>
|
|
3956
|
+
* ```
|
|
3957
|
+
*
|
|
3958
|
+
* ⚠️ **Un vrai `dl`, rendu par le composant.** Le lecteur d'ecran annonce « liste de
|
|
3959
|
+
* descriptions, 4 elements » et lit chaque terme avec sa valeur. Ecrits par
|
|
3960
|
+
* l'application et projetes, `dt` et `dd` echapperaient aux styles du composant,
|
|
3961
|
+
* et un `div` mal place rendrait la liste invalide.
|
|
3962
|
+
*
|
|
3963
|
+
* ⚠️ **Une valeur absente se dit** : « — » a l'ecran, « Non renseigne » au lecteur
|
|
3964
|
+
* d'ecran. Une cellule vide ne distingue pas « aucun » de « pas encore charge ».
|
|
3965
|
+
*
|
|
3966
|
+
* En colonnes quand la place le permet, empile en dessous : la largeur est celle
|
|
3967
|
+
* du CONTENEUR, pas de l'ecran — la meme liste dans une carte etroite s'empile
|
|
3968
|
+
* aussi sur un grand ecran (UX-18).
|
|
3969
|
+
*/
|
|
3970
|
+
class StDescriptionList {
|
|
3971
|
+
libelles = inject(ST_LIBELLES);
|
|
3972
|
+
elements = input.required(/* @ts-ignore */
|
|
3973
|
+
...(ngDevMode ? [{ debugName: "elements" }] : /* istanbul ignore next */ []));
|
|
3974
|
+
gabarits = contentChildren(StDescription, /* @ts-ignore */
|
|
3975
|
+
...(ngDevMode ? [{ debugName: "gabarits" }] : /* istanbul ignore next */ []));
|
|
3976
|
+
lignes = computed(() => {
|
|
3977
|
+
const gabarits = new Map(this.gabarits().map((gabarit) => [gabarit.cle(), gabarit.gabarit]));
|
|
3978
|
+
return this.elements().map((element) => ({
|
|
3979
|
+
element,
|
|
3980
|
+
gabarit: gabarits.get(element.cle ?? element.terme) ?? null,
|
|
3981
|
+
}));
|
|
3982
|
+
}, /* @ts-ignore */
|
|
3983
|
+
...(ngDevMode ? [{ debugName: "lignes" }] : /* istanbul ignore next */ []));
|
|
3984
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDescriptionList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3985
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StDescriptionList, isStandalone: true, selector: "st-description-list", inputs: { elements: { classPropertyName: "elements", publicName: "elements", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "st-description-list" }, queries: [{ propertyName: "gabarits", predicate: StDescription, isSignal: true }], ngImport: i0, template: "<dl class=\"st-description-list__liste\">\n @for (ligne of lignes(); track ligne.element.cle ?? ligne.element.terme) {\n <div class=\"st-description-list__groupe\">\n <dt class=\"st-description-list__terme\">{{ ligne.element.terme }}</dt>\n <dd class=\"st-description-list__valeur\">\n @if (ligne.gabarit; as gabarit) {\n <ng-container *ngTemplateOutlet=\"gabarit; context: { $implicit: ligne.element }\" />\n } @else if (ligne.element.description === null) {\n <span aria-hidden=\"true\">—</span>\n <span class=\"st-vh\">{{ libelles().nonRenseigne }}</span>\n } @else {\n {{ ligne.element.description }}\n }\n </dd>\n </div>\n }\n</dl>\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([hidden]){display:none!important}:host{display:block;container-type:inline-size}.st-description-list__liste{margin:0}.st-description-list__groupe{display:grid;gap:var(--st-espacement-1);padding-block:var(--st-espacement-2);border-block-end:1px solid var(--st-couleur-bordure)}.st-description-list__terme{font-weight:var(--st-graisse-semi);color:var(--st-couleur-texte-discret)}.st-description-list__valeur{margin:0;overflow-wrap:anywhere}@container (min-width: 32rem){.st-description-list__groupe{grid-template-columns:minmax(10rem,1fr) 2fr;gap:var(--st-espacement-4)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3986
|
+
}
|
|
3987
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDescriptionList, decorators: [{
|
|
3988
|
+
type: Component,
|
|
3989
|
+
args: [{ selector: 'st-description-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: { class: 'st-description-list' }, template: "<dl class=\"st-description-list__liste\">\n @for (ligne of lignes(); track ligne.element.cle ?? ligne.element.terme) {\n <div class=\"st-description-list__groupe\">\n <dt class=\"st-description-list__terme\">{{ ligne.element.terme }}</dt>\n <dd class=\"st-description-list__valeur\">\n @if (ligne.gabarit; as gabarit) {\n <ng-container *ngTemplateOutlet=\"gabarit; context: { $implicit: ligne.element }\" />\n } @else if (ligne.element.description === null) {\n <span aria-hidden=\"true\">—</span>\n <span class=\"st-vh\">{{ libelles().nonRenseigne }}</span>\n } @else {\n {{ ligne.element.description }}\n }\n </dd>\n </div>\n }\n</dl>\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([hidden]){display:none!important}:host{display:block;container-type:inline-size}.st-description-list__liste{margin:0}.st-description-list__groupe{display:grid;gap:var(--st-espacement-1);padding-block:var(--st-espacement-2);border-block-end:1px solid var(--st-couleur-bordure)}.st-description-list__terme{font-weight:var(--st-graisse-semi);color:var(--st-couleur-texte-discret)}.st-description-list__valeur{margin:0;overflow-wrap:anywhere}@container (min-width: 32rem){.st-description-list__groupe{grid-template-columns:minmax(10rem,1fr) 2fr;gap:var(--st-espacement-4)}}\n"] }]
|
|
3990
|
+
}], propDecorators: { elements: [{ type: i0.Input, args: [{ isSignal: true, alias: "elements", required: true }] }], gabarits: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StDescription), { isSignal: true }] }] } });
|
|
3991
|
+
|
|
3992
|
+
/**
|
|
3993
|
+
* Rendu d'un element, type par `[stElementDeListeDe]` :
|
|
3994
|
+
*
|
|
3995
|
+
* ```html
|
|
3996
|
+
* <ng-template stElementDeListe [stElementDeListeDe]="expeditions()" let-expedition>
|
|
3997
|
+
* <a [routerLink]="['/expeditions', expedition.id]">{{ expedition.reference }}</a>
|
|
3998
|
+
* </ng-template>
|
|
3999
|
+
* ```
|
|
4000
|
+
*/
|
|
4001
|
+
class StElementDeListe {
|
|
4002
|
+
/** Sert seulement au typage du contexte ; nom de l'attribut, d'ou l'absence d'alias. */
|
|
4003
|
+
stElementDeListeDe = input(undefined, /* @ts-ignore */
|
|
4004
|
+
...(ngDevMode ? [{ debugName: "stElementDeListeDe" }] : /* istanbul ignore next */ []));
|
|
4005
|
+
gabarit = inject(TemplateRef);
|
|
4006
|
+
static ngTemplateContextGuard(_directive, _contexte) {
|
|
4007
|
+
return true;
|
|
4008
|
+
}
|
|
4009
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StElementDeListe, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4010
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: StElementDeListe, isStandalone: true, selector: "ng-template[stElementDeListe]", inputs: { stElementDeListeDe: { classPropertyName: "stElementDeListeDe", publicName: "stElementDeListeDe", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
4011
|
+
}
|
|
4012
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StElementDeListe, decorators: [{
|
|
4013
|
+
type: Directive,
|
|
4014
|
+
args: [{ selector: 'ng-template[stElementDeListe]' }]
|
|
4015
|
+
}], propDecorators: { stElementDeListeDe: [{ type: i0.Input, args: [{ isSignal: true, alias: "stElementDeListeDe", required: false }] }] } });
|
|
4016
|
+
/**
|
|
4017
|
+
* Liste d'objets : expeditions, notifications, documents. La forme mobile d'un
|
|
4018
|
+
* tableau (UX-5).
|
|
4019
|
+
*
|
|
4020
|
+
* ```html
|
|
4021
|
+
* <st-list #liste etiquette="Mes expeditions" [elements]="expeditions()" cle="id">
|
|
4022
|
+
* <ng-template stElementDeListe [stElementDeListeDe]="expeditions()" let-expedition>…</ng-template>
|
|
4023
|
+
* </st-list>
|
|
4024
|
+
* <st-pagination mode="suite" [cible]="liste" … />
|
|
4025
|
+
* ```
|
|
4026
|
+
*
|
|
4027
|
+
* ⚠️ **Un lien DANS l'element, jamais un element cliquable** (UX-3) : le lien a un
|
|
4028
|
+
* nom, s'ouvre dans un onglet, et laisse les autres boutons de l'element
|
|
4029
|
+
* atteignables.
|
|
4030
|
+
*
|
|
4031
|
+
* ⚠️ **`focus()` va au premier element AJOUTE** depuis le dernier affichage : apres
|
|
4032
|
+
* « Afficher plus », la lecture reprend la ou la liste s'est allongee, et non en
|
|
4033
|
+
* tete. Apres un filtre, la liste raccourcit, et le focus va au premier element.
|
|
4034
|
+
*
|
|
4035
|
+
* `track` par la cle : un rafraichissement garde le focus sur le bon element.
|
|
4036
|
+
*/
|
|
4037
|
+
class StList {
|
|
4038
|
+
element = inject(ElementRef);
|
|
4039
|
+
elements = input.required(/* @ts-ignore */
|
|
4040
|
+
...(ngDevMode ? [{ debugName: "elements" }] : /* istanbul ignore next */ []));
|
|
4041
|
+
/** Propriete qui identifie un element. */
|
|
4042
|
+
cle = input.required(/* @ts-ignore */
|
|
4043
|
+
...(ngDevMode ? [{ debugName: "cle" }] : /* istanbul ignore next */ []));
|
|
4044
|
+
/** Nom de la liste, lu a l'entree. */
|
|
4045
|
+
etiquette = input(null, /* @ts-ignore */
|
|
4046
|
+
...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
|
|
4047
|
+
/** `ol` quand l'ordre a un sens : etapes, classement. */
|
|
4048
|
+
ordonnee = input(false, { ...(ngDevMode ? { debugName: "ordonnee" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4049
|
+
/** Contenu en cours de remplacement : `aria-busy`. */
|
|
4050
|
+
chargement = input(false, { ...(ngDevMode ? { debugName: "chargement" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4051
|
+
gabarit = contentChild(StElementDeListe, /* @ts-ignore */
|
|
4052
|
+
...(ngDevMode ? [{ debugName: "gabarit" }] : /* istanbul ignore next */ []));
|
|
4053
|
+
indiceDuPremierAjout = 0;
|
|
4054
|
+
lignes = computed(() => {
|
|
4055
|
+
const cle = this.cle();
|
|
4056
|
+
return this.elements().map((valeur, indice) => ({
|
|
4057
|
+
valeur,
|
|
4058
|
+
indice,
|
|
4059
|
+
identifiant: String(valeur[cle]),
|
|
4060
|
+
}));
|
|
4061
|
+
}, /* @ts-ignore */
|
|
4062
|
+
...(ngDevMode ? [{ debugName: "lignes" }] : /* istanbul ignore next */ []));
|
|
4063
|
+
constructor() {
|
|
4064
|
+
let longueur = 0;
|
|
4065
|
+
effect(() => {
|
|
4066
|
+
const nouvelle = this.elements().length;
|
|
4067
|
+
this.indiceDuPremierAjout = nouvelle > longueur && longueur > 0 ? longueur : 0;
|
|
4068
|
+
longueur = nouvelle;
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
/** Focus sur le premier element ajoute, ou sur le premier. */
|
|
4072
|
+
focus(options) {
|
|
4073
|
+
const elements = this.element.nativeElement.querySelectorAll('.st-list__element');
|
|
4074
|
+
(elements[this.indiceDuPremierAjout] ?? elements[0])?.focus(options);
|
|
4075
|
+
}
|
|
4076
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4077
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StList, isStandalone: true, selector: "st-list", inputs: { elements: { classPropertyName: "elements", publicName: "elements", isSignal: true, isRequired: true, transformFunction: null }, cle: { classPropertyName: "cle", publicName: "cle", isSignal: true, isRequired: true, transformFunction: null }, etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null }, ordonnee: { classPropertyName: "ordonnee", publicName: "ordonnee", isSignal: true, isRequired: false, transformFunction: null }, chargement: { classPropertyName: "chargement", publicName: "chargement", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "st-list" }, queries: [{ propertyName: "gabarit", first: true, predicate: StElementDeListe, descendants: true, isSignal: true }], ngImport: i0, template: "@if (ordonnee()) {\n <ol\n class=\"st-list__liste\"\n [attr.aria-label]=\"etiquette()\"\n [attr.aria-busy]=\"chargement() ? 'true' : null\"\n >\n <ng-container *ngTemplateOutlet=\"contenu\" />\n </ol>\n} @else {\n <ul\n class=\"st-list__liste\"\n [attr.aria-label]=\"etiquette()\"\n [attr.aria-busy]=\"chargement() ? 'true' : null\"\n >\n <ng-container *ngTemplateOutlet=\"contenu\" />\n </ul>\n}\n\n<ng-template #contenu>\n @for (ligne of lignes(); track ligne.identifiant) {\n <!-- tabindex -1 : cible du focus apres \u00AB Afficher plus \u00BB, hors tabulation. -->\n <li class=\"st-list__element\" tabindex=\"-1\">\n @if (gabarit(); as gabarit) {\n <ng-container\n *ngTemplateOutlet=\"\n gabarit.gabarit;\n context: { $implicit: ligne.valeur, indice: ligne.indice }\n \"\n />\n } @else {\n {{ ligne.valeur }}\n }\n </li>\n }\n</ng-template>\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([hidden]){display:none!important}:host{display:block}.st-list__liste{padding:0;margin:0;list-style:none}.st-list__liste[aria-busy=true]{cursor:progress}.st-list__element{padding-block:var(--st-espacement-3);border-block-end:1px solid var(--st-couleur-bordure)}.st-list__element:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4078
|
+
}
|
|
4079
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StList, decorators: [{
|
|
4080
|
+
type: Component,
|
|
4081
|
+
args: [{ selector: 'st-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: { class: 'st-list' }, template: "@if (ordonnee()) {\n <ol\n class=\"st-list__liste\"\n [attr.aria-label]=\"etiquette()\"\n [attr.aria-busy]=\"chargement() ? 'true' : null\"\n >\n <ng-container *ngTemplateOutlet=\"contenu\" />\n </ol>\n} @else {\n <ul\n class=\"st-list__liste\"\n [attr.aria-label]=\"etiquette()\"\n [attr.aria-busy]=\"chargement() ? 'true' : null\"\n >\n <ng-container *ngTemplateOutlet=\"contenu\" />\n </ul>\n}\n\n<ng-template #contenu>\n @for (ligne of lignes(); track ligne.identifiant) {\n <!-- tabindex -1 : cible du focus apres \u00AB Afficher plus \u00BB, hors tabulation. -->\n <li class=\"st-list__element\" tabindex=\"-1\">\n @if (gabarit(); as gabarit) {\n <ng-container\n *ngTemplateOutlet=\"\n gabarit.gabarit;\n context: { $implicit: ligne.valeur, indice: ligne.indice }\n \"\n />\n } @else {\n {{ ligne.valeur }}\n }\n </li>\n }\n</ng-template>\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([hidden]){display:none!important}:host{display:block}.st-list__liste{padding:0;margin:0;list-style:none}.st-list__liste[aria-busy=true]{cursor:progress}.st-list__element{padding-block:var(--st-espacement-3);border-block-end:1px solid var(--st-couleur-bordure)}.st-list__element:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}\n"] }]
|
|
4082
|
+
}], ctorParameters: () => [], propDecorators: { elements: [{ type: i0.Input, args: [{ isSignal: true, alias: "elements", required: true }] }], cle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cle", required: true }] }], etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }], ordonnee: [{ type: i0.Input, args: [{ isSignal: true, alias: "ordonnee", required: false }] }], chargement: [{ type: i0.Input, args: [{ isSignal: true, alias: "chargement", required: false }] }], gabarit: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StElementDeListe), { isSignal: true }] }] } });
|
|
4083
|
+
|
|
4084
|
+
/**
|
|
4085
|
+
* Bouton de tri d'une colonne, pose DANS l'en-tete : `<th><button stSortHeader>`.
|
|
4086
|
+
*
|
|
4087
|
+
* ⚠️ **Le bouton, pas l'en-tete cliquable** : un `th` n'est ni focalisable ni
|
|
4088
|
+
* annonce comme actionnable. Et l'etat se porte par `aria-sort` sur le `th`, que
|
|
4089
|
+
* `st-data-table` pose sur la SEULE colonne triee (FE-3).
|
|
4090
|
+
*
|
|
4091
|
+
* ⚠️ **Ne trie rien.** Il dit l'intention ; c'est le serveur qui trie, et le
|
|
4092
|
+
* contrat actuel ne le fait pas (audit de la vague 4, V4-3). Trier la page
|
|
4093
|
+
* affichee ferait croire a un tri de toute la liste.
|
|
4094
|
+
*
|
|
4095
|
+
* Une colonne non triee montre deux chevrons, pour se signaler triable sans
|
|
4096
|
+
* attendre le survol ; l'icone est decorative, l'etat est lu par `aria-sort`.
|
|
4097
|
+
*/
|
|
4098
|
+
class StSortHeader {
|
|
4099
|
+
sens = input(null, /* @ts-ignore */
|
|
4100
|
+
...(ngDevMode ? [{ debugName: "sens" }] : /* istanbul ignore next */ []));
|
|
4101
|
+
icone = computed(() => {
|
|
4102
|
+
const sens = this.sens();
|
|
4103
|
+
if (sens === 'croissant')
|
|
4104
|
+
return 'chevron-haut';
|
|
4105
|
+
if (sens === 'decroissant')
|
|
4106
|
+
return 'chevron-bas';
|
|
4107
|
+
return 'tri';
|
|
4108
|
+
}, /* @ts-ignore */
|
|
4109
|
+
...(ngDevMode ? [{ debugName: "icone" }] : /* istanbul ignore next */ []));
|
|
4110
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSortHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4111
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StSortHeader, isStandalone: true, selector: "button[stSortHeader]", inputs: { sens: { classPropertyName: "sens", publicName: "sens", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, properties: { "attr.data-sens": "sens()" }, classAttribute: "st-sort-header" }, ngImport: i0, template: "<span class=\"st-sort-header__libelle\"><ng-content /></span>\n<st-icon class=\"st-sort-header__icone\" taille=\"sm\" [nom]=\"icone()\" />\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([hidden]){display:none!important}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}:host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:24px;padding:0;font:inherit;font-weight:inherit;color:inherit;text-align:inherit;cursor:pointer;background:none;border:0}.st-sort-header__icone{flex:none;color:var(--st-couleur-texte-discret)}:host([data-sens]) .st-sort-header__icone{color:inherit}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4112
|
+
}
|
|
4113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StSortHeader, decorators: [{
|
|
4114
|
+
type: Component,
|
|
4115
|
+
args: [{ selector: 'button[stSortHeader]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon], host: {
|
|
4116
|
+
class: 'st-sort-header',
|
|
4117
|
+
type: 'button',
|
|
4118
|
+
'[attr.data-sens]': 'sens()',
|
|
4119
|
+
}, template: "<span class=\"st-sort-header__libelle\"><ng-content /></span>\n<st-icon class=\"st-sort-header__icone\" taille=\"sm\" [nom]=\"icone()\" />\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([hidden]){display:none!important}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}:host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:24px;padding:0;font:inherit;font-weight:inherit;color:inherit;text-align:inherit;cursor:pointer;background:none;border:0}.st-sort-header__icone{flex:none;color:var(--st-couleur-texte-discret)}:host([data-sens]) .st-sort-header__icone{color:inherit}\n"] }]
|
|
4120
|
+
}], propDecorators: { sens: [{ type: i0.Input, args: [{ isSignal: true, alias: "sens", required: false }] }] } });
|
|
4121
|
+
|
|
4122
|
+
let compteur$3 = 0;
|
|
4123
|
+
/**
|
|
4124
|
+
* Rendu d'une cellule, type par `[stCelluleDe]` :
|
|
4125
|
+
*
|
|
4126
|
+
* ```html
|
|
4127
|
+
* <ng-template stCellule="montant" [stCelluleDe]="lignes()" let-ligne>
|
|
4128
|
+
* <st-money [valeur]="ligne.montant" />
|
|
4129
|
+
* </ng-template>
|
|
4130
|
+
* ```
|
|
4131
|
+
*/
|
|
4132
|
+
class StCellule {
|
|
4133
|
+
cle = input.required({ ...(ngDevMode ? { debugName: "cle" } : /* istanbul ignore next */ {}), alias: 'stCellule' });
|
|
4134
|
+
/** Sert seulement au typage du contexte ; nom de l'attribut, d'ou l'absence d'alias. */
|
|
4135
|
+
stCelluleDe = input(undefined, /* @ts-ignore */
|
|
4136
|
+
...(ngDevMode ? [{ debugName: "stCelluleDe" }] : /* istanbul ignore next */ []));
|
|
4137
|
+
gabarit = inject(TemplateRef);
|
|
4138
|
+
static ngTemplateContextGuard(_directive, _contexte) {
|
|
4139
|
+
return true;
|
|
4140
|
+
}
|
|
4141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCellule, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4142
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: StCellule, isStandalone: true, selector: "ng-template[stCellule]", inputs: { cle: { classPropertyName: "cle", publicName: "stCellule", isSignal: true, isRequired: true, transformFunction: null }, stCelluleDe: { classPropertyName: "stCelluleDe", publicName: "stCelluleDe", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
4143
|
+
}
|
|
4144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StCellule, decorators: [{
|
|
4145
|
+
type: Directive,
|
|
4146
|
+
args: [{ selector: 'ng-template[stCellule]' }]
|
|
4147
|
+
}], propDecorators: { cle: [{ type: i0.Input, args: [{ isSignal: true, alias: "stCellule", required: true }] }], stCelluleDe: [{ type: i0.Input, args: [{ isSignal: true, alias: "stCelluleDe", required: false }] }] } });
|
|
4148
|
+
/** Contenu du tableau vide : un `st-empty`, avec son action. */
|
|
4149
|
+
class StTableauVide {
|
|
4150
|
+
gabarit = inject(TemplateRef);
|
|
4151
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StTableauVide, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
4152
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: StTableauVide, isStandalone: true, selector: "ng-template[stTableauVide]", ngImport: i0 });
|
|
4153
|
+
}
|
|
4154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StTableauVide, decorators: [{
|
|
4155
|
+
type: Directive,
|
|
4156
|
+
args: [{ selector: 'ng-template[stTableauVide]' }]
|
|
4157
|
+
}] });
|
|
4158
|
+
/** Lignes de squelette au premier chargement. */
|
|
4159
|
+
const LIGNES_DE_SQUELETTE = 5;
|
|
4160
|
+
/**
|
|
4161
|
+
* Tableau de donnees, sur un `<table>` NATIF.
|
|
4162
|
+
*
|
|
4163
|
+
* ```html
|
|
4164
|
+
* <st-data-table #tableau legende="Expeditions" [colonnes]="colonnes" [lignes]="lignes()"
|
|
4165
|
+
* cle="id" selectionnable [(selection)]="choisies" [chargement]="page.isLoading()">
|
|
4166
|
+
* <ng-template stCellule="reference" [stCelluleDe]="lignes()" let-ligne>
|
|
4167
|
+
* <a [routerLink]="['/expeditions', ligne.id]">{{ ligne.reference }}</a>
|
|
4168
|
+
* </ng-template>
|
|
4169
|
+
* <button stButton stActionsDeTableau (click)="exporter()">Exporter</button>
|
|
4170
|
+
* </st-data-table>
|
|
4171
|
+
* ```
|
|
4172
|
+
*
|
|
4173
|
+
* ⚠️ **Un tableau, pas une grille** (audit de la vague 4, V4-1). Le lecteur d'ecran
|
|
4174
|
+
* sait parcourir un `<table>` de cellule en cellule ; une grille ARIA remplacerait
|
|
4175
|
+
* ce parcours par les fleches, et rendrait les liens des cellules plus durs a
|
|
4176
|
+
* atteindre. Ni Material, ni `CdkTable` : pas de pair dans le tronc.
|
|
4177
|
+
*
|
|
4178
|
+
* - **Legende obligatoire** (UX-1), et une colonne d'en-tete de ligne — la premiere
|
|
4179
|
+
* par defaut — pour que chaque cellule se lise avec sa ligne (UX-2).
|
|
4180
|
+
* - **Zone de defilement nommee** : focalisable quand le tableau deborde (UX-6),
|
|
4181
|
+
* colonne figee sans masquer le focus (UX-7), en-tete fige seulement avec
|
|
4182
|
+
* `hauteurMax` (UX-8).
|
|
4183
|
+
* - **Selection de la PAGE, par cle** (V4-4) : elle ne garde que les lignes encore
|
|
4184
|
+
* affichees, et se vide donc au changement de page.
|
|
4185
|
+
* - **Tri PILOTE** : le tableau dit la demande et l'annonce, il ne reordonne
|
|
4186
|
+
* jamais les lignes (V4-3).
|
|
4187
|
+
* - **Chargement** : squelette au premier, lignes gardees et `aria-busy` ensuite
|
|
4188
|
+
* (UX-11).
|
|
4189
|
+
* - Export, actions de masse : zone `stActionsDeTableau`, et le SERVEUR fait le
|
|
4190
|
+
* travail (V4-5, SEC-4, SEC-5).
|
|
4191
|
+
*/
|
|
4192
|
+
class StDataTable {
|
|
4193
|
+
libelles = inject(ST_LIBELLES);
|
|
4194
|
+
legende = input.required(/* @ts-ignore */
|
|
4195
|
+
...(ngDevMode ? [{ debugName: "legende" }] : /* istanbul ignore next */ []));
|
|
4196
|
+
/** Lue mais non affichee, quand un titre visible nomme deja le tableau. */
|
|
4197
|
+
legendeMasquee = input(false, { ...(ngDevMode ? { debugName: "legendeMasquee" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4198
|
+
colonnes = input.required(/* @ts-ignore */
|
|
4199
|
+
...(ngDevMode ? [{ debugName: "colonnes" }] : /* istanbul ignore next */ []));
|
|
4200
|
+
lignes = input.required(/* @ts-ignore */
|
|
4201
|
+
...(ngDevMode ? [{ debugName: "lignes" }] : /* istanbul ignore next */ []));
|
|
4202
|
+
/** Propriete qui identifie une ligne. Jamais l'indice (FE-1). */
|
|
4203
|
+
cle = input.required(/* @ts-ignore */
|
|
4204
|
+
...(ngDevMode ? [{ debugName: "cle" }] : /* istanbul ignore next */ []));
|
|
4205
|
+
/** Colonne lue comme en-tete de chaque ligne. Par defaut : la premiere. */
|
|
4206
|
+
enTeteDeLigne = input(null, /* @ts-ignore */
|
|
4207
|
+
...(ngDevMode ? [{ debugName: "enTeteDeLigne" }] : /* istanbul ignore next */ []));
|
|
4208
|
+
premiereColonneFigee = input(false, { ...(ngDevMode ? { debugName: "premiereColonneFigee" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4209
|
+
/** Hauteur maximale, `32rem` : l'en-tete se fige, le corps defile. */
|
|
4210
|
+
hauteurMax = input(null, /* @ts-ignore */
|
|
4211
|
+
...(ngDevMode ? [{ debugName: "hauteurMax" }] : /* istanbul ignore next */ []));
|
|
4212
|
+
densite = input('confortable', /* @ts-ignore */
|
|
4213
|
+
...(ngDevMode ? [{ debugName: "densite" }] : /* istanbul ignore next */ []));
|
|
4214
|
+
chargement = input(false, { ...(ngDevMode ? { debugName: "chargement" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4215
|
+
selectionnable = input(false, { ...(ngDevMode ? { debugName: "selectionnable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4216
|
+
selection = model([], /* @ts-ignore */
|
|
4217
|
+
...(ngDevMode ? [{ debugName: "selection" }] : /* istanbul ignore next */ []));
|
|
4218
|
+
tri = model(null, /* @ts-ignore */
|
|
4219
|
+
...(ngDevMode ? [{ debugName: "tri" }] : /* istanbul ignore next */ []));
|
|
4220
|
+
/** Texte du tableau vide, sans gabarit `stTableauVide`. */
|
|
4221
|
+
texteVide = input(null, /* @ts-ignore */
|
|
4222
|
+
...(ngDevMode ? [{ debugName: "texteVide" }] : /* istanbul ignore next */ []));
|
|
4223
|
+
gabarits = contentChildren(StCellule, /* @ts-ignore */
|
|
4224
|
+
...(ngDevMode ? [{ debugName: "gabarits" }] : /* istanbul ignore next */ []));
|
|
4225
|
+
gabaritVide = contentChild(StTableauVide, /* @ts-ignore */
|
|
4226
|
+
...(ngDevMode ? [{ debugName: "gabaritVide" }] : /* istanbul ignore next */ []));
|
|
4227
|
+
zone = viewChild.required('zone', /* @ts-ignore */
|
|
4228
|
+
...(ngDevMode ? [{ debugName: "zone" }] : /* istanbul ignore next */ []));
|
|
4229
|
+
annonce = new AnnonceLocale(inject(DestroyRef));
|
|
4230
|
+
deborde = signal(false, /* @ts-ignore */
|
|
4231
|
+
...(ngDevMode ? [{ debugName: "deborde" }] : /* istanbul ignore next */ []));
|
|
4232
|
+
largeurFigee = signal(0, /* @ts-ignore */
|
|
4233
|
+
...(ngDevMode ? [{ debugName: "largeurFigee" }] : /* istanbul ignore next */ []));
|
|
4234
|
+
identifiants = (() => {
|
|
4235
|
+
const base = `st-tableau-${(compteur$3 += 1).toString()}`;
|
|
4236
|
+
return { legende: `${base}-legende`, selection: `${base}-selection` };
|
|
4237
|
+
})();
|
|
4238
|
+
cleDEnTete = computed(() => this.enTeteDeLigne() ?? this.colonnes()[0]?.cle ?? '', /* @ts-ignore */
|
|
4239
|
+
...(ngDevMode ? [{ debugName: "cleDEnTete" }] : /* istanbul ignore next */ []));
|
|
4240
|
+
cellules = computed(() => {
|
|
4241
|
+
const gabarits = new Map(this.gabarits().map((gabarit) => [gabarit.cle(), gabarit.gabarit]));
|
|
4242
|
+
return this.colonnes().map((colonne) => ({
|
|
4243
|
+
colonne,
|
|
4244
|
+
gabarit: gabarits.get(colonne.cle) ?? null,
|
|
4245
|
+
enTete: colonne.cle === this.cleDEnTete(),
|
|
4246
|
+
}));
|
|
4247
|
+
}, /* @ts-ignore */
|
|
4248
|
+
...(ngDevMode ? [{ debugName: "cellules" }] : /* istanbul ignore next */ []));
|
|
4249
|
+
rangees = computed(() => {
|
|
4250
|
+
const cle = this.cle();
|
|
4251
|
+
const entete = this.cleDEnTete();
|
|
4252
|
+
const choisies = new Set(this.selection());
|
|
4253
|
+
return this.lignes().map((valeur) => {
|
|
4254
|
+
const identifiant = String(valeur[cle]);
|
|
4255
|
+
return {
|
|
4256
|
+
valeur,
|
|
4257
|
+
identifiant,
|
|
4258
|
+
nom: texteDe(valeur[entete]),
|
|
4259
|
+
choisie: choisies.has(identifiant),
|
|
4260
|
+
};
|
|
4261
|
+
});
|
|
4262
|
+
}, /* @ts-ignore */
|
|
4263
|
+
...(ngDevMode ? [{ debugName: "rangees" }] : /* istanbul ignore next */ []));
|
|
4264
|
+
squelette = computed(() => this.chargement() && this.lignes().length === 0
|
|
4265
|
+
? Array.from({ length: LIGNES_DE_SQUELETTE }, (_, indice) => indice)
|
|
4266
|
+
: [], /* @ts-ignore */
|
|
4267
|
+
...(ngDevMode ? [{ debugName: "squelette" }] : /* istanbul ignore next */ []));
|
|
4268
|
+
nombreDeColonnes = computed(() => this.colonnes().length + (this.selectionnable() ? 1 : 0), /* @ts-ignore */
|
|
4269
|
+
...(ngDevMode ? [{ debugName: "nombreDeColonnes" }] : /* istanbul ignore next */ []));
|
|
4270
|
+
colonnesDeSquelette = computed(() => Array.from({ length: this.nombreDeColonnes() }, (_, indice) => indice), /* @ts-ignore */
|
|
4271
|
+
...(ngDevMode ? [{ debugName: "colonnesDeSquelette" }] : /* istanbul ignore next */ []));
|
|
4272
|
+
toutesChoisies = computed(() => {
|
|
4273
|
+
const rangees = this.rangees();
|
|
4274
|
+
return rangees.length > 0 && rangees.every((rangee) => rangee.choisie);
|
|
4275
|
+
}, /* @ts-ignore */
|
|
4276
|
+
...(ngDevMode ? [{ debugName: "toutesChoisies" }] : /* istanbul ignore next */ []));
|
|
4277
|
+
quelquesChoisies = computed(() => !this.toutesChoisies() && this.rangees().some((rangee) => rangee.choisie), /* @ts-ignore */
|
|
4278
|
+
...(ngDevMode ? [{ debugName: "quelquesChoisies" }] : /* istanbul ignore next */ []));
|
|
4279
|
+
constructor() {
|
|
4280
|
+
// V4-4 : une selection ne vise que ce qui est affiche. Nouvelle page, nouvelles
|
|
4281
|
+
// cles : les anciennes tombent.
|
|
4282
|
+
effect(() => {
|
|
4283
|
+
const presentes = new Set(this.rangees().map((rangee) => rangee.identifiant));
|
|
4284
|
+
const selection = this.selection();
|
|
4285
|
+
const gardees = selection.filter((identifiant) => presentes.has(identifiant));
|
|
4286
|
+
if (gardees.length !== selection.length)
|
|
4287
|
+
this.selection.set(gardees);
|
|
4288
|
+
});
|
|
4289
|
+
// Mesures du rendu, jamais sur le serveur (FE-5).
|
|
4290
|
+
const destruction = inject(DestroyRef);
|
|
4291
|
+
afterNextRender(() => {
|
|
4292
|
+
const zone = this.zone().nativeElement;
|
|
4293
|
+
if (typeof ResizeObserver === 'undefined')
|
|
4294
|
+
return;
|
|
4295
|
+
const observateur = new ResizeObserver(() => {
|
|
4296
|
+
this.deborde.set(zone.scrollWidth > zone.clientWidth + 1 || zone.scrollHeight > zone.clientHeight + 1);
|
|
4297
|
+
// UX-7 : la largeur des colonnes figees devient la marge de defilement,
|
|
4298
|
+
// pour que la cellule focalisee s'arrete A COTE d'elles et non dessous.
|
|
4299
|
+
const figees = zone.querySelectorAll('tr:first-child > [data-figee]');
|
|
4300
|
+
let largeur = 0;
|
|
4301
|
+
figees.forEach((cellule) => (largeur += cellule.getBoundingClientRect().width));
|
|
4302
|
+
this.largeurFigee.set(Math.ceil(largeur));
|
|
4303
|
+
});
|
|
4304
|
+
observateur.observe(zone);
|
|
4305
|
+
const tableau = zone.querySelector('table');
|
|
4306
|
+
if (tableau !== null)
|
|
4307
|
+
observateur.observe(tableau);
|
|
4308
|
+
destruction.onDestroy(() => {
|
|
4309
|
+
observateur.disconnect();
|
|
4310
|
+
});
|
|
4311
|
+
});
|
|
4312
|
+
}
|
|
4313
|
+
/** Amene le focus sur le tableau : sa legende est lue (UX-13). */
|
|
4314
|
+
focus(options) {
|
|
4315
|
+
this.zone().nativeElement.focus(options);
|
|
4316
|
+
}
|
|
4317
|
+
sensDe(colonne) {
|
|
4318
|
+
const tri = this.tri();
|
|
4319
|
+
return tri?.cle === colonne.cle ? tri.sens : null;
|
|
4320
|
+
}
|
|
4321
|
+
ariaSort(colonne) {
|
|
4322
|
+
const sens = this.sensDe(colonne);
|
|
4323
|
+
if (sens === null)
|
|
4324
|
+
return null;
|
|
4325
|
+
return sens === 'croissant' ? 'ascending' : 'descending';
|
|
4326
|
+
}
|
|
4327
|
+
trier(colonne) {
|
|
4328
|
+
const sens = this.sensDe(colonne) === 'croissant' ? 'decroissant' : 'croissant';
|
|
4329
|
+
this.tri.set({ cle: colonne.cle, sens });
|
|
4330
|
+
const libelles = this.libelles();
|
|
4331
|
+
this.annonce.annoncer(sens === 'croissant'
|
|
4332
|
+
? libelles.trieCroissant(colonne.libelle)
|
|
4333
|
+
: libelles.trieDecroissant(colonne.libelle));
|
|
4334
|
+
}
|
|
4335
|
+
basculerTout() {
|
|
4336
|
+
this.selection.set(this.toutesChoisies() ? [] : this.rangees().map((rangee) => rangee.identifiant));
|
|
4337
|
+
}
|
|
4338
|
+
basculer(identifiant) {
|
|
4339
|
+
const selection = this.selection();
|
|
4340
|
+
this.selection.set(selection.includes(identifiant)
|
|
4341
|
+
? selection.filter((choisie) => choisie !== identifiant)
|
|
4342
|
+
: [...selection, identifiant]);
|
|
4343
|
+
}
|
|
4344
|
+
texteDe(ligne, colonne) {
|
|
4345
|
+
return texteDe(ligne[colonne.cle]);
|
|
4346
|
+
}
|
|
4347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4348
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StDataTable, isStandalone: true, selector: "st-data-table", inputs: { legende: { classPropertyName: "legende", publicName: "legende", isSignal: true, isRequired: true, transformFunction: null }, legendeMasquee: { classPropertyName: "legendeMasquee", publicName: "legendeMasquee", isSignal: true, isRequired: false, transformFunction: null }, colonnes: { classPropertyName: "colonnes", publicName: "colonnes", isSignal: true, isRequired: true, transformFunction: null }, lignes: { classPropertyName: "lignes", publicName: "lignes", isSignal: true, isRequired: true, transformFunction: null }, cle: { classPropertyName: "cle", publicName: "cle", isSignal: true, isRequired: true, transformFunction: null }, enTeteDeLigne: { classPropertyName: "enTeteDeLigne", publicName: "enTeteDeLigne", isSignal: true, isRequired: false, transformFunction: null }, premiereColonneFigee: { classPropertyName: "premiereColonneFigee", publicName: "premiereColonneFigee", isSignal: true, isRequired: false, transformFunction: null }, hauteurMax: { classPropertyName: "hauteurMax", publicName: "hauteurMax", isSignal: true, isRequired: false, transformFunction: null }, densite: { classPropertyName: "densite", publicName: "densite", isSignal: true, isRequired: false, transformFunction: null }, chargement: { classPropertyName: "chargement", publicName: "chargement", isSignal: true, isRequired: false, transformFunction: null }, selectionnable: { classPropertyName: "selectionnable", publicName: "selectionnable", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, tri: { classPropertyName: "tri", publicName: "tri", isSignal: true, isRequired: false, transformFunction: null }, texteVide: { classPropertyName: "texteVide", publicName: "texteVide", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", tri: "triChange" }, host: { properties: { "attr.data-densite": "densite()", "attr.data-figee": "premiereColonneFigee() ? \"\" : null" }, classAttribute: "st-data-table" }, queries: [{ propertyName: "gabarits", predicate: StCellule, isSignal: true }, { propertyName: "gabaritVide", first: true, predicate: StTableauVide, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "zone", first: true, predicate: ["zone"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- UN seul ng-content : la projection se decide a la compilation, et un second\n emplacement de meme selecteur, dans une autre branche, ne recevrait jamais\n rien. -->\n<div class=\"st-data-table__barre\">\n @if (selectionnable()) {\n <p class=\"st-data-table__selection\" role=\"status\" [id]=\"identifiants.selection\">\n {{ selection().length > 0 ? libelles().lignesSelectionnees(selection().length) : '' }}\n </p>\n }\n <div class=\"st-data-table__actions\">\n <ng-content select=\"[stActionsDeTableau]\" />\n </div>\n</div>\n\n<!-- UX-6 : zone nommee par la legende ; focalisable quand elle deborde, et par\n programme toujours, pour recevoir le focus apres un changement de page. -->\n<div\n #zone\n class=\"st-data-table__zone\"\n role=\"region\"\n [attr.aria-labelledby]=\"identifiants.legende\"\n [attr.tabindex]=\"deborde() ? 0 : -1\"\n [attr.data-deborde]=\"deborde() ? '' : null\"\n [style.max-block-size]=\"hauteurMax()\"\n [style.scroll-padding-inline-start.px]=\"largeurFigee()\"\n>\n <table class=\"st-data-table__tableau\" [attr.aria-busy]=\"chargement() ? 'true' : null\">\n <caption\n class=\"st-data-table__legende\"\n [id]=\"identifiants.legende\"\n [class.st-vh]=\"legendeMasquee()\"\n >\n {{\n legende()\n }}\n </caption>\n <thead>\n <tr>\n @if (selectionnable()) {\n <th class=\"st-data-table__case\" scope=\"col\" data-figee>\n <input\n class=\"st-data-table__cocher\"\n type=\"checkbox\"\n [checked]=\"toutesChoisies()\"\n [indeterminate]=\"quelquesChoisies()\"\n [disabled]=\"rangees().length === 0\"\n [attr.aria-label]=\"libelles().selectionnerLaPage(rangees().length)\"\n (change)=\"basculerTout()\"\n />\n </th>\n }\n @for (cellule of cellules(); track cellule.colonne.cle; let premiere = $first) {\n <th\n scope=\"col\"\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n [attr.aria-sort]=\"ariaSort(cellule.colonne)\"\n >\n @if (cellule.colonne.triable) {\n <button\n stSortHeader\n type=\"button\"\n [sens]=\"sensDe(cellule.colonne)\"\n (click)=\"trier(cellule.colonne)\"\n >\n {{ cellule.colonne.libelle }}\n </button>\n } @else if (cellule.colonne.libelleMasque) {\n <span class=\"st-vh\">{{ cellule.colonne.libelle }}</span>\n } @else {\n {{ cellule.colonne.libelle }}\n }\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (rang of squelette(); track rang) {\n <tr class=\"st-data-table__squelette\" aria-hidden=\"true\">\n @for (colonne of colonnesDeSquelette(); track colonne) {\n <td><span class=\"st-data-table__barre-de-squelette\"></span></td>\n }\n </tr>\n }\n @for (rangee of rangees(); track rangee.identifiant) {\n <tr [attr.data-choisie]=\"rangee.choisie ? '' : null\">\n @if (selectionnable()) {\n <td class=\"st-data-table__case\" data-figee>\n <input\n class=\"st-data-table__cocher\"\n type=\"checkbox\"\n [checked]=\"rangee.choisie\"\n [attr.aria-label]=\"libelles().selectionnerLaLigne(rangee.nom ?? rangee.identifiant)\"\n (change)=\"basculer(rangee.identifiant)\"\n />\n </td>\n }\n @for (cellule of cellules(); track cellule.colonne.cle; let premiere = $first) {\n @if (cellule.enTete) {\n <th\n scope=\"row\"\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n valeur;\n context: { $implicit: rangee.valeur, cellule: cellule }\n \"\n />\n </th>\n } @else {\n <td\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n valeur;\n context: { $implicit: rangee.valeur, cellule: cellule }\n \"\n />\n </td>\n }\n }\n </tr>\n } @empty {\n @if (!chargement()) {\n <tr>\n <td class=\"st-data-table__vide\" [attr.colspan]=\"nombreDeColonnes()\">\n @if (gabaritVide(); as vide) {\n <ng-container *ngTemplateOutlet=\"vide.gabarit\" />\n } @else {\n {{ texteVide() ?? libelles().tableauVide }}\n }\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\n\n<ng-template #valeur let-ligne let-cellule=\"cellule\">\n @if (cellule.gabarit; as gabarit) {\n <ng-container\n *ngTemplateOutlet=\"gabarit; context: { $implicit: ligne, colonne: cellule.colonne }\"\n />\n } @else if (texteDe(ligne, cellule.colonne); as texte) {\n {{ texte }}\n } @else {\n <span aria-hidden=\"true\">—</span><span class=\"st-vh\">{{ libelles().nonRenseigne }}</span>\n }\n</ng-template>\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([hidden]){display:none!important}:host{--st-tableau-cellule-bloc: var(--st-espacement-3);--st-tableau-cellule-ligne: var(--st-espacement-3);--st-tableau-largeur-case: 3rem;display:block}:host([data-densite=compacte]){--st-tableau-cellule-bloc: var(--st-espacement-1);--st-tableau-cellule-ligne: var(--st-espacement-2)}.st-data-table__barre,.st-data-table__actions{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}.st-data-table__barre{justify-content:space-between}.st-data-table__barre:has(.st-data-table__selection,.st-data-table__actions:not(:empty)){min-block-size:var(--st-cible-tactile-min);margin-block-end:var(--st-espacement-2)}.st-data-table__actions{margin-inline-start:auto}.st-data-table__actions:empty{display:none}.st-data-table__selection{margin:0;font-weight:var(--st-graisse-semi)}.st-data-table__zone:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-data-table__zone{position:relative;overflow:auto;border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-md);background:linear-gradient(to right,var(--st-couleur-surface) 30%,transparent) left/2rem 100% no-repeat local,linear-gradient(to left,var(--st-couleur-surface) 30%,transparent) right/2rem 100% no-repeat local,radial-gradient(farthest-side at 0 50%,rgba(0,0,0,.2),transparent) left/.75rem 100% no-repeat scroll,radial-gradient(farthest-side at 100% 50%,rgba(0,0,0,.2),transparent) right/.75rem 100% no-repeat scroll,var(--st-couleur-surface)}.st-data-table__tableau{inline-size:100%;font-variant-numeric:tabular-nums;border-collapse:separate;border-spacing:0}.st-data-table__legende{padding:var(--st-espacement-3);font-weight:var(--st-graisse-semi);text-align:start}th,td{padding-block:var(--st-tableau-cellule-bloc);padding-inline:var(--st-tableau-cellule-ligne);text-align:start;vertical-align:top;overflow-wrap:anywhere;border-block-end:1px solid var(--st-couleur-bordure)}thead th{font-weight:var(--st-graisse-semi);color:var(--st-couleur-texte-discret);white-space:nowrap;background:var(--st-couleur-surface-enfoncee)}thead th{position:sticky;inset-block-start:0;z-index:2}tbody th{font-weight:var(--st-graisse-semi)}tbody tr:last-child>*{border-block-end:0}tbody tr:hover>*{background:var(--st-couleur-surface-survol)}tbody tr[data-choisie]>*{background:var(--st-couleur-primaire-discret)}tbody tr[data-choisie]>:first-child{box-shadow:inset 3px 0 0 var(--st-couleur-primaire)}:host-context([dir=rtl]) tbody tr[data-choisie]>:first-child{box-shadow:inset -3px 0 0 var(--st-couleur-primaire)}[data-alignement=fin]{text-align:end}.st-data-table__case{inline-size:var(--st-tableau-largeur-case);min-inline-size:var(--st-tableau-largeur-case);text-align:center;vertical-align:middle}.st-data-table__cocher:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-data-table__cocher{inline-size:24px;block-size:24px;margin:0;accent-color:var(--st-couleur-primaire);cursor:pointer}:host([data-figee]) [data-figee]{position:sticky;inset-inline-start:0;z-index:1;background:var(--st-couleur-surface)}:host([data-figee]) .st-data-table__case+[data-figee]{inset-inline-start:var(--st-tableau-largeur-case)}:host([data-figee]) thead [data-figee]{z-index:3;background:var(--st-couleur-surface-enfoncee)}.st-data-table__vide{padding:var(--st-espacement-5);color:var(--st-couleur-texte-discret);text-align:center}.st-data-table__barre-de-squelette{display:block;block-size:1em;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-sm)}@media(prefers-reduced-motion:no-preference){.st-data-table__barre-de-squelette{animation:st-tableau-pulsation 1.5s ease-in-out infinite}}@keyframes st-tableau-pulsation{50%{opacity:.5}}.st-data-table__tableau[aria-busy=true]{cursor:progress}@media(forced-colors:active){tbody tr[data-choisie]>:first-child{box-shadow:none;border-inline-start:3px solid Highlight}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StSortHeader, selector: "button[stSortHeader]", inputs: ["sens"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4349
|
+
}
|
|
4350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StDataTable, decorators: [{
|
|
4351
|
+
type: Component,
|
|
4352
|
+
args: [{ selector: 'st-data-table', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, StSortHeader], host: {
|
|
4353
|
+
class: 'st-data-table',
|
|
4354
|
+
'[attr.data-densite]': 'densite()',
|
|
4355
|
+
'[attr.data-figee]': 'premiereColonneFigee() ? "" : null',
|
|
4356
|
+
}, template: "<!-- UN seul ng-content : la projection se decide a la compilation, et un second\n emplacement de meme selecteur, dans une autre branche, ne recevrait jamais\n rien. -->\n<div class=\"st-data-table__barre\">\n @if (selectionnable()) {\n <p class=\"st-data-table__selection\" role=\"status\" [id]=\"identifiants.selection\">\n {{ selection().length > 0 ? libelles().lignesSelectionnees(selection().length) : '' }}\n </p>\n }\n <div class=\"st-data-table__actions\">\n <ng-content select=\"[stActionsDeTableau]\" />\n </div>\n</div>\n\n<!-- UX-6 : zone nommee par la legende ; focalisable quand elle deborde, et par\n programme toujours, pour recevoir le focus apres un changement de page. -->\n<div\n #zone\n class=\"st-data-table__zone\"\n role=\"region\"\n [attr.aria-labelledby]=\"identifiants.legende\"\n [attr.tabindex]=\"deborde() ? 0 : -1\"\n [attr.data-deborde]=\"deborde() ? '' : null\"\n [style.max-block-size]=\"hauteurMax()\"\n [style.scroll-padding-inline-start.px]=\"largeurFigee()\"\n>\n <table class=\"st-data-table__tableau\" [attr.aria-busy]=\"chargement() ? 'true' : null\">\n <caption\n class=\"st-data-table__legende\"\n [id]=\"identifiants.legende\"\n [class.st-vh]=\"legendeMasquee()\"\n >\n {{\n legende()\n }}\n </caption>\n <thead>\n <tr>\n @if (selectionnable()) {\n <th class=\"st-data-table__case\" scope=\"col\" data-figee>\n <input\n class=\"st-data-table__cocher\"\n type=\"checkbox\"\n [checked]=\"toutesChoisies()\"\n [indeterminate]=\"quelquesChoisies()\"\n [disabled]=\"rangees().length === 0\"\n [attr.aria-label]=\"libelles().selectionnerLaPage(rangees().length)\"\n (change)=\"basculerTout()\"\n />\n </th>\n }\n @for (cellule of cellules(); track cellule.colonne.cle; let premiere = $first) {\n <th\n scope=\"col\"\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n [attr.aria-sort]=\"ariaSort(cellule.colonne)\"\n >\n @if (cellule.colonne.triable) {\n <button\n stSortHeader\n type=\"button\"\n [sens]=\"sensDe(cellule.colonne)\"\n (click)=\"trier(cellule.colonne)\"\n >\n {{ cellule.colonne.libelle }}\n </button>\n } @else if (cellule.colonne.libelleMasque) {\n <span class=\"st-vh\">{{ cellule.colonne.libelle }}</span>\n } @else {\n {{ cellule.colonne.libelle }}\n }\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (rang of squelette(); track rang) {\n <tr class=\"st-data-table__squelette\" aria-hidden=\"true\">\n @for (colonne of colonnesDeSquelette(); track colonne) {\n <td><span class=\"st-data-table__barre-de-squelette\"></span></td>\n }\n </tr>\n }\n @for (rangee of rangees(); track rangee.identifiant) {\n <tr [attr.data-choisie]=\"rangee.choisie ? '' : null\">\n @if (selectionnable()) {\n <td class=\"st-data-table__case\" data-figee>\n <input\n class=\"st-data-table__cocher\"\n type=\"checkbox\"\n [checked]=\"rangee.choisie\"\n [attr.aria-label]=\"libelles().selectionnerLaLigne(rangee.nom ?? rangee.identifiant)\"\n (change)=\"basculer(rangee.identifiant)\"\n />\n </td>\n }\n @for (cellule of cellules(); track cellule.colonne.cle; let premiere = $first) {\n @if (cellule.enTete) {\n <th\n scope=\"row\"\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n valeur;\n context: { $implicit: rangee.valeur, cellule: cellule }\n \"\n />\n </th>\n } @else {\n <td\n [attr.data-alignement]=\"cellule.colonne.alignement ?? 'debut'\"\n [attr.data-figee]=\"premiere && premiereColonneFigee() ? '' : null\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n valeur;\n context: { $implicit: rangee.valeur, cellule: cellule }\n \"\n />\n </td>\n }\n }\n </tr>\n } @empty {\n @if (!chargement()) {\n <tr>\n <td class=\"st-data-table__vide\" [attr.colspan]=\"nombreDeColonnes()\">\n @if (gabaritVide(); as vide) {\n <ng-container *ngTemplateOutlet=\"vide.gabarit\" />\n } @else {\n {{ texteVide() ?? libelles().tableauVide }}\n }\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\n\n<ng-template #valeur let-ligne let-cellule=\"cellule\">\n @if (cellule.gabarit; as gabarit) {\n <ng-container\n *ngTemplateOutlet=\"gabarit; context: { $implicit: ligne, colonne: cellule.colonne }\"\n />\n } @else if (texteDe(ligne, cellule.colonne); as texte) {\n {{ texte }}\n } @else {\n <span aria-hidden=\"true\">—</span><span class=\"st-vh\">{{ libelles().nonRenseigne }}</span>\n }\n</ng-template>\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([hidden]){display:none!important}:host{--st-tableau-cellule-bloc: var(--st-espacement-3);--st-tableau-cellule-ligne: var(--st-espacement-3);--st-tableau-largeur-case: 3rem;display:block}:host([data-densite=compacte]){--st-tableau-cellule-bloc: var(--st-espacement-1);--st-tableau-cellule-ligne: var(--st-espacement-2)}.st-data-table__barre,.st-data-table__actions{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}.st-data-table__barre{justify-content:space-between}.st-data-table__barre:has(.st-data-table__selection,.st-data-table__actions:not(:empty)){min-block-size:var(--st-cible-tactile-min);margin-block-end:var(--st-espacement-2)}.st-data-table__actions{margin-inline-start:auto}.st-data-table__actions:empty{display:none}.st-data-table__selection{margin:0;font-weight:var(--st-graisse-semi)}.st-data-table__zone:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-data-table__zone{position:relative;overflow:auto;border:1px solid var(--st-couleur-bordure);border-radius:var(--st-rayon-md);background:linear-gradient(to right,var(--st-couleur-surface) 30%,transparent) left/2rem 100% no-repeat local,linear-gradient(to left,var(--st-couleur-surface) 30%,transparent) right/2rem 100% no-repeat local,radial-gradient(farthest-side at 0 50%,rgba(0,0,0,.2),transparent) left/.75rem 100% no-repeat scroll,radial-gradient(farthest-side at 100% 50%,rgba(0,0,0,.2),transparent) right/.75rem 100% no-repeat scroll,var(--st-couleur-surface)}.st-data-table__tableau{inline-size:100%;font-variant-numeric:tabular-nums;border-collapse:separate;border-spacing:0}.st-data-table__legende{padding:var(--st-espacement-3);font-weight:var(--st-graisse-semi);text-align:start}th,td{padding-block:var(--st-tableau-cellule-bloc);padding-inline:var(--st-tableau-cellule-ligne);text-align:start;vertical-align:top;overflow-wrap:anywhere;border-block-end:1px solid var(--st-couleur-bordure)}thead th{font-weight:var(--st-graisse-semi);color:var(--st-couleur-texte-discret);white-space:nowrap;background:var(--st-couleur-surface-enfoncee)}thead th{position:sticky;inset-block-start:0;z-index:2}tbody th{font-weight:var(--st-graisse-semi)}tbody tr:last-child>*{border-block-end:0}tbody tr:hover>*{background:var(--st-couleur-surface-survol)}tbody tr[data-choisie]>*{background:var(--st-couleur-primaire-discret)}tbody tr[data-choisie]>:first-child{box-shadow:inset 3px 0 0 var(--st-couleur-primaire)}:host-context([dir=rtl]) tbody tr[data-choisie]>:first-child{box-shadow:inset -3px 0 0 var(--st-couleur-primaire)}[data-alignement=fin]{text-align:end}.st-data-table__case{inline-size:var(--st-tableau-largeur-case);min-inline-size:var(--st-tableau-largeur-case);text-align:center;vertical-align:middle}.st-data-table__cocher:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-data-table__cocher{inline-size:24px;block-size:24px;margin:0;accent-color:var(--st-couleur-primaire);cursor:pointer}:host([data-figee]) [data-figee]{position:sticky;inset-inline-start:0;z-index:1;background:var(--st-couleur-surface)}:host([data-figee]) .st-data-table__case+[data-figee]{inset-inline-start:var(--st-tableau-largeur-case)}:host([data-figee]) thead [data-figee]{z-index:3;background:var(--st-couleur-surface-enfoncee)}.st-data-table__vide{padding:var(--st-espacement-5);color:var(--st-couleur-texte-discret);text-align:center}.st-data-table__barre-de-squelette{display:block;block-size:1em;background:var(--st-couleur-surface-enfoncee);border-radius:var(--st-rayon-sm)}@media(prefers-reduced-motion:no-preference){.st-data-table__barre-de-squelette{animation:st-tableau-pulsation 1.5s ease-in-out infinite}}@keyframes st-tableau-pulsation{50%{opacity:.5}}.st-data-table__tableau[aria-busy=true]{cursor:progress}@media(forced-colors:active){tbody tr[data-choisie]>:first-child{box-shadow:none;border-inline-start:3px solid Highlight}}\n"] }]
|
|
4357
|
+
}], ctorParameters: () => [], propDecorators: { legende: [{ type: i0.Input, args: [{ isSignal: true, alias: "legende", required: true }] }], legendeMasquee: [{ type: i0.Input, args: [{ isSignal: true, alias: "legendeMasquee", required: false }] }], colonnes: [{ type: i0.Input, args: [{ isSignal: true, alias: "colonnes", required: true }] }], lignes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lignes", required: true }] }], cle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cle", required: true }] }], enTeteDeLigne: [{ type: i0.Input, args: [{ isSignal: true, alias: "enTeteDeLigne", required: false }] }], premiereColonneFigee: [{ type: i0.Input, args: [{ isSignal: true, alias: "premiereColonneFigee", required: false }] }], hauteurMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "hauteurMax", required: false }] }], densite: [{ type: i0.Input, args: [{ isSignal: true, alias: "densite", required: false }] }], chargement: [{ type: i0.Input, args: [{ isSignal: true, alias: "chargement", required: false }] }], selectionnable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionnable", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], tri: [{ type: i0.Input, args: [{ isSignal: true, alias: "tri", required: false }] }, { type: i0.Output, args: ["triChange"] }], texteVide: [{ type: i0.Input, args: [{ isSignal: true, alias: "texteVide", required: false }] }], gabarits: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StCellule), { isSignal: true }] }], gabaritVide: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StTableauVide), { isSignal: true }] }], zone: [{ type: i0.ViewChild, args: ['zone', { isSignal: true }] }] } });
|
|
4358
|
+
/** Texte d'une valeur ; `null` quand elle est absente, pour le dire (UX de la vague 4). */
|
|
4359
|
+
function texteDe(valeur) {
|
|
4360
|
+
if (valeur === null || valeur === undefined || valeur === '')
|
|
4361
|
+
return null;
|
|
4362
|
+
if (typeof valeur === 'string')
|
|
4363
|
+
return valeur;
|
|
4364
|
+
if (typeof valeur === 'number' || typeof valeur === 'boolean' || typeof valeur === 'bigint') {
|
|
4365
|
+
return String(valeur);
|
|
4366
|
+
}
|
|
4367
|
+
return null;
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4370
|
+
/**
|
|
4371
|
+
* Filtre actif, retirable : « Statut : Expediee ✕ ».
|
|
4372
|
+
*
|
|
4373
|
+
* ```html
|
|
4374
|
+
* <button stFilterChip libelle="Statut : Expediee" (click)="retirer('statut')"></button>
|
|
4375
|
+
* ```
|
|
4376
|
+
*
|
|
4377
|
+
* ⚠️ **Toute la puce est le bouton**, et son nom CONTIENT le texte visible —
|
|
4378
|
+
* « Retirer le filtre Statut : Expediee » (WCAG 2.5.3) : une commande vocale
|
|
4379
|
+
* « cliquer Statut » l'atteint. Une croix seule, a cote d'un texte inerte, serait
|
|
4380
|
+
* une cible de quelques pixels sans nom.
|
|
4381
|
+
*/
|
|
4382
|
+
class StFilterChip {
|
|
4383
|
+
libelles = inject(ST_LIBELLES);
|
|
4384
|
+
libelle = input.required(/* @ts-ignore */
|
|
4385
|
+
...(ngDevMode ? [{ debugName: "libelle" }] : /* istanbul ignore next */ []));
|
|
4386
|
+
nom = computed(() => this.libelles().retirerLeFiltre(this.libelle()), /* @ts-ignore */
|
|
4387
|
+
...(ngDevMode ? [{ debugName: "nom" }] : /* istanbul ignore next */ []));
|
|
4388
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StFilterChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4389
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StFilterChip, isStandalone: true, selector: "button[stFilterChip]", inputs: { libelle: { classPropertyName: "libelle", publicName: "libelle", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "type": "button" }, properties: { "attr.aria-label": "nom()" }, classAttribute: "st-filter-chip" }, ngImport: i0, template: "<span class=\"st-filter-chip__libelle\">{{ libelle() }}</span>\n<st-icon class=\"st-filter-chip__croix\" nom=\"croix\" taille=\"sm\" />\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([hidden]){display:none!important}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}:host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:32px;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3) var(--st-espacement-2);font:inherit;color:var(--st-couleur-texte);cursor:pointer;background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-pleine)}:host:hover{background:var(--st-couleur-surface-survol)}.st-filter-chip__croix{flex:none}\n"], dependencies: [{ kind: "component", type: StIcon, selector: "st-icon", inputs: ["nom", "taille", "ton", "etiquette"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4390
|
+
}
|
|
4391
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StFilterChip, decorators: [{
|
|
4392
|
+
type: Component,
|
|
4393
|
+
args: [{ selector: 'button[stFilterChip]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StIcon], host: {
|
|
4394
|
+
class: 'st-filter-chip',
|
|
4395
|
+
type: 'button',
|
|
4396
|
+
'[attr.aria-label]': 'nom()',
|
|
4397
|
+
}, template: "<span class=\"st-filter-chip__libelle\">{{ libelle() }}</span>\n<st-icon class=\"st-filter-chip__croix\" nom=\"croix\" taille=\"sm\" />\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([hidden]){display:none!important}:host:focus-visible{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}:host{display:inline-flex;gap:var(--st-espacement-1);align-items:center;min-block-size:32px;padding-block:var(--st-espacement-1);padding-inline:var(--st-espacement-3) var(--st-espacement-2);font:inherit;color:var(--st-couleur-texte);cursor:pointer;background:var(--st-couleur-surface-enfoncee);border:1px solid var(--st-couleur-bordure-forte);border-radius:var(--st-rayon-pleine)}:host:hover{background:var(--st-couleur-surface-survol)}.st-filter-chip__croix{flex:none}\n"] }]
|
|
4398
|
+
}], propDecorators: { libelle: [{ type: i0.Input, args: [{ isSignal: true, alias: "libelle", required: true }] }] } });
|
|
4399
|
+
|
|
4400
|
+
let compteur$2 = 0;
|
|
4401
|
+
/**
|
|
4402
|
+
* Barre de filtres : les champs, puis les filtres en vigueur, retirables.
|
|
4403
|
+
*
|
|
4404
|
+
* ```html
|
|
4405
|
+
* <st-filter-bar [filtresActifs]="actifs()" (retrait)="retirer($event)" (effacement)="effacer()">
|
|
4406
|
+
* <st-select libelle="Statut" [options]="statuts" [(value)]="statut" />
|
|
4407
|
+
* </st-filter-bar>
|
|
4408
|
+
* ```
|
|
4409
|
+
*
|
|
4410
|
+
* Un formulaire NOMME : repere « Filtres » dans la liste des reperes du lecteur
|
|
4411
|
+
* d'ecran. Pas un repere `search` : un `st-search-field` pose dedans en porte deja
|
|
4412
|
+
* un, et deux reperes imbriques se liraient en double.
|
|
4413
|
+
*
|
|
4414
|
+
* ⚠️ **Les filtres s'appliquent au changement**, par l'application : Entree dans
|
|
4415
|
+
* un champ n'envoie rien, et ne recharge pas la page.
|
|
4416
|
+
*
|
|
4417
|
+
* ⚠️ **Retirer un filtre rend le focus a la puce suivante**, sinon a la
|
|
4418
|
+
* precedente, sinon au formulaire ; « Effacer les filtres » le rend au formulaire
|
|
4419
|
+
* (UX-14). Le focus ne tombe jamais sur le document, et le retrait s'annonce.
|
|
4420
|
+
*
|
|
4421
|
+
* ⚠️ **Dans l'adresse, des valeurs d'enumeration seulement** (SEC-2) : un texte
|
|
4422
|
+
* cherche, un nom, un IBAN n'y vont pas. Le composant ne touche pas a l'adresse ;
|
|
4423
|
+
* la regle est pour l'application qui le ferait.
|
|
4424
|
+
*/
|
|
4425
|
+
class StFilterBar {
|
|
4426
|
+
libelles = inject(ST_LIBELLES);
|
|
4427
|
+
element = inject(ElementRef);
|
|
4428
|
+
injecteur = inject(Injector);
|
|
4429
|
+
/** Nom du formulaire. Par defaut : « Filtres ». */
|
|
4430
|
+
etiquette = input(null, /* @ts-ignore */
|
|
4431
|
+
...(ngDevMode ? [{ debugName: "etiquette" }] : /* istanbul ignore next */ []));
|
|
4432
|
+
filtresActifs = input([], /* @ts-ignore */
|
|
4433
|
+
...(ngDevMode ? [{ debugName: "filtresActifs" }] : /* istanbul ignore next */ []));
|
|
4434
|
+
retrait = output();
|
|
4435
|
+
effacement = output();
|
|
4436
|
+
annonce = new AnnonceLocale(inject(DestroyRef));
|
|
4437
|
+
identifiantActifs = `st-filtres-${(compteur$2 += 1).toString()}-actifs`;
|
|
4438
|
+
etiquetteLue = computed(() => this.etiquette() ?? this.libelles().filtres, /* @ts-ignore */
|
|
4439
|
+
...(ngDevMode ? [{ debugName: "etiquetteLue" }] : /* istanbul ignore next */ []));
|
|
4440
|
+
retirer(filtre, indice) {
|
|
4441
|
+
this.retrait.emit(filtre.cle);
|
|
4442
|
+
this.annonce.annoncer(this.libelles().filtreRetire(filtre.libelle));
|
|
4443
|
+
this.apresLeRendu(() => {
|
|
4444
|
+
const puces = this.element.nativeElement.querySelectorAll('.st-filter-chip');
|
|
4445
|
+
return puces[Math.min(indice, puces.length - 1)] ?? null;
|
|
4446
|
+
});
|
|
4447
|
+
}
|
|
4448
|
+
effacer() {
|
|
4449
|
+
this.effacement.emit();
|
|
4450
|
+
this.annonce.annoncer(this.libelles().filtresEffaces);
|
|
4451
|
+
this.apresLeRendu(() => null);
|
|
4452
|
+
}
|
|
4453
|
+
apresLeRendu(cible) {
|
|
4454
|
+
afterNextRender(() => {
|
|
4455
|
+
const formulaire = this.element.nativeElement.querySelector('form');
|
|
4456
|
+
(cible() ?? formulaire)?.focus();
|
|
4457
|
+
}, { injector: this.injecteur });
|
|
4458
|
+
}
|
|
4459
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StFilterBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4460
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: StFilterBar, isStandalone: true, selector: "st-filter-bar", inputs: { etiquette: { classPropertyName: "etiquette", publicName: "etiquette", isSignal: true, isRequired: false, transformFunction: null }, filtresActifs: { classPropertyName: "filtresActifs", publicName: "filtresActifs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retrait: "retrait", effacement: "effacement" }, host: { classAttribute: "st-filter-bar" }, ngImport: i0, template: "<!-- tabindex -1 : cible du focus quand la derniere puce disparait. -->\n<form\n class=\"st-filter-bar__formulaire\"\n tabindex=\"-1\"\n [attr.aria-label]=\"etiquetteLue()\"\n (submit)=\"$event.preventDefault()\"\n>\n <div class=\"st-filter-bar__champs\">\n <ng-content />\n </div>\n\n @if (filtresActifs().length > 0) {\n <div class=\"st-filter-bar__actifs\">\n <p class=\"st-filter-bar__titre\" [id]=\"identifiantActifs\">{{ libelles().filtresActifs }}</p>\n <ul class=\"st-filter-bar__puces\" [attr.aria-labelledby]=\"identifiantActifs\">\n @for (filtre of filtresActifs(); track filtre.cle; let indice = $index) {\n <li>\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stFilterChip, et le libelle est requis. -->\n <button\n stFilterChip\n type=\"button\"\n [libelle]=\"filtre.libelle\"\n (click)=\"retirer(filtre, indice)\"\n ></button>\n </li>\n }\n </ul>\n <button stButton type=\"button\" variante=\"discret\" (click)=\"effacer()\">\n {{ libelles().effacerLesFiltres }}\n </button>\n </div>\n }\n</form>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-filter-bar__formulaire:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-filter-bar__formulaire{display:grid;gap:var(--st-espacement-3)}.st-filter-bar__champs{display:flex;flex-wrap:wrap;gap:var(--st-espacement-3) var(--st-espacement-4);align-items:flex-end}.st-filter-bar__actifs{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}.st-filter-bar__titre{margin:0;font-weight:var(--st-graisse-semi)}.st-filter-bar__puces{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);padding:0;margin:0;list-style:none}\n"], dependencies: [{ kind: "component", type: StButton, selector: "button[stButton]", inputs: ["variante", "taille", "pleineLargeur", "iconeAvant", "iconeApres", "chargement", "indisponible"] }, { kind: "component", type: StFilterChip, selector: "button[stFilterChip]", inputs: ["libelle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4461
|
+
}
|
|
4462
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StFilterBar, decorators: [{
|
|
4463
|
+
type: Component,
|
|
4464
|
+
args: [{ selector: 'st-filter-bar', changeDetection: ChangeDetectionStrategy.OnPush, imports: [StButton, StFilterChip], host: { class: 'st-filter-bar' }, template: "<!-- tabindex -1 : cible du focus quand la derniere puce disparait. -->\n<form\n class=\"st-filter-bar__formulaire\"\n tabindex=\"-1\"\n [attr.aria-label]=\"etiquetteLue()\"\n (submit)=\"$event.preventDefault()\"\n>\n <div class=\"st-filter-bar__champs\">\n <ng-content />\n </div>\n\n @if (filtresActifs().length > 0) {\n <div class=\"st-filter-bar__actifs\">\n <p class=\"st-filter-bar__titre\" [id]=\"identifiantActifs\">{{ libelles().filtresActifs }}</p>\n <ul class=\"st-filter-bar__puces\" [attr.aria-labelledby]=\"identifiantActifs\">\n @for (filtre of filtresActifs(); track filtre.cle; let indice = $index) {\n <li>\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content --\n Le contenu vient du gabarit de stFilterChip, et le libelle est requis. -->\n <button\n stFilterChip\n type=\"button\"\n [libelle]=\"filtre.libelle\"\n (click)=\"retirer(filtre, indice)\"\n ></button>\n </li>\n }\n </ul>\n <button stButton type=\"button\" variante=\"discret\" (click)=\"effacer()\">\n {{ libelles().effacerLesFiltres }}\n </button>\n </div>\n }\n</form>\n<span class=\"st-vh\" aria-live=\"polite\">{{ annonce.texte() }}</span>\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([hidden]){display:none!important}:host{display:block}.st-filter-bar__formulaire:focus{outline:2px solid var(--st-couleur-anneau-focus);outline-offset:2px}.st-filter-bar__formulaire{display:grid;gap:var(--st-espacement-3)}.st-filter-bar__champs{display:flex;flex-wrap:wrap;gap:var(--st-espacement-3) var(--st-espacement-4);align-items:flex-end}.st-filter-bar__actifs{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);align-items:center}.st-filter-bar__titre{margin:0;font-weight:var(--st-graisse-semi)}.st-filter-bar__puces{display:flex;flex-wrap:wrap;gap:var(--st-espacement-2);padding:0;margin:0;list-style:none}\n"] }]
|
|
4465
|
+
}], propDecorators: { etiquette: [{ type: i0.Input, args: [{ isSignal: true, alias: "etiquette", required: false }] }], filtresActifs: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtresActifs", required: false }] }], retrait: [{ type: i0.Output, args: ["retrait"] }], effacement: [{ type: i0.Output, args: ["effacement"] }] } });
|
|
4466
|
+
|
|
3784
4467
|
let compteur$1 = 0;
|
|
3785
4468
|
/**
|
|
3786
4469
|
* Choix du theme : systeme, clair, sombre.
|
|
@@ -4418,5 +5101,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
|
|
|
4418
5101
|
* Generated bundle index. Do not edit.
|
|
4419
5102
|
*/
|
|
4420
5103
|
|
|
4421
|
-
export { AnnonceLocale, CHEMIN_DU_SPRITE, ContratDeChamp, ControleDeSaisie, FUSEAU_PAR_DEFAUT, GABARITS_D_ETAT, IDENTIFIANT_DU_CONTENU_PRINCIPAL, LIBELLES_EN, LIBELLES_FR, NOMS_DICONE, REGION_VIVANTE_PARENTE, ST_LIBELLES, ST_LOCALE, StAppShell, StAvatar, StBadge, StBanner, StBreadcrumb, StButton, StButtonGroup, StCalendar, StCard, StChargement, StCheckbox, StCode, StControleDeChamp, StCopyButton, StCount, StDate, StDivider, StEmpty, StErreur, StError, StErrorSummary, StFieldset, StFileUpload, StFormField, StHeader, StHeading, StHorsLigne, StIbanField, StIcon, StIconButton, StInlineMessage, StInput, StLangSwitch, StLink, StLogo, StMasked, StMoney, StOfflineBanner, StOtpField, StPageTitle, StPasswordField, StPhoneField, StProgress, StRadioGroup, StRelativeTime, StSearchField, StSection, StSelect, StSkeleton, StSkipLink, StSpinner, StState, StStatusPill, StSucces, StSwitch, StTag, StText, StTextarea, StThemeToggle, StTimer, StVide, copierDansLePressePapiers, erreurDate, erreurIban, erreurPlageDeDates, erreurTelephone, erreursDeFichiers, exempleDeDate, filtrerOptions, formaterDuree, fournirCheminDuSprite, fusionnerErreurs, messageDErreur, motifLocal, normaliserPourRecherche, pressePapiersDisponible, provideStarterUi };
|
|
5104
|
+
export { AnnonceLocale, CHEMIN_DU_SPRITE, ContratDeChamp, ControleDeSaisie, FUSEAU_PAR_DEFAUT, GABARITS_D_ETAT, IDENTIFIANT_DU_CONTENU_PRINCIPAL, LIBELLES_EN, LIBELLES_FR, NOMS_DICONE, REGION_VIVANTE_PARENTE, ST_LIBELLES, ST_LOCALE, StAppShell, StAvatar, StBadge, StBanner, StBreadcrumb, StButton, StButtonGroup, StCalendar, StCard, StCellule, StChargement, StCheckbox, StCode, StControleDeChamp, StCopyButton, StCount, StDataTable, StDate, StDescription, StDescriptionList, StDivider, StElementDeListe, StEmpty, StErreur, StError, StErrorSummary, StFieldset, StFileUpload, StFilterBar, StFilterChip, StFormField, StHeader, StHeading, StHorsLigne, StIbanField, StIcon, StIconButton, StInlineMessage, StInput, StLangSwitch, StLink, StList, StLogo, StMasked, StMoney, StOfflineBanner, StOtpField, StPageTitle, StPagination, StPasswordField, StPhoneField, StProgress, StRadioGroup, StRelativeTime, StSearchField, StSection, StSelect, StSkeleton, StSkipLink, StSortHeader, StSpinner, StState, StStatusPill, StSucces, StSwitch, StTableauVide, StTag, StText, StTextarea, StThemeToggle, StTimer, StVide, copierDansLePressePapiers, erreurDate, erreurIban, erreurPlageDeDates, erreurTelephone, erreursDeFichiers, exempleDeDate, filtrerOptions, formaterDuree, fournirCheminDuSprite, fusionnerErreurs, messageDErreur, motifLocal, normaliserPourRecherche, pressePapiersDisponible, provideStarterUi };
|
|
4422
5105
|
//# sourceMappingURL=starterlib-ui.mjs.map
|