@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.
Files changed (137) hide show
  1. package/dist/components/data-display/card.d.ts +2 -2
  2. package/dist/components/data-display/collapsible.d.ts +23 -4
  3. package/dist/components/data-display/collapsible.js +119 -4
  4. package/dist/components/data-display/index.d.ts +3 -1
  5. package/dist/components/data-display/index.js +2 -0
  6. package/dist/components/data-display/legend.d.ts +15 -0
  7. package/dist/components/data-display/legend.js +11 -0
  8. package/dist/components/data-display/list-row.js +1 -1
  9. package/dist/components/data-display/popover.d.ts +58 -5
  10. package/dist/components/data-display/popover.js +206 -27
  11. package/dist/components/data-display/progress.d.ts +50 -2
  12. package/dist/components/data-display/progress.js +57 -9
  13. package/dist/components/data-entry/calendar.d.ts +1 -1
  14. package/dist/components/data-entry/calendar.js +67 -32
  15. package/dist/components/data-entry/checkbox.d.ts +19 -6
  16. package/dist/components/data-entry/checkbox.js +55 -16
  17. package/dist/components/data-entry/choice-option.d.ts +1 -1
  18. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  19. package/dist/components/data-entry/field.js +0 -1
  20. package/dist/components/data-entry/form.js +1 -1
  21. package/dist/components/data-entry/label.d.ts +3 -2
  22. package/dist/components/data-entry/label.js +23 -10
  23. package/dist/components/data-entry/month-picker.d.ts +1 -1
  24. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  25. package/dist/components/data-entry/transfer.js +16 -6
  26. package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
  27. package/dist/components/data-entry/tree-select.js +29 -12
  28. package/dist/components/data-entry/tree-utils.d.ts +1 -1
  29. package/dist/components/data-entry/upload.js +7 -2
  30. package/dist/components/feedback/dialog.d.ts +105 -38
  31. package/dist/components/feedback/dialog.js +272 -194
  32. package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
  33. package/dist/components/feedback/overlay-close-focus.js +31 -0
  34. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  35. package/dist/components/feedback/sheet.d.ts +53 -11
  36. package/dist/components/feedback/sheet.js +150 -81
  37. package/dist/components/feedback/tooltip.d.ts +51 -7
  38. package/dist/components/feedback/tooltip.js +107 -25
  39. package/dist/components/general/button.js +2 -2
  40. package/dist/components/general/logo.d.ts +17 -0
  41. package/dist/components/general/logo.js +22 -16
  42. package/dist/components/layout/app-shell.d.ts +1 -1
  43. package/dist/components/layout/app-shell.js +52 -8
  44. package/dist/components/layout/aspect-ratio.js +0 -1
  45. package/dist/components/layout/auth-divider.js +0 -1
  46. package/dist/components/layout/flex.d.ts +1 -1
  47. package/dist/components/layout/flex.js +19 -2
  48. package/dist/components/layout/nav-surface.d.ts +26 -0
  49. package/dist/components/layout/nav-surface.js +17 -0
  50. package/dist/components/layout/org-switcher.d.ts +5 -1
  51. package/dist/components/layout/org-switcher.js +5 -3
  52. package/dist/components/layout/separator.js +0 -1
  53. package/dist/components/layout/sidebar.js +4 -1
  54. package/dist/components/layout/split-pane.d.ts +14 -1
  55. package/dist/components/layout/split-pane.js +26 -13
  56. package/dist/components/layout/topbar-item.js +1 -2
  57. package/dist/components/navigation/app-setting-picker.js +21 -2
  58. package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
  59. package/dist/components/navigation/app-setting-toggle.js +96 -0
  60. package/dist/components/navigation/dropdown-menu.d.ts +199 -18
  61. package/dist/components/navigation/dropdown-menu.js +344 -117
  62. package/dist/components/navigation/index.d.ts +2 -0
  63. package/dist/components/navigation/index.js +2 -0
  64. package/dist/components/navigation/pagination-utils.d.ts +2 -1
  65. package/dist/components/navigation/tabs.d.ts +47 -6
  66. package/dist/components/navigation/tabs.js +152 -90
  67. package/dist/components/ui/accordion.d.ts +50 -5
  68. package/dist/components/ui/accordion.js +239 -33
  69. package/dist/components/ui/aspect-ratio.d.ts +23 -2
  70. package/dist/components/ui/aspect-ratio.js +15 -13
  71. package/dist/components/ui/avatar.d.ts +29 -4
  72. package/dist/components/ui/avatar.js +111 -25
  73. package/dist/components/ui/hover-card.d.ts +42 -4
  74. package/dist/components/ui/hover-card.js +183 -27
  75. package/dist/components/ui/label.js +0 -1
  76. package/dist/components/ui/segmented.d.ts +3 -3
  77. package/dist/components/ui/separator.d.ts +8 -2
  78. package/dist/components/ui/separator.js +17 -9
  79. package/dist/components/ui/toggle-group.d.ts +50 -5
  80. package/dist/components/ui/toggle-group.js +79 -20
  81. package/dist/components/ui/toggle.d.ts +31 -5
  82. package/dist/components/ui/toggle.js +42 -3
  83. package/dist/i18n/messages/en.json +8 -0
  84. package/dist/i18n/messages/ja.json +8 -0
  85. package/dist/i18n/messages/vi.json +8 -0
  86. package/dist/lib/control-styles.d.ts +1 -1
  87. package/dist/lib/control-styles.js +1 -1
  88. package/dist/lib/slot.d.ts +32 -0
  89. package/dist/lib/slot.js +22 -0
  90. package/dist/lib/variants.d.ts +22 -3
  91. package/dist/lib/variants.js +56 -1
  92. package/dist/props/components/app.prop.d.ts +25 -1
  93. package/dist/props/components/data-display.prop.d.ts +17 -0
  94. package/dist/props/components/data-entry.prop.d.ts +32 -4
  95. package/dist/props/components/index.d.ts +1 -1
  96. package/dist/props/components/layout.prop.d.ts +118 -5
  97. package/dist/props/components/navigation.prop.d.ts +1 -1
  98. package/dist/props/registry.d.ts +22 -2
  99. package/dist/props/registry.js +33 -2
  100. package/dist/props/vocabulary/content.prop.d.ts +1 -1
  101. package/dist/props/vocabulary/index.d.ts +2 -2
  102. package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
  103. package/dist/props/vocabulary/layout.prop.d.ts +57 -1
  104. package/dist/styles/control.css +142 -1
  105. package/dist/styles/core.css +5 -2
  106. package/dist/styles/data-display-layout.css +72 -0
  107. package/dist/styles/focus-ring.css +4 -0
  108. package/dist/styles/index.css +5 -2
  109. package/dist/styles/layout.css +106 -0
  110. package/dist/styles/shell-layout.css +140 -8
  111. package/dist/styles/text-layout.css +5 -4
  112. package/dist/tokens/base.css +1 -1
  113. package/dist/tokens/components/data-display.css +11 -0
  114. package/dist/tokens/components/shell.css +3 -1
  115. package/dist/tokens/foundation.css +2 -0
  116. package/docs/COMPONENTS.md +9 -3
  117. package/docs/DESIGN-AUTHORITY.md +96 -71
  118. package/docs/FRAME-COVERAGE-REPORT.md +5 -3
  119. package/docs/README.md +14 -14
  120. package/docs/WHAT-BELONGS-HERE.md +179 -0
  121. package/docs/data-display/legend.tsx +145 -0
  122. package/docs/data-display/progress.tsx +32 -0
  123. package/docs/data-entry/segmented.tsx +1 -1
  124. package/docs/data-entry/select.tsx +2 -2
  125. package/docs/feedback/sheet.tsx +1 -1
  126. package/docs/layout/app-shell-arrangements.tsx +225 -0
  127. package/docs/layout/aspect-ratio.tsx +1 -1
  128. package/docs/navigation/app-setting-picker.tsx +26 -1
  129. package/docs/navigation/app-setting-toggle.tsx +111 -0
  130. package/package.json +11 -8
  131. package/scripts/_agent-setup.mjs +165 -3
  132. package/scripts/consumer-rule.md +98 -0
  133. package/scripts/guinea-pig-skill.md +322 -0
  134. package/scripts/init-guinea-pig.mjs +62 -0
  135. package/scripts/postinstall.mjs +13 -2
  136. package/scripts/ui-audit.mjs +115 -14
  137. /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 * as DialogPrimitive from "@radix-ui/react-dialog";
