@starterlib/ui 0.10.12 → 0.10.14

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.
@@ -3,7 +3,7 @@ import { AnneesDuCalendrier, ContratDeChamp, ContratDeChampABornes, ControleDeSa
3
3
  import * as i0 from "@angular/core";
4
4
  import { InjectionToken, Provider, Signal } from "@angular/core";
5
5
  import { AnnonceLocale, CatalogueDuPointDEntree, LibellesUi } from "@starterlib/ui";
6
- import { DateIso, FrequenceDeRecurrence, HeureIso, PlageDeDates, Recurrence } from "@starterlib/util/valeurs";
6
+ import { DateIso, FrequenceDeRecurrence, HeureIso, MoisIso, PartieDeDate, PlageDeDates, Recurrence } from "@starterlib/util/valeurs";
7
7
  /**
8
8
  * L'envoi d'un formulaire valide. Il rend les refus du serveur, que Signal Forms place sous leur
9
9
  * champ (`fieldTree`) et au recapitulatif, puis efface des que la valeur du champ change ; rien
@@ -257,6 +257,14 @@ export interface LibellesFormulaires {
257
257
  readonly derniereEcheance: string;
258
258
  readonly nombreDEcheances: string;
259
259
  readonly recapitulatif: string;
260
+ /** Les cases de `st-memorable-date` (lot 267) ; l'annee est celle du tronc (`annee`). */
261
+ readonly jour: string;
262
+ readonly mois: string;
263
+ /** L'exemple dit AVANT la saisie, dans l'ordre de la langue (« 27 3 1990 »). */
264
+ readonly exempleDeDateMemorable: (exemple: string) => string;
265
+ /** Les parties qui manquent, dans l'ordre de la langue : jamais les trois (le champ est vide). */
266
+ readonly dateMemorableIncomplete: (manque: readonly PartieDeDate[]) => string;
267
+ readonly dateMemorableInexistante: (exemple: string) => string;
260
268
  }
261
269
  export declare const LIBELLES_FORMULAIRES_FR: LibellesFormulaires;
262
270
  export declare const LIBELLES_FORMULAIRES_EN: LibellesFormulaires;
@@ -828,6 +836,129 @@ export declare class StDateRange extends ContratDeChamp implements FormValueCont
828
836
  "value": "valueChange";
829
837
  }, never, never, true, never>;
830
838
  }
