@hgdc/ui 0.48.0 → 0.49.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 CHANGED
@@ -159,14 +159,15 @@ Filtres, tri et page vivent dans l'URL du produit : les composants sont contrôl
159
159
 
160
160
  ## Formulaires et réglages
161
161
 
162
- | Import | Exemple |
163
- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
164
- | `settings-section` | `<SettingsSection title="Nom de l'espace" description="…" dirty={changed} saving={saving} onSave={save}>…</SettingsSection>` : un Enregistrer par bloc |
165
- | `date-range-picker` | `<DateRangePicker value={{ from: "2026-10-01", to: "2026-10-15" }} onChange={(range, preset) => …} />` : raccourcis et période libre |
166
- | `date-time-field` | `<DateTimeField value={sendAt} onChange={setSendAt} min={Date.now()} />` : millisecondes, heure du navigateur |
167
- | `color-field` | `<ColorField value="#7c3aed" onChange={…} />` : pastille, code hexadécimal, contraste avec le texte blanc et son verdict |
168
- | `money-field` | `<MoneyField value={centimes} onChange={…} />` : CHF en centimes, « 1’250.50 », accepte « 12.– » ; `formatChf`, `parseMoney` |
169
- | `address-fields` | `<AddressFields value={address} onChange={…} errors={validatePostalAddress(address)} />` : rue, numéro, NPA, localité, pays (facture QR) |
162
+ | Import | Exemple |
163
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
164
+ | `settings-section` | `<SettingsSection title="Nom de l'espace" description="…" dirty={changed} saving={saving} onSave={save}>…</SettingsSection>` : un Enregistrer par bloc |
165
+ | `date-range-picker` | `<DateRangePicker value={{ from: "2026-10-01", to: "2026-10-15" }} onChange={(range, preset) => …} />` : raccourcis et période libre |
166
+ | `date-time-field` | `<DateTimeField value={sendAt} onChange={setSendAt} min={Date.now()} />` : millisecondes, heure du navigateur |
167
+ | `color-field` | `<ColorField value="#7c3aed" onChange={…} />` : pastille, code hexadécimal, contraste avec le texte blanc et son verdict |
168
+ | `money-field` | `<MoneyField value={centimes} onChange={…} />` : CHF en centimes, « 1’250.50 », accepte « 12.– » ; `formatChf`, `parseMoney` |
169
+ | `address-fields` | `<AddressFields value={address} onChange={…} errors={validatePostalAddress(address)} />` : rue, numéro, NPA, localité, pays (facture QR) |
170
+ | `floating-field` | `<FloatingField label="Adresse e-mail" type="email" error={message} surface="card" />` : libellé dans le champ, monté sur le filet au focus (connexion) |
170
171
 
171
172
  `access-request` : `<AccessRequest endpoint="https://<deploiement>.convex.site/f/<id>" product="Mail" signInRender={<Link to="/sign-in" />} />`, la page « Demander un accès » d'un produit (les comptes HGDC s'ouvrent sur invitation). Poste en JSON vers un formulaire HGDC Mail Forms : prénom, nom, entreprise, e-mail professionnel, message facultatif, `produit` caché, piège `_gotcha`. États envoi, réussite, erreur, champ refusé ; `submitAccessRequest` s'appelle seul.
172
173
 