5
- import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
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 DialogRoot(props) {
15
- return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
16
- }
17
- function DialogTrigger(props) {
18
- return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
19
- }
20
- function DialogPortal(props) {
21
- return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
22
- }
23
- function DialogClose(props) {
24
- return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
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 DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27
- DialogPrimitive.Overlay,
28
- {
29
- ref,
30
- "data-slot": "dialog-overlay",
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
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
39
- const DialogContent = React.forwardRef(
40
- ({
41
- className,
42
- children,
43
- showClose,
44
- showCloseButton: showCloseButtonProp,
45
- overlayClassName,
46
- ...props
47
- }, ref) => {
48
- const { t } = useTranslation();
49
- const showCloseButton = showCloseButtonProp ?? showClose ?? true;
50
- return /* @__PURE__ */ jsxs(DialogPortal, { children: [
51
- /* @__PURE__ */ jsx(DialogOverlay, { className: overlayClassName }),
52
- /* @__PURE__ */ jsxs(
53
- DialogPrimitive.Content,
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
- ref,
73
+ role,
74
+ "aria-labelledby": props["aria-labelledby"] ?? titleId,
75
+ "aria-describedby": props["aria-describedby"] ?? descriptionId,
56
76
  "data-slot": "dialog-content",
57
- className: cn(
58
- "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",
59
- className
60
- ),
61
- ...props,
62
- children: [
63
- children,
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(({ className, ...props }, ref) => {
120
- const cls = cn(className);
121
- return /* @__PURE__ */ jsx(DialogPrimitive.Title, { ref, "data-slot": "dialog-title", className: cls, ...props });
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
- DialogPrimitive.Description,
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(props) {
138
- return /* @__PURE__ */ jsx(AlertDialogPrimitive.Root, { "data-slot": "dialog", ...props });
219
+ function AlertDialogRoot({ children, ...openProps }) {
220
+ const state = useOverlayOpenState(openProps);
221
+ return /* @__PURE__ */ jsx(DialogOpenContext.Provider, { value: state, children });
139
222
  }
140
- const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
141
- const AlertDialogPortal = AlertDialogPrimitive.Portal;
142
- const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
143
- AlertDialogPrimitive.Overlay,
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
- ref,
162
- "data-slot": "dialog-content",
163
- className: cn(
164
- "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",
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
- children: [
169
- children,
170
- showCloseButton ? /* @__PURE__ */ jsx(AlertDialogPrimitive.Cancel, { asChild: true, children: /* @__PURE__ */ jsx(
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 = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
218
- AlertDialogPrimitive.Title,
219
- {
220
- ref,
221
- "data-slot": "dialog-title",
222
- className: cn(className),
223
- ...props
224
- }
225
- ));
226
- AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
227
- const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
228
- AlertDialogPrimitive.Description,
229
- {
230
- ref,
231
- "data-slot": "dialog-description",
232
- className: cn(className),
233
- ...props
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
- const DialogCancel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
240
- AlertDialogPrimitive.Cancel,
241
- {
242
- ref,
243
- className: cn(buttonVariants({ variant: "outline" }), className),
244
- ...props
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(AlertDialogPrimitive.Root, { "data-slot": "dialog", open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogPrimitive.Portal, { children: [
312
- /* @__PURE__ */ jsx(
313
- AlertDialogPrimitive.Overlay,
314
- {
315
- "data-slot": "dialog-overlay",
316
- className: "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"
317
- }
318
- ),
319
- /* @__PURE__ */ jsxs(
320
- AlertDialogPrimitive.Content,
321
- {
322
- "data-slot": "dialog-content",
323
- className: "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",
324
- children: [
325
- /* @__PURE__ */ jsxs(DialogHeader, { tone: headerTone, children: [
326
- /* @__PURE__ */ jsx(AlertDialogPrimitive.Title, { "data-slot": "dialog-title", children: title }),
327
- description ? /* @__PURE__ */ jsx(AlertDialogPrimitive.Description, { "data-slot": "dialog-description", children: description }) : null
328
- ] }),
329
- needsPhrase && /* @__PURE__ */ jsxs("div", { className: "ui-stack-xs", children: [
330
- /* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: t("common.typeToConfirm", { phrase }) }),
331
- /* @__PURE__ */ jsx(
332
- Input,
333
- {
334
- id: inputId,
335
- value: typed,
336
- onChange: (e) => {
337
- setTyped(e.target.value);
338
- },
339
- autoComplete: "off",
340
- spellCheck: false,
341
- placeholder: phrase,
342
- "aria-required": "true",
343
- disabled: busy
344
- }
345
- )
346
- ] }),
347
- stepUpFailed && /* @__PURE__ */ jsx("p", { id: stepErrorId, role: "alert", className: "ui-dialog-step-up-error", children: t("feedback.alert.stepUpFailed") }),
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), Ant-Design style.
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
  */