839
+ /**
840
+ * Mois d'une annee : un champ texte, au format de la langue courante (`mm/aaaa`). Composant bonus du
841
+ * lot 267 : sa condition d'entree, son poids et ce qu'il n'est pas sont dans `README.md` a cote.
842
+ *
843
+ * ```html
844
+ * <st-month-field libelle="Periode du releve" [formField]="releve.periode" />
845
+ * ```
846
+ *
847
+ * Le modele est un mois ISO `AAAA-MM` ou `null`, jamais un objet `Date` : un releve de septembre n'a
848
+ * ni jour ni fuseau.
849
+ *
850
+ * ⚠️ **Un champ texte, jamais `type="month"`** (D7 de l'audit de la vague 3, comme les dates et les
851
+ * heures) : le champ natif n'existe ni dans Firefox ni dans Safari de bureau, et suit la langue du
852
+ * NAVIGATEUR. Le format attendu et un exemple sont dits en aide, AVANT la saisie.
853
+ *
854
+ * ⚠️ **Rien n'est devine** (`lireMois`) : l'annee s'ecrit en quatre chiffres ; « 092026 » se lit
855
+ * 09/2026 (le pave numerique d'un telephone n'a pas de barre). Une saisie illisible garde le texte,
856
+ * vide le modele et rend le formulaire invalide, comme `st-date-field`.
857
+ */
858
+ export declare class StMonthField extends ContratDeChamp implements FormValueControl<MoisIso | null> {
859
+ protected readonly libelles: import("@angular/core").Signal<import("@starterlib/ui").LibellesUi>;
860
+ private readonly locale;
861
+ readonly value: import("@angular/core").ModelSignal<string | null>;
862
+ readonly autocomplete: import("@angular/core").InputSignal<string | null>;
863
+ /**
864
+ * Quitte au moins une fois. Relie a un formulaire, `touched` le dit ; employe seul, rien ne le
865
+ * dirait, et une saisie illisible ne s'afficherait jamais.
866
+ */
867
+ protected readonly quitte: import("@angular/core").WritableSignal<boolean>;
868
+ protected readonly saisie: import("@angular/forms/signals").TransformedValueSignal<string>;
869
+ /** La saisie est-elle illisible ? Le modele vaut alors `null`, comme un champ vide. */
870
+ readonly lectureInvalide: import("@angular/core").Signal<boolean>;
871
+ protected readonly aideLue: import("@angular/core").Signal<string>;
872
+ protected readonly erreursAffichees: import("@angular/core").Signal<readonly import("@starterlib/ui/saisie").ErreurAffichable[]>;
873
+ protected surSaisie(evenement: Event): void;
874
+ protected aLaSortieDuChamp(): void;
875
+ private formater;
876
+ static ɵfac: i0.ɵɵFactoryDeclaration<StMonthField, never>;
877
+ static ɵcmp: i0.ɵɵComponentDeclaration<StMonthField, "st-month-field", never, {
878
+ "value": {
879
+ "alias": "value";
880
+ "required": false;
881
+ "isSignal": true;
882
+ };
883
+ "autocomplete": {
884
+ "alias": "autocomplete";
885
+ "required": false;
886
+ "isSignal": true;
887
+ };
888
+ }, {
889
+ "value": "valueChange";
890
+ }, never, never, true, never>;
891
+ }
892
+ /**
893
+ * Date memorable : jour, mois et annee en trois cases numeriques, dans l'ordre de la langue
894
+ * courante. Composant bonus du lot 267 : sa condition d'entree, son poids et ce qu'il n'est pas sont
895
+ * dans `README.md` a cote.
896
+ *
897
+ * ```html
898
+ * <st-memorable-date libelle="Date de naissance" naissance [formField]="identite.naissance" />
899
+ * ```
900
+ *
901
+ * Une date qu'une personne CONNAIT (sa naissance, la delivrance d'un document) se tape plus vite et
902
+ * plus surement qu'elle ne se cherche dans un calendrier, qui fait remonter decennie par decennie.
903
+ * Le modele est une date ISO `AAAA-MM-JJ` ou `null`, jamais un objet `Date`.
904
+ *
905
+ * - Trois cases etiquetees (« Jour », « Mois », « Annee ») dans un `fieldset` dont la legende est
906
+ * la question ; l'exemple est dit AVANT la saisie, dans l'ordre de la langue (« 27 3 1990 »).
907
+ * - `naissance` pose `autocomplete="bday-day"`, `bday-month`, `bday-year` : le navigateur remplit.
908
+ * - Rien n'est devine : l'annee s'ecrit en quatre chiffres ; une date incomplete dit la partie qui
909
+ * manque, une date qui n'existe pas (31/02) le dit, et le modele reste `null` dans les deux cas.
910
+ */
911
+ export declare class StMemorableDate extends ContratDeChamp implements FormValueControl<DateIso | null> {
912
+ private readonly libellesDuTronc;
913
+ private readonly locale;
914
+ private readonly catalogue;
915
+ protected readonly libelles: import("@angular/core").Signal<import("@starterlib/ui/formulaires").LibellesFormulaires>;
916
+ readonly value: import("@angular/core").ModelSignal<string | null>;
917
+ /** Une date de naissance : le navigateur peut remplir les trois cases. */
918
+ readonly naissance: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
919
+ private readonly cases;
920
+ protected readonly identifiant: string;
921
+ /** Quitte au moins une fois (le groupe entier) : une date incomplete se dit alors. */
922
+ protected readonly quitte: import("@angular/core").WritableSignal<boolean>;
923
+ protected readonly saisie: import("@angular/forms/signals").TransformedValueSignal<string>;
924
+ protected readonly parties: import("@angular/core").Signal<Record<PartieDeDate, string>>;
925
+ /** Les trois cases, dans l'ordre de la langue. */
926
+ protected readonly ordre: import("@angular/core").Signal<{
927
+ partie: PartieDeDate;
928
+ libelle: string;
929
+ longueur: number;
930
+ autocomplete: string;
931
+ }[]>;
932
+ /** La saisie est-elle illisible ou incomplete ? Le modele vaut alors `null`. */
933
+ readonly lectureInvalide: import("@angular/core").Signal<boolean>;
934
+ protected readonly aideLue: import("@angular/core").Signal<string>;
935
+ protected readonly erreursAffichees: import("@angular/core").Signal<readonly import("@starterlib/ui/saisie").ErreurAffichable[]>;
936
+ protected readonly enErreur: import("@angular/core").Signal<boolean>;
937
+ /** Signal Forms amene le focus sur la premiere case, dans l'ordre de la langue. */
938
+ focus(options?: FocusOptions): void;
939
+ protected surSaisie(partie: PartieDeDate, evenement: Event): void;
940
+ /** Le groupe n'est quitte que si le focus en SORT : passer du jour au mois n'est pas partir. */
941
+ protected aLaSortie(evenement: FocusEvent): void;
942
+ private libelleDe;
943
+ private exemple;
944
+ private lire;
945
+ private formater;
946
+ static ɵfac: i0.ɵɵFactoryDeclaration<StMemorableDate, never>;
947
+ static ɵcmp: i0.ɵɵComponentDeclaration<StMemorableDate, "st-memorable-date", never, {
948
+ "value": {
949
+ "alias": "value";
950
+ "required": false;
951
+ "isSignal": true;
952
+ };
953
+ "naissance": {
954
+ "alias": "naissance";
955
+ "required": false;
956
+ "isSignal": true;
957
+ };
958
+ }, {
959
+ "value": "valueChange";
960
+ }, never, never, true, never>;
961
+ }
831
962
  /**
832
963
  * Heure d'horloge : un champ texte, au format de la langue courante.
833
964
  *
@@ -81,6 +81,8 @@ export declare function messageDErreur(erreur: ErreurAffichable, libelles: Libel
81
81
  export declare function libelleDePartie(libelles: LibellesUi, partie: string, nature?: string): string;
82
82
  /** Le motif a taper dans la langue courante : `jj/mm/aaaa`. */