@@ -0,0 +1,24 @@
1
+ import { type ReactNode } from "react";
2
+ /**
3
+ * Champ à libellé flottant, pour les écrans de connexion : le libellé est dans le champ vide et
4
+ * monte sur le filet dès que le champ a le focus ou une valeur (saisie, autoremplissage).
5
+ *
6
+ * Tout en CSS : `placeholder=" "` rend `:placeholder-shown` vrai tant que le champ est vide, le
7
+ * libellé (`peer-…`) suit sans JavaScript, dès le rendu serveur. Le libellé posé sur le filet
8
+ * reprend le fond de la surface où le champ est posé (`surface`, fond de page par défaut). Texte à
9
+ * 16 px : sous 16 px, Safari zoome au focus.
10
+ *
11
+ * L'erreur et la description sont citées par `aria-describedby` ; l'erreur garde une icône, pour ne
12
+ * pas reposer sur la seule couleur (WCAG 1.4.1).
13
+ */
14
+ declare function FloatingField({ label, error, description, surface, className, id, ...props }: Omit<React.ComponentProps<"input">, "placeholder"> & {
15
+ /** Libellé visible, aussi nom accessible du champ. */
16
+ label: string;
17
+ /** Message d'erreur sous le champ ; le champ passe en `aria-invalid`. */
18
+ error?: ReactNode;
19
+ /** Aide sous le champ, quand il n'y a pas d'erreur. */
20
+ description?: ReactNode;
21
+ /** Fond sous le champ : le libellé posé sur le filet le reprend. */
22
+ surface?: "background" | "card";
23
+ }): React.JSX.Element;
24
+ export { FloatingField };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import AlertCircleIcon from "@hugeicons/core-free-icons/AlertCircleIcon";
4
+ import { HugeiconsIcon } from "@hugeicons/react";
5
+ import { useId } from "react";
6
+ import { cn } from "./cn.js";
7
+ /**
8
+ * Champ à libellé flottant, pour les écrans de connexion : le libellé est dans le champ vide et
9
+ * monte sur le filet dès que le champ a le focus ou une valeur (saisie, autoremplissage).
10
+ *
11
+ * Tout en CSS : `placeholder=" "` rend `:placeholder-shown` vrai tant que le champ est vide, le
12
+ * libellé (`peer-…`) suit sans JavaScript, dès le rendu serveur. Le libellé posé sur le filet
13
+ * reprend le fond de la surface où le champ est posé (`surface`, fond de page par défaut). Texte à
14
+ * 16 px : sous 16 px, Safari zoome au focus.
15
+ *
16
+ * L'erreur et la description sont citées par `aria-describedby` ; l'erreur garde une icône, pour ne
17
+ * pas reposer sur la seule couleur (WCAG 1.4.1).
18
+ */
19
+ function FloatingField({ label, error, description, surface = "background", className, id, ...props }) {
20
+ const generated = useId();
21
+ const inputId = id ?? generated;
22
+ const errorId = `${inputId}-error`;
23
+ const descriptionId = `${inputId}-description`;
24
+ const describedBy = [error ? errorId : null, !error && description ? descriptionId : null, props["aria-describedby"]]
25
+ .filter(Boolean)
26
+ .join(" ");
27
+ return (_jsxs("div", { "data-slot": "floating-field", className: cn("grid gap-1.5", className), children: [_jsxs("div", { className: "relative", children: [_jsx("input", { ...props, id: inputId, placeholder: " ", "aria-invalid": error ? true : props["aria-invalid"], "aria-describedby": describedBy || undefined, className: "peer touch-field h-14 w-full min-w-0 rounded-md bg-transparent px-3.5 text-base text-foreground ring-1 ring-control-off transition-[box-shadow] duration-(--duration-fast) outline-none ring-inset focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-2 aria-invalid:ring-destructive" }), _jsx("label", { htmlFor: inputId, className: cn("pointer-events-none absolute top-1/2 left-2.5 max-w-[calc(100%-1.25rem)] -translate-y-1/2 truncate px-1 text-base text-muted-foreground transition-[top,font-size,color] duration-(--duration-fast) ease-(--ease-out) peer-focus-visible:top-0 peer-focus-visible:text-xs peer-focus-visible:text-accent peer-aria-invalid:text-destructive peer-[:not(:placeholder-shown)]:top-0 peer-[:not(:placeholder-shown)]:text-xs motion-reduce:transition-none", surface === "card" ? "bg-card" : "bg-background"), children: label })] }), error ? (_jsxs("p", { id: errorId, "data-slot": "floating-field-error", className: "flex gap-2 text-sm text-destructive", children: [_jsx(HugeiconsIcon, { icon: AlertCircleIcon, "aria-hidden": true, className: "mt-0.5 size-4 shrink-0" }), _jsx("span", { children: error })] })) : description ? (_jsx("p", { id: descriptionId, "data-slot": "floating-field-description", className: "text-sm text-muted-foreground", children: description })) : null] }));
28
+ }
29
+ export { FloatingField };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hgdc/ui",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Interface de la H Collection : jetons de la DA, thèmes, polices et composants.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/hugogebel-boop/hgdc-kit/tree/main/packages/ui#readme",