@hgdc/ui 0.66.1 → 0.66.2
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.
|
@@ -38,6 +38,7 @@ export function DestructiveDialog({ open, onOpenChange, title, question, consequ
|
|
|
38
38
|
const view = shown.current;
|
|
39
39
|
const [typed, setTyped] = useState("");
|
|
40
40
|
const [pending, setPending] = useState(false);
|
|
41
|
+
const confirmButton = useRef(null);
|
|
41
42
|
const ready = !pending && (view.name === undefined || matchesConfirmation(typed, view.name));
|
|
42
43
|
const [wasOpen, setWasOpen] = useState(open);
|
|
43
44
|
if (open !== wasOpen) {
|
|
@@ -55,6 +56,11 @@ export function DestructiveDialog({ open, onOpenChange, title, question, consequ
|
|
|
55
56
|
}
|
|
56
57
|
catch (error) {
|
|
57
58
|
toast.error(formatError(error));
|
|
59
|
+
// Le bouton se désactive pendant l'envoi et le focus part sur `body`, hors de la boîte restée ouverte.
|
|
60
|
+
requestAnimationFrame(() => {
|
|
61
|
+
if (document.activeElement === document.body)
|
|
62
|
+
confirmButton.current?.focus();
|
|
63
|
+
});
|
|
58
64
|
}
|
|
59
65
|
finally {
|
|
60
66
|
setPending(false);
|
|
@@ -69,5 +75,5 @@ export function DestructiveDialog({ open, onOpenChange, title, question, consequ
|
|
|
69
75
|
}, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: view.title }), _jsxs(DialogDescription, { render: _jsx("div", {}), className: "grid gap-1 text-[15px]", children: [_jsx("p", { children: view.question }), view.consequence ? _jsx("p", { children: view.consequence }) : null, _jsx("p", { className: "font-semibold text-destructive", children: "Impossible de revenir en arri\u00E8re." })] })] }), view.name !== undefined ? (_jsxs("form", { className: "grid gap-3", onSubmit: (event) => {
|
|
70
76
|
event.preventDefault();
|
|
71
77
|
void confirm();
|
|
72
|
-
}, children: [_jsxs("p", { className: "text-[15px] leading-7 text-muted-foreground", children: ["Tape ", _jsx(NameChip, { name: view.name }), " pour confirmer."] }), _jsx(Input, { "aria-label": `Tape ${view.name} pour confirmer`, autoFocus: true, autoComplete: "off", autoCapitalize: "off", spellCheck: false, value: typed, onChange: (event) => setTyped(event.target.value), placeholder: view.name, "aria-invalid": typed.length > 0 && !view.name.startsWith(typed) ? true : undefined })] })) : null, _jsxs(DialogFooter, { className: "sm:justify-start", children: [_jsxs(Button, { variant: "destructive", isLoading: pending, disabled: !ready, onClick: () => void confirm(), children: [view.confirmLabel ?? view.title, _jsx(Kbd, { "aria-hidden": true, className: "ml-1 hidden bg-current/10 text-current lg:inline-flex", children: mod }), _jsx(Kbd, { "aria-hidden": true, className: "hidden bg-current/10 text-current lg:inline-flex", children: "\u21B5" })] }), _jsxs(Button, { variant: "outline", disabled: pending, onClick: () => onOpenChange(false), children: ["Annuler", _jsx(Kbd, { "aria-hidden": true, className: "ml-1 hidden lg:inline-flex", children: "\u00C9chap" })] })] })] }) }));
|
|
78
|
+
}, children: [_jsxs("p", { className: "text-[15px] leading-7 text-muted-foreground", children: ["Tape ", _jsx(NameChip, { name: view.name }), " pour confirmer."] }), _jsx(Input, { "aria-label": `Tape ${view.name} pour confirmer`, autoFocus: true, autoComplete: "off", autoCapitalize: "off", spellCheck: false, value: typed, onChange: (event) => setTyped(event.target.value), placeholder: view.name, "aria-invalid": typed.length > 0 && !view.name.startsWith(typed) ? true : undefined })] })) : null, _jsxs(DialogFooter, { className: "sm:justify-start", children: [_jsxs(Button, { ref: confirmButton, variant: "destructive", isLoading: pending, disabled: !ready, onClick: () => void confirm(), children: [view.confirmLabel ?? view.title, _jsx(Kbd, { "aria-hidden": true, className: "ml-1 hidden bg-current/10 text-current lg:inline-flex", children: mod }), _jsx(Kbd, { "aria-hidden": true, className: "hidden bg-current/10 text-current lg:inline-flex", children: "\u21B5" })] }), _jsxs(Button, { variant: "outline", disabled: pending, onClick: () => onOpenChange(false), children: ["Annuler", _jsx(Kbd, { "aria-hidden": true, className: "ml-1 hidden lg:inline-flex", children: "\u00C9chap" })] })] })] }) }));
|
|
73
79
|
}
|
package/dist/settings-section.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useId } from "react";
|
|
2
|
+
import { useId, useRef } from "react";
|
|
3
3
|
import { Button } from "./button.js";
|
|
4
4
|
import { cn } from "./cn.js";
|
|
5
5
|
/**
|
|
@@ -26,8 +26,9 @@ import { cn } from "./cn.js";
|
|
|
26
26
|
export function SettingsSection({ title, description, children, footer, footerNote, tone = "default", onSave, dirty = true, saving = false, saveLabel = "Enregistrer", className, }) {
|
|
27
27
|
const titleId = useId();
|
|
28
28
|
const descriptionId = useId();
|
|
29
|
+
const heading = useRef(null);
|
|
29
30
|
const hasFooter = Boolean(footer || footerNote || onSave);
|
|
30
|
-
const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4 p-5 sm:p-6", children: [_jsxs("div", { children: [_jsx("h2", { id: titleId, className: "text-base font-semibold text-balance", children: title }), description ? (_jsx("p", { id: descriptionId, className: "mt-1 max-w-prose text-sm text-pretty text-muted-foreground", children: description })) : null] }), children] }), hasFooter ? (_jsxs("div", { "data-slot": "settings-section-footer", className: "flex flex-wrap items-center justify-end gap-3 border-t border-border bg-muted/40 px-5 py-3 sm:px-6", children: [footerNote ? _jsx("p", { className: "mr-auto text-sm text-muted-foreground", children: footerNote }) : null, footer, onSave ? (_jsx(Button, { type: "submit", size: "sm", disabled: !dirty, isLoading: saving, children: saveLabel })) : null] })) : null] }));
|
|
31
|
+
const body = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4 p-5 sm:p-6", children: [_jsxs("div", { children: [_jsx("h2", { ref: heading, id: titleId, tabIndex: -1, className: "text-base font-semibold text-balance outline-none", children: title }), description ? (_jsx("p", { id: descriptionId, className: "mt-1 max-w-prose text-sm text-pretty text-muted-foreground", children: description })) : null] }), children] }), hasFooter ? (_jsxs("div", { "data-slot": "settings-section-footer", className: "flex flex-wrap items-center justify-end gap-3 border-t border-border bg-muted/40 px-5 py-3 sm:px-6", children: [footerNote ? _jsx("p", { className: "mr-auto text-sm text-muted-foreground", children: footerNote }) : null, footer, onSave ? (_jsx(Button, { type: "submit", size: "sm", disabled: !dirty, isLoading: saving, children: saveLabel })) : null] })) : null] }));
|
|
31
32
|
const shared = {
|
|
32
33
|
"data-slot": "settings-section",
|
|
33
34
|
"data-tone": tone,
|
|
@@ -41,7 +42,14 @@ export function SettingsSection({ title, description, children, footer, footerNo
|
|
|
41
42
|
event.preventDefault();
|
|
42
43
|
if (!dirty || saving)
|
|
43
44
|
return;
|
|
44
|
-
|
|
45
|
+
// Le bouton se désactive pendant l'envoi et le focus part sur `body` : il revient au titre du bloc.
|
|
46
|
+
// Un focus resté dans un champ (Entrée) n'y est pas déplacé.
|
|
47
|
+
void Promise.resolve(onSave()).finally(() => {
|
|
48
|
+
requestAnimationFrame(() => {
|
|
49
|
+
if (document.activeElement === document.body)
|
|
50
|
+
heading.current?.focus();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
45
53
|
};
|
|
46
54
|
// Un `<form>` nommé par son titre a le rôle « form » : il apparaît dans les repères du lecteur d'écran.
|
|
47
55
|
return (_jsx("form", { ...shared, onSubmit: submit, children: body }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hgdc/ui",
|
|
3
|
-
"version": "0.66.
|
|
3
|
+
"version": "0.66.2",
|
|
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",
|