@godxjp/ui 19.6.0 → 20.0.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/dist/components/data-display/card.d.ts +2 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.js +1 -1
- package/dist/components/data-display/popover.d.ts +58 -5
- package/dist/components/data-display/popover.js +206 -27
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- package/dist/components/data-entry/calendar.d.ts +1 -1
- package/dist/components/data-entry/calendar.js +67 -32
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/field.js +0 -1
- package/dist/components/data-entry/form.js +1 -1
- package/dist/components/data-entry/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +16 -6
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +29 -12
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +272 -194
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.js +2 -2
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +52 -8
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +19 -2
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +5 -3
- package/dist/components/layout/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.js +1 -2
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +199 -18
- package/dist/components/navigation/dropdown-menu.js +344 -117
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- package/dist/components/navigation/tabs.d.ts +47 -6
- package/dist/components/navigation/tabs.js +152 -90
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/label.js +0 -1
- package/dist/components/ui/segmented.d.ts +3 -3
- package/dist/components/ui/separator.d.ts +8 -2
- package/dist/components/ui/separator.js +17 -9
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +1 -1
- package/dist/lib/control-styles.js +1 -1
- package/dist/lib/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/data-display.prop.d.ts +17 -0
- package/dist/props/components/data-entry.prop.d.ts +32 -4
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +118 -5
- package/dist/props/components/navigation.prop.d.ts +1 -1
- package/dist/props/registry.d.ts +22 -2
- package/dist/props/registry.js +33 -2
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- package/dist/styles/control.css +142 -1
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +72 -0
- package/dist/styles/focus-ring.css +4 -0
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +106 -0
- package/dist/styles/shell-layout.css +140 -8
- package/dist/styles/text-layout.css +5 -4
- package/dist/tokens/base.css +1 -1
- package/dist/tokens/components/data-display.css +11 -0
- package/dist/tokens/components/shell.css +3 -1
- package/dist/tokens/foundation.css +2 -0
- package/docs/COMPONENTS.md +9 -3
- package/docs/DESIGN-AUTHORITY.md +96 -71
- package/docs/FRAME-COVERAGE-REPORT.md +5 -3
- package/docs/README.md +14 -14
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- package/docs/data-display/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- package/docs/data-entry/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +2 -2
- package/docs/feedback/sheet.tsx +1 -1
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- package/docs/navigation/app-setting-picker.tsx +26 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -0
- package/package.json +11 -8
- package/scripts/_agent-setup.mjs +165 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +62 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +115 -14
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
|
@@ -1,83 +1,160 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import { chain, mergeRefs } from "@react-aria/utils";
|
|
5
|
+
import { Dialog as RacDialog, Modal, ModalOverlay } from "react-aria-components";
|
|
6
6
|
import { X } from "lucide-react";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
8
|
+
import { Slot } from "../../lib/slot.js";
|
|
8
9
|
import { overlayHeaderToneClass } from "./overlay-header-tone.js";
|
|
10
|
+
import { useOverlayCloseFocus } from "./overlay-close-focus.js";
|
|
9
11
|
import { buttonVariants } from "../general/button.js";
|
|
10
12
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
11
13
|
import { Button } from "../general/button.js";
|
|
12
14
|
import { Input } from "../data-entry/input.js";
|
|
13
15
|
import { Label } from "../data-entry/label.js";
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
function useOverlayOpenState({ open, defaultOpen, onOpenChange }) {
|
|
17
|
+
const [uncontrolled, setUncontrolled] = React.useState(defaultOpen ?? false);
|
|
18
|
+
const isOpen = open ?? uncontrolled;
|
|
19
|
+
const setOpen = React.useCallback(
|
|
20
|
+
(next) => {
|
|
21
|
+
if (next === isOpen) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (open === void 0) {
|
|
25
|
+
setUncontrolled(next);
|
|
26
|
+
}
|
|
27
|
+
onOpenChange?.(next);
|
|
28
|
+
},
|
|
29
|
+
[isOpen, open, onOpenChange]
|
|
30
|
+
);
|
|
31
|
+
return React.useMemo(() => ({ isOpen, setOpen }), [isOpen, setOpen]);
|
|
25
32
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
className: cn(
|
|
32
|
-
"ui-dialog-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
33
|
-
className
|
|
34
|
-
),
|
|
35
|
-
...props
|
|
33
|
+
const DialogOpenContext = React.createContext(null);
|
|
34
|
+
function useDialogOpenState(component) {
|
|
35
|
+
const state = React.useContext(DialogOpenContext);
|
|
36
|
+
if (!state) {
|
|
37
|
+
throw new Error(`\`${component}\` ph\u1EA3i n\u1EB1m trong \`Dialog\` ho\u1EB7c \`AlertDialogRoot\`.`);
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
const DialogLabelContext = React.createContext(null);
|
|
42
|
+
const OVERLAY_CLASS = "ui-dialog-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0";
|
|
43
|
+
const CONTENT_CLASS = "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 duration-200 outline-none";
|
|
44
|
+
function DialogShell({
|
|
45
|
+
role = "dialog",
|
|
46
|
+
className,
|
|
47
|
+
overlayClassName,
|
|
48
|
+
isDismissable,
|
|
49
|
+
onCloseAutoFocus,
|
|
50
|
+
children,
|
|
51
|
+
style,
|
|
52
|
+
ref,
|
|
53
|
+
...props
|
|
54
|
+
}) {
|
|
55
|
+
const state = useDialogOpenState("DialogContent");
|
|
56
|
+
const titleId = React.useId();
|
|
57
|
+
const descriptionId = React.useId();
|
|
58
|
+
const labels = React.useMemo(() => ({ titleId, descriptionId }), [titleId, descriptionId]);
|
|
59
|
+
const dataState = state.isOpen ? "open" : "closed";
|
|
60
|
+
useOverlayCloseFocus(state.isOpen, onCloseAutoFocus);
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
ModalOverlay,
|
|
63
|
+
{
|
|
64
|
+
isOpen: state.isOpen,
|
|
65
|
+
onOpenChange: state.setOpen,
|
|
66
|
+
isDismissable,
|
|
67
|
+
"data-slot": "dialog-overlay",
|
|
68
|
+
"data-state": dataState,
|
|
69
|
+
className: cn(OVERLAY_CLASS, overlayClassName),
|
|
70
|
+
children: /* @__PURE__ */ jsx(Modal, { className: "contents", children: /* @__PURE__ */ jsx(
|
|
71
|
+
RacDialog,
|
|
54
72
|
{
|
|
55
|
-
|
|
73
|
+
role,
|
|
74
|
+
"aria-labelledby": props["aria-labelledby"] ?? titleId,
|
|
75
|
+
"aria-describedby": props["aria-describedby"] ?? descriptionId,
|
|
56
76
|
"data-slot": "dialog-content",
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
showCloseButton ? /* @__PURE__ */ jsxs(
|
|
65
|
-
DialogPrimitive.Close,
|
|
66
|
-
{
|
|
67
|
-
"data-slot": "dialog-close",
|
|
68
|
-
className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
|
|
69
|
-
children: [
|
|
70
|
-
/* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" }),
|
|
71
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t("feedback.alert.dismiss") })
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
) : null
|
|
75
|
-
]
|
|
77
|
+
"data-state": dataState,
|
|
78
|
+
className: cn(CONTENT_CLASS, className),
|
|
79
|
+
render: (racProps) => {
|
|
80
|
+
const { "data-rac": _rac, ref: racRef, ...rest } = racProps;
|
|
81
|
+
return /* @__PURE__ */ jsx("section", { ...rest, ...props, style, ref: mergeRefs(ref, racRef) });
|
|
82
|
+
},
|
|
83
|
+
children: /* @__PURE__ */ jsx(DialogLabelContext.Provider, { value: labels, children })
|
|
76
84
|
}
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
) })
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
function DialogRoot({ children, modal: _modal, ...openProps }) {
|
|
90
|
+
const state = useOverlayOpenState(openProps);
|
|
91
|
+
return /* @__PURE__ */ jsx(DialogOpenContext.Provider, { value: state, children });
|
|
92
|
+
}
|
|
93
|
+
function DialogTrigger({ asChild, onClick, ...props }) {
|
|
94
|
+
const state = useDialogOpenState("DialogTrigger");
|
|
95
|
+
const Comp = asChild ? Slot : "button";
|
|
96
|
+
return /* @__PURE__ */ jsx(
|
|
97
|
+
Comp,
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
"data-slot": "dialog-trigger",
|
|
101
|
+
"aria-haspopup": "dialog",
|
|
102
|
+
"aria-expanded": state.isOpen,
|
|
103
|
+
"data-state": state.isOpen ? "open" : "closed",
|
|
104
|
+
...props,
|
|
105
|
+
onClick: chain(onClick, () => {
|
|
106
|
+
state.setOpen(!state.isOpen);
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function DialogPortal({ children }) {
|
|
112
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
113
|
+
}
|
|
114
|
+
function DialogClose({ asChild, onClick, ...props }) {
|
|
115
|
+
const state = useDialogOpenState("DialogClose");
|
|
116
|
+
const Comp = asChild ? Slot : "button";
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
Comp,
|
|
119
|
+
{
|
|
120
|
+
type: "button",
|
|
121
|
+
"data-slot": "dialog-close",
|
|
122
|
+
...props,
|
|
123
|
+
onClick: chain(onClick, () => {
|
|
124
|
+
state.setOpen(false);
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
function DialogOverlay(_props) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
DialogOverlay.displayName = "DialogOverlay";
|
|
133
|
+
function DialogContent({
|
|
134
|
+
className,
|
|
135
|
+
children,
|
|
136
|
+
showClose,
|
|
137
|
+
showCloseButton: showCloseButtonProp,
|
|
138
|
+
overlayClassName,
|
|
139
|
+
forceMount: _forceMount,
|
|
140
|
+
...props
|
|
141
|
+
}) {
|
|
142
|
+
const { t } = useTranslation();
|
|
143
|
+
const showCloseButton = showCloseButtonProp ?? showClose ?? true;
|
|
144
|
+
return /* @__PURE__ */ jsxs(DialogShell, { isDismissable: true, className, overlayClassName, ...props, children: [
|
|
145
|
+
children,
|
|
146
|
+
showCloseButton ? /* @__PURE__ */ jsxs(
|
|
147
|
+
DialogClose,
|
|
148
|
+
{
|
|
149
|
+
className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
|
|
150
|
+
children: [
|
|
151
|
+
/* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" }),
|
|
152
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t("feedback.alert.dismiss") })
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
) : null
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
81
158
|
DialogContent.displayName = "DialogContent";
|
|
82
159
|
const DialogHeader = ({
|
|
83
160
|
className,
|
|
@@ -116,17 +193,22 @@ const DialogFooter = ({ className, ...props }) => (
|
|
|
116
193
|
/* @__PURE__ */ jsx("div", { "data-slot": "dialog-footer", className, ...props })
|
|
117
194
|
);
|
|
118
195
|
DialogFooter.displayName = "DialogFooter";
|
|
119
|
-
const DialogTitle = React.forwardRef(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
196
|
+
const DialogTitle = React.forwardRef(
|
|
197
|
+
({ className, ...props }, ref) => {
|
|
198
|
+
const labels = React.useContext(DialogLabelContext);
|
|
199
|
+
const cls = cn(className);
|
|
200
|
+
return /* @__PURE__ */ jsx("h2", { ref, id: labels?.titleId, "data-slot": "dialog-title", className: cls, ...props });
|
|
201
|
+
}
|
|
202
|
+
);
|
|
123
203
|
DialogTitle.displayName = "DialogTitle";
|
|
124
204
|
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => {
|
|
205
|
+
const labels = React.useContext(DialogLabelContext);
|
|
125
206
|
const cls = cn(className);
|
|
126
207
|
return /* @__PURE__ */ jsx(
|
|
127
|
-
|
|
208
|
+
"p",
|
|
128
209
|
{
|
|
129
210
|
ref,
|
|
211
|
+
id: labels?.descriptionId,
|
|
130
212
|
"data-slot": "dialog-description",
|
|
131
213
|
className: cls,
|
|
132
214
|
...props
|
|
@@ -134,53 +216,62 @@ const DialogDescription = React.forwardRef(({ className, ...props }, ref) => {
|
|
|
134
216
|
);
|
|
135
217
|
});
|
|
136
218
|
DialogDescription.displayName = "DialogDescription";
|
|
137
|
-
function AlertDialogRoot(
|
|
138
|
-
|
|
219
|
+
function AlertDialogRoot({ children, ...openProps }) {
|
|
220
|
+
const state = useOverlayOpenState(openProps);
|
|
221
|
+
return /* @__PURE__ */ jsx(DialogOpenContext.Provider, { value: state, children });
|
|
139
222
|
}
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ref,
|
|
146
|
-
"data-slot": "dialog-overlay",
|
|
147
|
-
className: cn(
|
|
148
|
-
"ui-dialog-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
149
|
-
className
|
|
150
|
-
),
|
|
151
|
-
...props
|
|
152
|
-
}
|
|
153
|
-
));
|
|
154
|
-
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
155
|
-
const AlertDialogContent = React.forwardRef(({ className, children, showClose, showCloseButton: showCloseButtonProp, ...props }, ref) => {
|
|
156
|
-
const { t } = useTranslation();
|
|
157
|
-
const showCloseButton = showCloseButtonProp ?? showClose ?? false;
|
|
158
|
-
return /* @__PURE__ */ jsxs(
|
|
159
|
-
AlertDialogPrimitive.Content,
|
|
223
|
+
function AlertDialogTrigger({ asChild, onClick, ...props }) {
|
|
224
|
+
const state = useDialogOpenState("AlertDialogTrigger");
|
|
225
|
+
const Comp = asChild ? Slot : "button";
|
|
226
|
+
return /* @__PURE__ */ jsx(
|
|
227
|
+
Comp,
|
|
160
228
|
{
|
|
161
|
-
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
className
|
|
166
|
-
),
|
|
229
|
+
type: "button",
|
|
230
|
+
"aria-haspopup": "dialog",
|
|
231
|
+
"aria-expanded": state.isOpen,
|
|
232
|
+
"data-state": state.isOpen ? "open" : "closed",
|
|
167
233
|
...props,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"button",
|
|
172
|
-
{
|
|
173
|
-
type: "button",
|
|
174
|
-
"data-slot": "dialog-close",
|
|
175
|
-
className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
|
|
176
|
-
"aria-label": t("feedback.alert.dismiss"),
|
|
177
|
-
children: /* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" })
|
|
178
|
-
}
|
|
179
|
-
) }) : null
|
|
180
|
-
]
|
|
234
|
+
onClick: chain(onClick, () => {
|
|
235
|
+
state.setOpen(!state.isOpen);
|
|
236
|
+
})
|
|
181
237
|
}
|
|
182
238
|
);
|
|
183
|
-
}
|
|
239
|
+
}
|
|
240
|
+
function AlertDialogPortal({ children }) {
|
|
241
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
242
|
+
}
|
|
243
|
+
function AlertDialogOverlay(_props) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
AlertDialogOverlay.displayName = "AlertDialogOverlay";
|
|
247
|
+
function AlertDialogContent({
|
|
248
|
+
className,
|
|
249
|
+
children,
|
|
250
|
+
showClose,
|
|
251
|
+
showCloseButton: showCloseButtonProp,
|
|
252
|
+
forceMount: _forceMount,
|
|
253
|
+
...props
|
|
254
|
+
}) {
|
|
255
|
+
const { t } = useTranslation();
|
|
256
|
+
const state = useDialogOpenState("AlertDialogContent");
|
|
257
|
+
const showCloseButton = showCloseButtonProp ?? showClose ?? false;
|
|
258
|
+
return /* @__PURE__ */ jsxs(DialogShell, { role: "alertdialog", isDismissable: false, className, ...props, children: [
|
|
259
|
+
children,
|
|
260
|
+
showCloseButton ? /* @__PURE__ */ jsx(
|
|
261
|
+
"button",
|
|
262
|
+
{
|
|
263
|
+
type: "button",
|
|
264
|
+
"data-slot": "dialog-close",
|
|
265
|
+
className: "ui-focus-ring transition-opacity focus:outline-hidden disabled:pointer-events-none",
|
|
266
|
+
"aria-label": t("feedback.alert.dismiss"),
|
|
267
|
+
onClick: () => {
|
|
268
|
+
state.setOpen(false);
|
|
269
|
+
},
|
|
270
|
+
children: /* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" })
|
|
271
|
+
}
|
|
272
|
+
) : null
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
184
275
|
AlertDialogContent.displayName = "AlertDialogContent";
|
|
185
276
|
const AlertDialogHeader = ({
|
|
186
277
|
className,
|
|
@@ -214,36 +305,39 @@ const AlertDialogFooter = ({ className, ...props }) => (
|
|
|
214
305
|
/* @__PURE__ */ jsx("div", { "data-slot": "dialog-footer", className: cn(className), ...props })
|
|
215
306
|
);
|
|
216
307
|
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
217
|
-
const AlertDialogTitle =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
));
|
|
236
|
-
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
237
|
-
const DialogAction = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
|
|
308
|
+
const AlertDialogTitle = DialogTitle;
|
|
309
|
+
const AlertDialogDescription = DialogDescription;
|
|
310
|
+
function DialogAction({ asChild, className, onClick, ...props }) {
|
|
311
|
+
const state = useDialogOpenState("DialogAction");
|
|
312
|
+
const Comp = asChild ? Slot : "button";
|
|
313
|
+
return /* @__PURE__ */ jsx(
|
|
314
|
+
Comp,
|
|
315
|
+
{
|
|
316
|
+
type: "button",
|
|
317
|
+
className: cn(buttonVariants(), className),
|
|
318
|
+
...props,
|
|
319
|
+
onClick: chain(onClick, () => {
|
|
320
|
+
state.setOpen(false);
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
}
|
|
238
325
|
DialogAction.displayName = "DialogAction";
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
))
|
|
326
|
+
function DialogCancel({ asChild, className, onClick, ...props }) {
|
|
327
|
+
const state = useDialogOpenState("DialogCancel");
|
|
328
|
+
const Comp = asChild ? Slot : "button";
|
|
329
|
+
return /* @__PURE__ */ jsx(
|
|
330
|
+
Comp,
|
|
331
|
+
{
|
|
332
|
+
type: "button",
|
|
333
|
+
className: cn(buttonVariants({ variant: "outline" }), className),
|
|
334
|
+
...props,
|
|
335
|
+
onClick: chain(onClick, () => {
|
|
336
|
+
state.setOpen(false);
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
247
341
|
DialogCancel.displayName = "DialogCancel";
|
|
248
342
|
const AlertDialogAction = DialogAction;
|
|
249
343
|
const AlertDialogCancel = DialogCancel;
|
|
@@ -308,59 +402,43 @@ function AlertDialog({
|
|
|
308
402
|
if (!keepOpenOnConfirm) onOpenChange(false);
|
|
309
403
|
})();
|
|
310
404
|
};
|
|
311
|
-
return /* @__PURE__ */ jsx(
|
|
312
|
-
/* @__PURE__ */
|
|
313
|
-
|
|
314
|
-
{
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
349
|
-
/* @__PURE__ */ jsx(DialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", disabled: busy, children: resolvedCancel }) }),
|
|
350
|
-
/* @__PURE__ */ jsx(
|
|
351
|
-
Button,
|
|
352
|
-
{
|
|
353
|
-
variant: effectiveVariant === "destructive" ? "destructive" : "default",
|
|
354
|
-
onClick: handleConfirm,
|
|
355
|
-
disabled: busy || !phraseMatches,
|
|
356
|
-
"aria-describedby": stepUpFailed ? stepErrorId : void 0,
|
|
357
|
-
children: confirmLabelText
|
|
358
|
-
}
|
|
359
|
-
)
|
|
360
|
-
] })
|
|
361
|
-
]
|
|
362
|
-
}
|
|
363
|
-
)
|
|
405
|
+
return /* @__PURE__ */ jsx(AlertDialogRoot, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
406
|
+
/* @__PURE__ */ jsxs(DialogHeader, { tone: headerTone, children: [
|
|
407
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: title }),
|
|
408
|
+
description ? /* @__PURE__ */ jsx(AlertDialogDescription, { children: description }) : null
|
|
409
|
+
] }),
|
|
410
|
+
needsPhrase && /* @__PURE__ */ jsxs("div", { className: "ui-stack-xs", children: [
|
|
411
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: t("common.typeToConfirm", { phrase }) }),
|
|
412
|
+
/* @__PURE__ */ jsx(
|
|
413
|
+
Input,
|
|
414
|
+
{
|
|
415
|
+
id: inputId,
|
|
416
|
+
value: typed,
|
|
417
|
+
onChange: (e) => {
|
|
418
|
+
setTyped(e.target.value);
|
|
419
|
+
},
|
|
420
|
+
autoComplete: "off",
|
|
421
|
+
spellCheck: false,
|
|
422
|
+
placeholder: phrase,
|
|
423
|
+
"aria-required": "true",
|
|
424
|
+
disabled: busy
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
] }),
|
|
428
|
+
stepUpFailed && /* @__PURE__ */ jsx("p", { id: stepErrorId, role: "alert", className: "ui-dialog-step-up-error", children: t("feedback.alert.stepUpFailed") }),
|
|
429
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
430
|
+
/* @__PURE__ */ jsx(DialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", disabled: busy, children: resolvedCancel }) }),
|
|
431
|
+
/* @__PURE__ */ jsx(
|
|
432
|
+
Button,
|
|
433
|
+
{
|
|
434
|
+
variant: effectiveVariant === "destructive" ? "destructive" : "default",
|
|
435
|
+
onClick: handleConfirm,
|
|
436
|
+
disabled: busy || !phraseMatches,
|
|
437
|
+
"aria-describedby": stepUpFailed ? stepErrorId : void 0,
|
|
438
|
+
children: confirmLabelText
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
] })
|
|
364
442
|
] }) });
|
|
365
443
|
}
|
|
366
444
|
const Dialog = Object.assign(DialogRoot, {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Trả tiêu điểm về nơi nó đứng trước khi lớp phủ mở — ĐỒNG BỘ, ngay trong lượt đóng.
|
|
4
|
+
*
|
|
5
|
+
* Radix trả tiêu điểm lúc tháo thẻ và cho phép chặn qua `onCloseAutoFocus`.
|
|
6
|
+
* `FocusScope restoreFocus` của react-aria cũng trả, nhưng trong một `requestAnimationFrame` SAU khi
|
|
7
|
+
* tháo — nên trong đúng một khung hình, mọi thứ đọc `document.activeElement` ngay sau lệnh đóng
|
|
8
|
+
* (test bàn phím, và trình đọc màn hình đang theo tiêu điểm) đều thấy `<body>`.
|
|
9
|
+
*
|
|
10
|
+
* Hook này lấp đúng khung hình đó. rAF của react-aria chạy sau, thấy tiêu điểm đã rời `<body>` nên
|
|
11
|
+
* tự bỏ qua (`if (ownerDocument.activeElement === ownerDocument.body)`), nên không có hai lệnh focus
|
|
12
|
+
* tranh nhau. Nếu lớp phủ còn treo để chạy hoạt ảnh thoát, `ariaHideOutside` vẫn đang giữ `inert`
|
|
13
|
+
* quanh trigger và `focus()` ở đây thành vô hiệu — lúc ấy rAF kia mới là thứ trả tiêu điểm, y như
|
|
14
|
+
* khi không có hook này.
|
|
15
|
+
*
|
|
16
|
+
* Nút cần trả về được ghi lại NGAY TRONG LƯỢT DỰNG mở ra, không phải trong một effect: effect của
|
|
17
|
+
* con chạy trước effect của cha, nên đến lượt cha thì `FocusScope` của react-aria đã kéo tiêu điểm
|
|
18
|
+
* vào trong tấm nội dung rồi.
|
|
19
|
+
*
|
|
20
|
+
* `contentRef` là tuỳ chọn, và chỉ lớp phủ NON-MODAL mới cần truyền. Một lớp phủ modal (Dialog,
|
|
21
|
+
* Sheet) đóng thì tiêu điểm chắc chắn đang ở trong nó hoặc đã rơi xuống `<body>`, nên trả về luôn
|
|
22
|
+
* là đúng. Popover non-modal thì có thêm một đường đóng nữa: tiêu điểm ĐI RA — Tab sang phần tử kế
|
|
23
|
+
* tiếp, hay bấm thẳng vào một ô nhập khác — và chính việc đi ra ấy đóng panel. Ở đường đó, kéo tiêu
|
|
24
|
+
* điểm về trigger là CƯỚP tiêu điểm chứ không phải trả. Truyền `contentRef` để phân biệt: còn ở
|
|
25
|
+
* trong tấm (hoặc đã rơi xuống `<body>`) thì trả, đã sang một phần tử khác còn sống thì để yên.
|
|
26
|
+
*
|
|
27
|
+
* Sự kiện `closeAutoFocus` vẫn phát ở MỌI đường đóng, kể cả khi không trả tiêu điểm: consumer dùng
|
|
28
|
+
* nó để tự đưa tiêu điểm tới chỗ người dùng cần gõ tiếp, và việc đó không được phụ thuộc vào tiêu
|
|
29
|
+
* điểm đang tình cờ ở đâu.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useOverlayCloseFocus(isOpen: boolean, onCloseAutoFocus?: (event: Event) => void, contentRef?: React.RefObject<HTMLElement | null>): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useOverlayCloseFocus(isOpen, onCloseAutoFocus, contentRef) {
|
|
4
|
+
const nodeToRestore = React.useRef(null);
|
|
5
|
+
const renderedOpen = React.useRef(false);
|
|
6
|
+
const committedOpen = React.useRef(isOpen);
|
|
7
|
+
if (isOpen !== renderedOpen.current) {
|
|
8
|
+
renderedOpen.current = isOpen;
|
|
9
|
+
if (isOpen && typeof document !== "undefined") {
|
|
10
|
+
const active = document.activeElement;
|
|
11
|
+
nodeToRestore.current = active === document.body ? null : active;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
if (committedOpen.current && !isOpen) {
|
|
16
|
+
const event = new Event("closeAutoFocus", { cancelable: true });
|
|
17
|
+
onCloseAutoFocus?.(event);
|
|
18
|
+
const node = nodeToRestore.current;
|
|
19
|
+
nodeToRestore.current = null;
|
|
20
|
+
const active = document.activeElement;
|
|
21
|
+
const focusMovedAway = contentRef != null && active != null && active !== document.body && active.isConnected && contentRef.current?.contains(active) !== true;
|
|
22
|
+
if (!event.defaultPrevented && !focusMovedAway && node?.isConnected === true) {
|
|
23
|
+
node.focus();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
committedOpen.current = isOpen;
|
|
27
|
+
}, [isOpen, onCloseAutoFocus, contentRef]);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
useOverlayCloseFocus
|
|
31
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ToneProp } from "../../props/vocabulary/index.js";
|
|
2
2
|
/**
|
|
3
|
-
* Soft semantic background band for an overlay header (Sheet / Dialog / Drawer)
|
|
3
|
+
* Soft semantic background band for an overlay header (Sheet / Dialog / Drawer).
|
|
4
4
|
* Tints ONLY the background — text stays `--foreground` (渋み: never invert the band into a loud
|
|
5
5
|
* solid fill).
|
|
6
6
|
*/
|