83
83
  export declare function motifLocal(libelles: LibellesUi, locale: string): string;
84
+ /** Le motif d'un mois dans la langue courante : `mm/aaaa` (`st-month-field`, lot 267). */
85
+ export declare function motifDuMoisLocal(libelles: LibellesUi, locale: string): string;
84
86
  /** Le motif d'une heure dans la langue courante : `hh:mm`, `hh:mm AM/PM`. */
85
87
  export declare function motifDHeureLocal(libelles: LibellesUi, locale: string, secondes?: boolean): string;
86
88
  /**
@@ -0,0 +1,103 @@
1
+ import * as i0 from "@angular/core";
2
+ import { TemplateRef, TrackByFunction } from "@angular/core";
3
+ /** Contexte du gabarit d'une ligne : l'element, et son rang dans la liste ENTIERE (a partir de 0). */
4
+ export interface ContexteDeLigneVirtuelle<T> {
5
+ readonly $implicit: T;
6
+ readonly index: number;
7
+ }
8
+ /**
9
+ * Le gabarit d'une ligne de `st-virtual-list`.
10
+ *
11
+ * ```html
12
+ * <ng-template [stVirtualItem]="operations()" let-operation let-rang="index">
13
+ * {{ operation.libelle }}
14
+ * </ng-template>
15
+ * ```
16
+ *
17
+ * Les elements lies a la directive ne servent qu'a TYPER le gabarit (comme `ngForOf`) : sans eux,
18
+ * l'element de la ligne est `unknown` pour le compilateur des gabarits stricts.
19
+ */
20
+ export declare class StVirtualItem<T = unknown> {
21
+ readonly typage: import("@angular/core").InputSignal<"" | readonly T[]>;
22
+ readonly gabarit: TemplateRef<ContexteDeLigneVirtuelle<T>>;
23
+ static ngTemplateContextGuard<T>(_directive: StVirtualItem<T>, _contexte: unknown): _contexte is ContexteDeLigneVirtuelle<T>;
24
+ static ɵfac: i0.ɵɵFactoryDeclaration<StVirtualItem<any>, never>;
25
+ static ɵdir: i0.ɵɵDirectiveDeclaration<StVirtualItem<any>, "ng-template[stVirtualItem]", never, {
26
+ "typage": {
27
+ "alias": "stVirtualItem";
28
+ "required": false;
29
+ "isSignal": true;
30
+ };
31
+ }, {}, never, never, true, never>;
32
+ }
33
+ /**
34
+ * Liste virtuelle : plusieurs milliers de lignes AFFICHEES ensemble, dont seules celles de la fenetre
35
+ * (et une marge) existent dans le document. Bonus du lot 267, sous condition ecrite (`README.md`) :
36
+ * une liste paginee par curseur n'en a pas besoin.
37
+ *
38
+ * ```html
39
+ * <st-virtual-list libelle="Operations de septembre" [elements]="operations()" [suivi]="parId">
40
+ * <ng-template [stVirtualItem]="operations()" let-operation>{{ operation.libelle }}</ng-template>
41
+ * </st-virtual-list>
42
+ * ```
43
+ *
44
+ * - **Le rang et le total sont dits** : chaque ligne porte `aria-posinset` et `aria-setsize`, un
45
+ * lecteur d'ecran annonce « 37 sur 10 000 » alors que trente lignes seulement existent.
46
+ * - **La fenetre est atteignable au clavier** : elle prend le focus (`tabindex="0"`), porte le nom
47
+ * de la liste, et defile aux fleches, a Page suivante et a Fin, sans contenu focalisable requis.
48
+ * - **Hauteur de ligne FIXE** (`hauteurDeLigne`, en pixels CSS) : c'est elle qui rend le calcul de
49
+ * la fenetre exact sans mesurer le document a chaque defilement. Une ligne plus haute que prevu est
50
+ * coupee ; le texte qui peut s'allonger (agrandi a 200 %) se tronque dans la ligne, et le gabarit
51
+ * en dit l'integralite par son nom accessible ou une page de detail.
52
+ * - **Hauteur de la fenetre** : `hauteurDeFenetre`, une longueur CSS (`24rem` par defaut), posee sur
53
+ * l'hote en variable interne (`--st-virtual-list-hauteur`, sans promesse : seule l'entree en est une).
54
+ */
55
+ export declare class StVirtualList<T = unknown> {
56
+ readonly elements: import("@angular/core").InputSignal<readonly T[]>;
57
+ /** Le nom de la liste, dit a l'entree dans la fenetre : « Operations de septembre ». */
58
+ readonly libelle: import("@angular/core").InputSignal<string>;
59
+ readonly hauteurDeLigne: import("@angular/core").InputSignalWithTransform<number, unknown>;
60
+ /** Hauteur de la fenetre, longueur CSS : `24rem` par defaut, `12rem` pour un apercu. */
61
+ readonly hauteurDeFenetre: import("@angular/core").InputSignal<string>;
62
+ /** Le suivi des lignes ; par defaut le rang (une liste qui ne bouge pas). */
63
+ readonly suivi: import("@angular/core").InputSignal<TrackByFunction<T>>;
64
+ protected readonly ligne: import("@angular/core").Signal<StVirtualItem<T>>;
65
+ private readonly fenetre;
66
+ /** Hauteur valide : une hauteur nulle ou negative ferait rendre la liste entiere. */
67
+ protected readonly hauteur: import("@angular/core").Signal<number>;
68
+ /** Marges de rendu : cinq lignes au moins de part et d'autre, dix au plus. */
69
+ protected readonly margeMin: import("@angular/core").Signal<number>;
70
+ protected readonly margeMax: import("@angular/core").Signal<number>;
71
+ /** Amene une ligne dans la fenetre (« aller a la ligne 4 200 »). */
72
+ allerA(rang: number, comportement?: ScrollBehavior): void;
73
+ static ɵfac: i0.ɵɵFactoryDeclaration<StVirtualList<any>, never>;
74
+ static ɵcmp: i0.ɵɵComponentDeclaration<StVirtualList<any>, "st-virtual-list", never, {
75
+ "elements": {
76
+ "alias": "elements";
77
+ "required": true;
78
+ "isSignal": true;
79
+ };
80
+ "libelle": {
81
+ "alias": "libelle";
82
+ "required": true;
83
+ "isSignal": true;
84
+ };
85
+ "hauteurDeLigne": {
86
+ "alias": "hauteurDeLigne";
87
+ "required": false;
88
+ "isSignal": true;
89
+ };
90
+ "hauteurDeFenetre": {
91
+ "alias": "hauteurDeFenetre";
92
+ "required": false;
93
+ "isSignal": true;
94
+ };
95
+ "suivi": {
96
+ "alias": "suivi";
97
+ "required": false;
98
+ "isSignal": true;
99
+ };
100
+ }, {}, ["ligne"], never, true, never>;
101
+ }
102
+
103
+ export {};
@@ -0,0 +1,46 @@
1
+ # `@starterlib/ui/virtuel` — `st-virtual-list`, bonus sous condition ecrite (lot 267)
2
+
3
+ ## Condition d'entree
4
+
5
+ Une liste REELLE au-dela de la pagination par curseur : plusieurs milliers de lignes AFFICHEES
6
+ ensemble par un ecran nomme (un journal d'operations a parcourir d'un trait, une liste de
7
+ rapprochement). Construite le 2026-10-01 sur decision du proprietaire (vague 4) ; la condition
8
+ reste la regle de son emploi : une liste paginee par curseur (`st-list`, `st-data-table` de
9
+ `@starterlib/ui/listes`) n'en a pas besoin, et le backend ne rend pas de page plus longue que sa
10
+ borne.
11
+
12
+ Ce qu'elle n'est pas : une grille editable (`st-data-grid`) ni une liste de transfert
13
+ (`st-transfer-list`), qui restent conditionnees ; ni un tableau (pas de colonnes ni de tri).
14
+
15
+ ## API
16
+
17
+ ```html
18
+ <st-virtual-list libelle="Operations de septembre" [elements]="operations()" [suivi]="parId">
19
+ <ng-template [stVirtualItem]="operations()" let-operation let-rang="index">
20
+ {{ operation.libelle }}
21
+ </ng-template>
22
+ </st-virtual-list>
23
+ ```
24
+
25
+ - `elements` : le tableau entier ; seules les lignes de la fenetre (et cinq a dix de marge) existent
26
+ dans le document.
27
+ - `libelle` : le nom de la fenetre (`role="region"`, focalisable : elle defile aux fleches, a Page
28
+ suivante et a Fin, sans contenu focalisable requis).
29
+ - Chaque ligne porte `aria-posinset` et `aria-setsize` : « 37 sur 10 000 ».
30
+ - `hauteurDeLigne` : en pixels CSS, FIXE (44 par defaut) ; une valeur nulle ou negative est refusee
31
+ (signalee en developpement, 44 retenus) : elle ferait rendre la liste entiere.
32
+ - `suivi` : la fonction de suivi des lignes (le rang par defaut).
33
+ - `allerA(rang)` : amene une ligne dans la fenetre.
34
+ - Hauteur de la fenetre : l'entree `hauteurDeFenetre`, une longueur CSS (`24rem` par defaut).
35
+
36
+ ## Gardes et poids
37
+
38
+ - Point d'entree a lui : il traine `@angular/cdk/scrolling` et RIEN d'autre (`pnpm paquets:check`,
39
+ au sous-chemin exact) ; il n'importe aucun autre point d'entree du socle, et aucun ne l'importe.
40
+ Partage separement en federation (`partageDuSocle`, vingt-huit specificateurs en 0.10.14).
41
+ - Poids (gzip 9) : `starterlib-ui-virtuel.mjs`, 2 685 octets ; le pair `@angular/cdk/scrolling`
42
+ (22.2.0), 9 002 octets avant elagage, qu'une page paie a son premier emploi. Le tronc n'en recoit
43
+ rien.
44
+ - Essais : `st-virtual-list.spec.ts` (fenetre seule rendue sur 10 000, rang et total, fenetre
45
+ nommee et focalisable, hauteur fixe et hauteur invalide refusee, total suivi, axe) ; banc en vrai
46
+ navigateur `banc/virtuel.navigateur.spec.ts` (defilement jusqu'a la derniere ligne).