@aloudata/aloudata-design 3.0.27 → 3.1.1

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 (224) hide show
  1. package/dist/AProgress/index.js +10 -0
  2. package/dist/AProgress/index.js.map +1 -1
  3. package/dist/Alert/index.js +3 -1
  4. package/dist/Alert/index.js.map +1 -1
  5. package/dist/Badge/index.js +37 -11
  6. package/dist/Badge/index.js.map +1 -1
  7. package/dist/Breadcrumb/index.js +12 -2
  8. package/dist/Breadcrumb/index.js.map +1 -1
  9. package/dist/Button/index.d.ts +1 -1
  10. package/dist/Button/index.js +29 -9
  11. package/dist/Button/index.js.map +1 -1
  12. package/dist/Card/index.js +1 -1
  13. package/dist/Card/index.js.map +1 -1
  14. package/dist/Carousel/index.js +9 -1
  15. package/dist/Carousel/index.js.map +1 -1
  16. package/dist/Checkbox/index.js +25 -4
  17. package/dist/Checkbox/index.js.map +1 -1
  18. package/dist/Checkbox/type.d.ts +10 -2
  19. package/dist/Collapse/index.js +68 -22
  20. package/dist/Collapse/index.js.map +1 -1
  21. package/dist/ColorPicker/index.js +112 -46
  22. package/dist/ColorPicker/index.js.map +1 -1
  23. package/dist/DataPreviewTable/components/Body/Cell.d.ts +2 -0
  24. package/dist/DataPreviewTable/components/Body/Cell.js +9 -1
  25. package/dist/DataPreviewTable/components/Body/Cell.js.map +1 -1
  26. package/dist/DataPreviewTable/components/Body/index.d.ts +1 -0
  27. package/dist/DataPreviewTable/components/Body/index.js +35 -23
  28. package/dist/DataPreviewTable/components/Body/index.js.map +1 -1
  29. package/dist/DataPreviewTable/components/Header/index.d.ts +1 -0
  30. package/dist/DataPreviewTable/components/Header/index.js +36 -28
  31. package/dist/DataPreviewTable/components/Header/index.js.map +1 -1
  32. package/dist/DataPreviewTable/index.js +19 -2
  33. package/dist/DataPreviewTable/index.js.map +1 -1
  34. package/dist/Drawer/index.d.ts +4 -0
  35. package/dist/Drawer/index.js +91 -78
  36. package/dist/Drawer/index.js.map +1 -1
  37. package/dist/Dropdown/index.d.ts +1 -1
  38. package/dist/Dropdown/index.js +116 -98
  39. package/dist/Dropdown/index.js.map +1 -1
  40. package/dist/Form/FormItemContext.d.ts +1 -1
  41. package/dist/Form/FormItemContext.js.map +1 -1
  42. package/dist/Form/index.js +50 -7
  43. package/dist/Form/index.js.map +1 -1
  44. package/dist/IconButton/index.d.ts +2 -2
  45. package/dist/IconButton/index.js +9 -3
  46. package/dist/IconButton/index.js.map +1 -1
  47. package/dist/Input/components/Input/index.js +23 -7
  48. package/dist/Input/components/Input/index.js.map +1 -1
  49. package/dist/Input/components/TextArea/index.js +20 -2
  50. package/dist/Input/components/TextArea/index.js.map +1 -1
  51. package/dist/Input/privateSemanticOwner.d.ts +2 -0
  52. package/dist/Input/privateSemanticOwner.js +1 -0
  53. package/dist/InputNumber/index.js +19 -5
  54. package/dist/InputNumber/index.js.map +1 -1
  55. package/dist/InputNumber/type.d.ts +2 -2
  56. package/dist/LogicTree/components/LogicItem/index.d.ts +2 -1
  57. package/dist/LogicTree/components/LogicItem/index.js +13 -2
  58. package/dist/LogicTree/components/LogicItem/index.js.map +1 -1
  59. package/dist/LogicTree/index.js +2 -0
  60. package/dist/LogicTree/index.js.map +1 -1
  61. package/dist/MemberPicker/components/MemberSelection.d.ts +1 -0
  62. package/dist/MemberPicker/components/MemberSelection.js +83 -94
  63. package/dist/MemberPicker/components/MemberSelection.js.map +1 -1
  64. package/dist/MemberPicker/components/MultipleOption.js +12 -0
  65. package/dist/MemberPicker/components/MultipleOption.js.map +1 -1
  66. package/dist/MemberPicker/components/Panel.d.ts +1 -0
  67. package/dist/MemberPicker/components/Panel.js +4 -1
  68. package/dist/MemberPicker/components/Panel.js.map +1 -1
  69. package/dist/MemberPicker/components/SelectedMemberTags.js +14 -6
  70. package/dist/MemberPicker/components/SelectedMemberTags.js.map +1 -1
  71. package/dist/MemberPicker/components/SingleOption.js +12 -0
  72. package/dist/MemberPicker/components/SingleOption.js.map +1 -1
  73. package/dist/MemberPicker/components/UserGroupSelection.d.ts +2 -1
  74. package/dist/MemberPicker/components/UserGroupSelection.js +61 -48
  75. package/dist/MemberPicker/components/UserGroupSelection.js.map +1 -1
  76. package/dist/MemberPicker/index.js +134 -108
  77. package/dist/MemberPicker/index.js.map +1 -1
  78. package/dist/Menu/index.d.ts +8 -0
  79. package/dist/Menu/index.js +33 -3
  80. package/dist/Menu/index.js.map +1 -1
  81. package/dist/Modal/index.d.ts +4 -0
  82. package/dist/Modal/index.js +55 -66
  83. package/dist/Modal/index.js.map +1 -1
  84. package/dist/OverflowCount/index.js +10 -0
  85. package/dist/OverflowCount/index.js.map +1 -1
  86. package/dist/Pagination/PageSizeChanger.d.ts +1 -0
  87. package/dist/Pagination/PageSizeChanger.js +3 -2
  88. package/dist/Pagination/PageSizeChanger.js.map +1 -1
  89. package/dist/Pagination/index.js +34 -19
  90. package/dist/Pagination/index.js.map +1 -1
  91. package/dist/Pagination/types.d.ts +3 -0
  92. package/dist/Popconfirm/index.js +9 -1
  93. package/dist/Popconfirm/index.js.map +1 -1
  94. package/dist/Popover/index.js +52 -1
  95. package/dist/Popover/index.js.map +1 -1
  96. package/dist/Progress/index.js +4 -0
  97. package/dist/Progress/index.js.map +1 -1
  98. package/dist/Radio/components/Group/index.d.ts +5 -1
  99. package/dist/Radio/components/Group/index.js +7 -2
  100. package/dist/Radio/components/Group/index.js.map +1 -1
  101. package/dist/Radio/components/Radio/index.js +27 -1
  102. package/dist/Radio/components/Radio/index.js.map +1 -1
  103. package/dist/Radio/interface/radio.d.ts +7 -0
  104. package/dist/Radio/interface/radioGroup.d.ts +2 -0
  105. package/dist/RenameInput/index.js +11 -0
  106. package/dist/RenameInput/index.js.map +1 -1
  107. package/dist/Select/BaseSelect.d.ts +3 -0
  108. package/dist/Select/BaseSelect.js +235 -38
  109. package/dist/Select/BaseSelect.js.map +1 -1
  110. package/dist/Select/SelectTrigger.d.ts +2 -0
  111. package/dist/Select/SelectTrigger.js +13 -5
  112. package/dist/Select/SelectTrigger.js.map +1 -1
  113. package/dist/Select/Selector/FixedOverflow.d.ts +1 -0
  114. package/dist/Select/Selector/FixedOverflow.js +3 -3
  115. package/dist/Select/Selector/FixedOverflow.js.map +1 -1
  116. package/dist/Select/Selector/MultipleSelector.js +1 -0
  117. package/dist/Select/Selector/MultipleSelector.js.map +1 -1
  118. package/dist/Select/components/OptionCollection.d.ts +41 -0
  119. package/dist/Select/components/OptionCollection.js +124 -0
  120. package/dist/Select/components/OptionCollection.js.map +1 -0
  121. package/dist/Select/index.js +45 -3
  122. package/dist/Select/index.js.map +1 -1
  123. package/dist/Select/interface.d.ts +1 -1
  124. package/dist/Skeleton/index.js +1 -1
  125. package/dist/Skeleton/index.js.map +1 -1
  126. package/dist/Spin/index.d.ts +1 -1
  127. package/dist/Spin/index.js +22 -7
  128. package/dist/Spin/index.js.map +1 -1
  129. package/dist/Steps/index.js +66 -14
  130. package/dist/Steps/index.js.map +1 -1
  131. package/dist/Switch/index.d.ts +5 -0
  132. package/dist/Switch/index.js +22 -5
  133. package/dist/Switch/index.js.map +1 -1
  134. package/dist/Table/components/Cell.js +8 -3
  135. package/dist/Table/components/Cell.js.map +1 -1
  136. package/dist/Table/components/ExpandCell.js +6 -2
  137. package/dist/Table/components/ExpandCell.js.map +1 -1
  138. package/dist/Table/components/Footer/index.js +2 -1
  139. package/dist/Table/components/Footer/index.js.map +1 -1
  140. package/dist/Table/components/Header.js +25 -18
  141. package/dist/Table/components/Header.js.map +1 -1
  142. package/dist/Table/hooks/useRowSelection.js +14 -7
  143. package/dist/Table/hooks/useRowSelection.js.map +1 -1
  144. package/dist/Table/index.js +50 -29
  145. package/dist/Table/index.js.map +1 -1
  146. package/dist/Tabs/index.d.ts +1 -1
  147. package/dist/Tabs/index.js +92 -11
  148. package/dist/Tabs/index.js.map +1 -1
  149. package/dist/Tag/index.js +20 -1
  150. package/dist/Tag/index.js.map +1 -1
  151. package/dist/Tooltip/index.js +34 -5
  152. package/dist/Tooltip/index.js.map +1 -1
  153. package/dist/Tour/index.js +13 -1
  154. package/dist/Tour/index.js.map +1 -1
  155. package/dist/Tree/Tree2.js +2 -0
  156. package/dist/Tree/Tree2.js.map +1 -1
  157. package/dist/_utils/dialogAccessibility.d.ts +17 -0
  158. package/dist/_utils/dialogAccessibility.js +110 -0
  159. package/dist/_utils/dialogAccessibility.js.map +1 -0
  160. package/dist/_utils/formControlSemanticOwner.d.ts +3 -0
  161. package/dist/_utils/formControlSemanticOwner.js +17 -0
  162. package/dist/_utils/formControlSemanticOwner.js.map +1 -0
  163. package/dist/_utils/overlayCoordinator.js +1 -11
  164. package/dist/_utils/overlayCoordinator.js.map +1 -1
  165. package/dist/aloudata-design.css +1 -1
  166. package/dist/locale/default.d.ts +6 -0
  167. package/dist/locale/en_US.d.ts +6 -0
  168. package/dist/locale/en_US.js +6 -0
  169. package/dist/locale/en_US.js.map +1 -1
  170. package/dist/locale/zh_CN.d.ts +6 -0
  171. package/dist/locale/zh_CN.js +6 -0
  172. package/dist/locale/zh_CN.js.map +1 -1
  173. package/dist/theme/contract/baselineResolvedTokenValueManifest.d.ts +1 -1
  174. package/dist/theme/contract/controlStateColorArtifact.d.ts +73 -0
  175. package/dist/theme/contract/controlStateColorArtifact.js +2 -0
  176. package/dist/theme/contract/darkResolvedTokenValueManifest.d.ts +1 -1
  177. package/dist/theme/contract/focusRingArtifact.d.ts +51 -0
  178. package/dist/theme/contract/focusRingArtifact.js +2 -0
  179. package/dist/theme/contract/tokenOutputContract.d.ts +1 -1
  180. package/dist/theme/contract/tokenOutputContract.js +4 -0
  181. package/dist/theme/contract/tokenOutputContract.js.map +1 -1
  182. package/dist/theme/generator/acceptedColorValues.d.ts +2 -1
  183. package/dist/theme/generator/baselineTokenValueDelivery.d.ts +9 -1
  184. package/dist/theme/generator/minimalAcceptedColorValues.d.ts +2 -2
  185. package/dist/theme/generator/neutralSourceResolver.d.ts +28 -0
  186. package/dist/theme/generator/neutralSourceResolver.js +2 -0
  187. package/dist/theme/generator/neutralSourceValues.d.ts +27 -0
  188. package/dist/theme/generator/neutralSourceValues.js +2 -0
  189. package/dist/theme/index.d.ts +2 -0
  190. package/dist/theme/index.js +1 -0
  191. package/dist/theme/neutralSourceContract.d.ts +3 -0
  192. package/dist/theme/neutralSourceContract.js +11 -0
  193. package/dist/theme/neutralSourceContract.js.map +1 -0
  194. package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.d.ts +1 -1
  195. package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.js +47 -44
  196. package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.js.map +1 -1
  197. package/dist/theme/runtime/colorValueMode/generated/slateDarkDeclarations.d.ts +3 -0
  198. package/dist/theme/runtime/colorValueMode/generated/slateDarkDeclarations.js +2 -0
  199. package/dist/theme/runtime/colorValueMode/generated/slateLightDeclarations.d.ts +3 -0
  200. package/dist/theme/runtime/colorValueMode/generated/slateLightDeclarations.js +2 -0
  201. package/dist/theme/runtime/colorValueMode/generated/stoneDarkDeclarations.d.ts +3 -0
  202. package/dist/theme/runtime/colorValueMode/generated/stoneDarkDeclarations.js +2 -0
  203. package/dist/theme/runtime/colorValueMode/generated/stoneLightDeclarations.d.ts +3 -0
  204. package/dist/theme/runtime/colorValueMode/generated/stoneLightDeclarations.js +2 -0
  205. package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.d.ts +1 -1
  206. package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.js +43 -40
  207. package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.js.map +1 -1
  208. package/dist/theme/runtime/colorValueMode/index.js +2 -12
  209. package/dist/theme/runtime/colorValueMode/index.js.map +1 -1
  210. package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.d.ts +2 -0
  211. package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.js +48 -0
  212. package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.js.map +1 -0
  213. package/dist/theme/runtime/colorValueMode/registry.d.ts +1 -1
  214. package/dist/theme/runtime/colorValueMode/registry.js +2 -1
  215. package/dist/theme/runtime/colorValueMode/registry.js.map +1 -1
  216. package/dist/theme/runtime/initAldTheme.js +1 -3
  217. package/dist/theme/runtime/initAldTheme.js.map +1 -1
  218. package/dist/theme/runtime/neutralSource/index.d.ts +6 -0
  219. package/dist/theme/runtime/neutralSource/index.js +11 -0
  220. package/dist/theme/runtime/neutralSource/index.js.map +1 -0
  221. package/dist/theme/runtime/neutralSource.d.ts +2 -0
  222. package/dist/theme/runtime/themeToCssVars.js +3 -0
  223. package/dist/theme/runtime/themeToCssVars.js.map +1 -1
  224. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import Memo from "../Icon/components/CloseLightLine.js";
3
- import { FloatingLayerProvider, useFloatingLayer } from "../_utils/floatingLayer.js";
4
- import { useBodyScrollLock } from "../_utils/overlayCoordinator.js";
3
+ import { FLOATING_LAYER_BASE_Z_INDEX, FloatingLayerProvider } from "../_utils/floatingLayer.js";
4
+ import { ModalLayerProvider, useModalLayer } from "../_utils/overlayCoordinator.js";
5
+ import { focusInitialDialogTarget, markDialogEscapeEvent, useDialogAccessibility } from "../_utils/dialogAccessibility.js";
5
6
  import { useEffect, useLayoutEffect, useRef, useState } from "react";
6
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
8
  import * as DialogPrimitive from "@radix-ui/react-dialog";
@@ -24,11 +25,19 @@ var slideAnimationStyles = {
24
25
  bottom: "data-[state=open]:tw-animate-drawer-in-bottom data-[state=closed]:tw-animate-drawer-out-bottom"
25
26
  };
26
27
  function Drawer(props) {
27
- const { open = false, onClose, title, description, placement = "right", width, height, size = "default", closable = true, mask = true, maskClosable = true, className, rootClassName, style, contentWrapperStyle, bodyStyle, headerStyle, footer, footerStyle, extra, children, zIndex = 1e3, getContainer } = props;
28
+ const { open = false, onClose, title, description, placement = "right", width, height, size = "default", closable = true, mask = true, maskClosable = true, className, rootClassName, "data-testid": dataTestId, "aria-label": ariaLabel, style, contentWrapperStyle, bodyStyle, headerStyle, footer, footerStyle, extra, children, zIndex = 1e3, keyboard = true, getContainer } = props;
28
29
  const contentRef = useRef(null);
29
30
  const [portalContainer, setPortalContainer] = useState();
30
31
  const inline = getContainer === false;
31
- useBodyScrollLock(open);
32
+ const { id: drawerLayerId, maskZIndex, contentZIndex, isTop } = useModalLayer(open, zIndex);
33
+ const nextFloatingLevel = (maskZIndex - FLOATING_LAYER_BASE_Z_INDEX) / 10 + 1;
34
+ useDialogAccessibility({
35
+ open,
36
+ isTop,
37
+ keyboard,
38
+ containerRef: contentRef,
39
+ onEscape: (event) => onClose?.(event)
40
+ });
32
41
  useLayoutEffect(() => {
33
42
  if (getContainer === void 0 || getContainer === false) setPortalContainer(void 0);
34
43
  else if (typeof getContainer === "string") setPortalContainer(document.querySelector(getContainer) ?? void 0);
@@ -36,31 +45,9 @@ function Drawer(props) {
36
45
  else setPortalContainer(getContainer);
37
46
  }, [getContainer]);
38
47
  useEffect(() => {
39
- if (!open || !contentRef.current) return;
48
+ if (!open || !isTop || !contentRef.current) return;
40
49
  return hideOthers(contentRef.current);
41
- }, [open]);
42
- useEffect(() => {
43
- if (!open || !contentRef.current) return;
44
- const container = contentRef.current;
45
- const handleKeyDown = (e) => {
46
- if (e.key !== "Tab") return;
47
- const focusable = container.querySelectorAll("a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])");
48
- if (focusable.length === 0) return;
49
- const first = focusable[0];
50
- const last = focusable[focusable.length - 1];
51
- if (e.shiftKey) {
52
- if (document.activeElement === first || !container.contains(document.activeElement)) {
53
- e.preventDefault();
54
- last.focus();
55
- }
56
- } else if (document.activeElement === last || !container.contains(document.activeElement)) {
57
- e.preventDefault();
58
- first.focus();
59
- }
60
- };
61
- document.addEventListener("keydown", handleKeyDown);
62
- return () => document.removeEventListener("keydown", handleKeyDown);
63
- }, [open]);
50
+ }, [isTop, open]);
64
51
  const isHorizontal = placement === "left" || placement === "right";
65
52
  const preset = sizePresets[size] || sizePresets.default;
66
53
  const sizeStyle = isHorizontal ? {
@@ -70,81 +57,107 @@ function Drawer(props) {
70
57
  height: height ?? preset,
71
58
  maxHeight: "100vh"
72
59
  };
73
- const { maskZIndex, contentZIndex, nextLevel } = useFloatingLayer(zIndex);
74
60
  const drawerNode = /* @__PURE__ */ jsxs("div", {
75
61
  className: cn("ald-drawer-root tw-pointer-events-none tw-inset-0", inline ? "tw-absolute" : "tw-fixed", rootClassName),
76
- children: [mask && /* @__PURE__ */ jsx("div", {
62
+ children: [mask && isTop && /* @__PURE__ */ jsx("div", {
77
63
  "data-state": open ? "open" : "closed",
78
64
  className: "ald-drawer-mask tw-pointer-events-auto tw-absolute tw-inset-0 tw-bg-[var(--background-inverted)] tw-opacity-45 data-[state=closed]:tw-animate-mask-out data-[state=open]:tw-animate-mask-in",
79
65
  style: { zIndex: maskZIndex },
80
66
  onClick: maskClosable ? () => onClose?.() : void 0
81
67
  }), /* @__PURE__ */ jsx(DialogPrimitive.Content, {
82
68
  ref: contentRef,
83
- className: cn("ald-drawer tw-pointer-events-auto tw-absolute tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-xl", placementStyles[placement], slideAnimationStyles[placement], className),
69
+ tabIndex: -1,
70
+ role: "dialog",
71
+ "data-testid": dataTestId,
72
+ "aria-modal": isTop || void 0,
73
+ "aria-label": ariaLabel,
74
+ ...description ? {} : { "aria-describedby": void 0 },
75
+ ...!isTop ? { inert: "" } : {},
76
+ "aria-hidden": !isTop || void 0,
77
+ className: cn("ald-drawer tw-pointer-events-auto tw-absolute tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-[var(--elevation-bottom-bottom-xl)]", placementStyles[placement], slideAnimationStyles[placement], className),
84
78
  style: {
85
79
  zIndex: contentZIndex,
86
80
  ...sizeStyle,
87
81
  ...contentWrapperStyle,
88
82
  ...style
89
83
  },
90
- onEscapeKeyDown: () => onClose?.(),
84
+ onOpenAutoFocus: (event) => {
85
+ event.preventDefault();
86
+ requestAnimationFrame(() => {
87
+ const drawer = contentRef.current;
88
+ if (drawer) focusInitialDialogTarget(drawer);
89
+ });
90
+ },
91
+ onEscapeKeyDown: (event) => {
92
+ if (!event.defaultPrevented) markDialogEscapeEvent(event);
93
+ event.preventDefault();
94
+ },
91
95
  onInteractOutside: (event) => {
92
96
  if (!maskClosable) event.preventDefault();
93
97
  },
94
- children: /* @__PURE__ */ jsxs(FloatingLayerProvider, {
95
- value: nextLevel,
96
- children: [
97
- (title || description || closable || extra) && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {
98
- className: "ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--background-default)] tw-px-6 tw-pb-0 tw-pt-4",
99
- style: headerStyle,
100
- children: /* @__PURE__ */ jsxs("div", {
101
- className: "tw-flex tw-w-full tw-flex-col tw-gap-[6px]",
102
- children: [/* @__PURE__ */ jsxs("div", {
103
- className: "tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]",
104
- children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
105
- className: "ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]",
106
- children: title
107
- }), extra || closable ? /* @__PURE__ */ jsxs("div", {
108
- className: "ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]",
109
- children: [extra, closable && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
110
- asChild: true,
111
- children: /* @__PURE__ */ jsx("button", {
112
- type: "button",
113
- className: "ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-[var(--action-ghost-normal)] hover:tw-bg-[var(--action-ghost-hover)]",
114
- onClick: () => onClose?.(),
115
- children: /* @__PURE__ */ jsx(Memo, {
116
- size: 20,
117
- color: "var(--content-secondary)"
98
+ children: /* @__PURE__ */ jsx(ModalLayerProvider, {
99
+ id: drawerLayerId,
100
+ children: /* @__PURE__ */ jsxs(FloatingLayerProvider, {
101
+ value: nextFloatingLevel,
102
+ children: [
103
+ !title && /* @__PURE__ */ jsx(DialogPrimitive.Title, {
104
+ className: "tw-sr-only",
105
+ children: ariaLabel || "Drawer"
106
+ }),
107
+ (title || description || closable || extra) && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {
108
+ className: "ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--background-default)] tw-px-6 tw-pb-0 tw-pt-4",
109
+ style: headerStyle,
110
+ children: /* @__PURE__ */ jsxs("div", {
111
+ className: "tw-flex tw-w-full tw-flex-col tw-gap-[6px]",
112
+ children: [/* @__PURE__ */ jsxs("div", {
113
+ className: "tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]",
114
+ children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
115
+ className: "ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]",
116
+ children: title
117
+ }), extra || closable ? /* @__PURE__ */ jsxs("div", {
118
+ className: "ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]",
119
+ children: [extra, closable && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
120
+ asChild: true,
121
+ children: /* @__PURE__ */ jsx("button", {
122
+ type: "button",
123
+ className: "ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-[var(--action-ghost-normal)] hover:tw-bg-[var(--action-ghost-hover)] focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)] forced-colors:focus-visible:tw-outline-[Highlight]",
124
+ "aria-label": "Close",
125
+ "data-overlay-close": "true",
126
+ onClick: () => onClose?.(),
127
+ children: /* @__PURE__ */ jsx(Memo, {
128
+ size: 20,
129
+ color: "var(--content-secondary)"
130
+ })
118
131
  })
119
- })
120
- })]
132
+ })]
133
+ }) : null]
134
+ }), description ? /* @__PURE__ */ jsx(DialogPrimitive.Description, {
135
+ asChild: true,
136
+ children: /* @__PURE__ */ jsx("div", {
137
+ className: "ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]",
138
+ children: description
139
+ })
121
140
  }) : null]
122
- }), description ? /* @__PURE__ */ jsx("div", {
123
- className: "ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]",
124
- children: description
125
- }) : null]
126
- })
127
- }) }),
128
- /* @__PURE__ */ jsx("div", {
129
- className: "ald-drawer-body ant-drawer-body tw-min-w-0 tw-flex-[1_0_0] tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]",
130
- style: bodyStyle,
131
- children
132
- }),
133
- footer && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]" }), /* @__PURE__ */ jsx("div", {
134
- className: "ald-drawer-footer ant-drawer-footer tw-flex tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]",
135
- style: footerStyle,
136
- children: footer
137
- })] })
138
- ]
141
+ })
142
+ }) }),
143
+ /* @__PURE__ */ jsx("div", {
144
+ className: "ald-drawer-body ant-drawer-body tw-min-w-0 tw-flex-[1_0_0] tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]",
145
+ style: bodyStyle,
146
+ children
147
+ }),
148
+ footer && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]" }), /* @__PURE__ */ jsx("div", {
149
+ className: "ald-drawer-footer ant-drawer-footer tw-flex tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]",
150
+ style: footerStyle,
151
+ children: footer
152
+ })] })
153
+ ]
154
+ })
139
155
  })
140
156
  })]
141
157
  });
142
158
  return /* @__PURE__ */ jsx(DialogPrimitive.Root, {
143
159
  open,
144
160
  modal: false,
145
- onOpenChange: (val) => {
146
- if (!val) onClose?.();
147
- },
148
161
  children: inline ? open ? drawerNode : null : /* @__PURE__ */ jsx(DialogPrimitive.Portal, {
149
162
  container: portalContainer,
150
163
  children: drawerNode
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/Drawer/index.tsx"],"sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { hideOthers } from 'aria-hidden';\nimport React, { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { CloseLightLine } from '../Icon';\nimport {\n FloatingLayerProvider,\n useFloatingLayer,\n} from '../_utils/floatingLayer';\nimport { useBodyScrollLock } from '../_utils/overlayCoordinator';\nimport { cn } from '../lib/utils';\n\nexport interface DrawerProps {\n open?: boolean;\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void;\n title?: React.ReactNode;\n description?: React.ReactNode;\n placement?: 'top' | 'right' | 'bottom' | 'left';\n width?: number | string;\n height?: number | string;\n size?: 'default' | 'large';\n closable?: boolean;\n mask?: boolean;\n maskClosable?: boolean;\n destroyOnClose?: boolean;\n className?: string;\n rootClassName?: string;\n style?: React.CSSProperties;\n contentWrapperStyle?: React.CSSProperties;\n bodyStyle?: React.CSSProperties;\n headerStyle?: React.CSSProperties;\n maskStyle?: React.CSSProperties;\n footer?: React.ReactNode;\n footerStyle?: React.CSSProperties;\n extra?: React.ReactNode;\n children?: React.ReactNode;\n zIndex?: number;\n keyboard?: boolean;\n getContainer?:\n | string\n | Element\n | DocumentFragment\n | (() => Element | DocumentFragment)\n | false;\n}\n\nconst sizePresets: Record<string, number> = {\n default: 378,\n large: 736,\n};\n\nconst placementStyles: Record<string, string> = {\n right: 'tw-inset-y-0 tw-right-0 tw-h-full',\n left: 'tw-inset-y-0 tw-left-0 tw-h-full',\n top: 'tw-inset-x-0 tw-top-0 tw-w-full',\n bottom: 'tw-inset-x-0 tw-bottom-0 tw-w-full',\n};\n\nconst slideAnimationStyles: Record<string, string> = {\n right:\n 'data-[state=open]:tw-animate-drawer-in-right data-[state=closed]:tw-animate-drawer-out-right',\n left: 'data-[state=open]:tw-animate-drawer-in-left data-[state=closed]:tw-animate-drawer-out-left',\n top: 'data-[state=open]:tw-animate-drawer-in-top data-[state=closed]:tw-animate-drawer-out-top',\n bottom:\n 'data-[state=open]:tw-animate-drawer-in-bottom data-[state=closed]:tw-animate-drawer-out-bottom',\n};\n\nfunction Drawer(props: DrawerProps) {\n const {\n open = false,\n onClose,\n title,\n description,\n placement = 'right',\n width,\n height,\n size = 'default',\n closable = true,\n mask = true,\n maskClosable = true,\n className,\n rootClassName,\n style,\n contentWrapperStyle,\n bodyStyle,\n headerStyle,\n footer,\n footerStyle,\n extra,\n children,\n zIndex = 1000,\n getContainer,\n } = props;\n\n const contentRef = useRef<HTMLDivElement>(null);\n const [portalContainer, setPortalContainer] = useState<\n Element | DocumentFragment\n >();\n const inline = getContainer === false;\n useBodyScrollLock(open);\n\n useLayoutEffect(() => {\n if (getContainer === undefined || getContainer === false) {\n setPortalContainer(undefined);\n } else if (typeof getContainer === 'string') {\n setPortalContainer(document.querySelector(getContainer) ?? undefined);\n } else if (typeof getContainer === 'function') {\n setPortalContainer(getContainer());\n } else {\n setPortalContainer(getContainer);\n }\n }, [getContainer]);\n\n // ---- modal={false} 补偿:aria-hidden(屏幕阅读器只感知抽屉) ----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n return hideOthers(contentRef.current);\n }, [open]);\n\n // ---- modal={false} 补偿:Tab 焦点循环(不使用 MutationObserver,避免 FocusScope 劫持焦点)----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n const container = contentRef.current;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = container.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (e.shiftKey) {\n if (\n document.activeElement === first ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (\n document.activeElement === last ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [open]);\n\n const isHorizontal = placement === 'left' || placement === 'right';\n const preset = sizePresets[size] || sizePresets.default;\n const effectiveWidth = width ?? preset;\n const effectiveHeight = height ?? preset;\n const sizeStyle = isHorizontal\n ? { width: effectiveWidth, maxWidth: '100vw' }\n : { height: effectiveHeight, maxHeight: '100vh' };\n const { maskZIndex, contentZIndex, nextLevel } = useFloatingLayer(zIndex);\n\n const drawerNode = (\n <div\n className={cn(\n 'ald-drawer-root tw-pointer-events-none tw-inset-0',\n inline ? 'tw-absolute' : 'tw-fixed',\n rootClassName,\n )}\n >\n {/* modal={false} 时 DialogPrimitive.Overlay 不渲染,用普通 div 替代 */}\n {mask && (\n <div\n data-state={open ? 'open' : 'closed'}\n className=\"ald-drawer-mask tw-pointer-events-auto tw-absolute tw-inset-0 tw-bg-[var(--background-inverted)] tw-opacity-45 data-[state=closed]:tw-animate-mask-out data-[state=open]:tw-animate-mask-in\"\n style={{ zIndex: maskZIndex }}\n onClick={maskClosable ? () => onClose?.() : undefined}\n />\n )}\n <DialogPrimitive.Content\n ref={contentRef}\n className={cn(\n 'ald-drawer tw-pointer-events-auto tw-absolute tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-xl',\n placementStyles[placement],\n slideAnimationStyles[placement],\n className,\n )}\n style={{\n zIndex: contentZIndex,\n ...sizeStyle,\n ...contentWrapperStyle,\n ...style,\n }}\n onEscapeKeyDown={() => onClose?.()}\n onInteractOutside={(event) => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n >\n <FloatingLayerProvider value={nextLevel}>\n {(title || description || closable || extra) && (\n <>\n {/* antd 兼容:保留 ant-drawer-header / ant-drawer-header-title class,消费方 CSS 可能依赖这些选择器 */}\n <div\n className=\"ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--background-default)] tw-px-6 tw-pb-0 tw-pt-4\"\n style={headerStyle}\n >\n <div className=\"tw-flex tw-w-full tw-flex-col tw-gap-[6px]\">\n <div className=\"tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]\">\n <DialogPrimitive.Title className='ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]'>\n {title}\n </DialogPrimitive.Title>\n {extra || closable ? (\n <div className=\"ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]\">\n {extra}\n {closable && (\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n className=\"ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-[var(--action-ghost-normal)] hover:tw-bg-[var(--action-ghost-hover)]\"\n onClick={() => onClose?.()}\n >\n <CloseLightLine\n size={20}\n color=\"var(--content-secondary)\"\n />\n </button>\n </DialogPrimitive.Close>\n )}\n </div>\n ) : null}\n </div>\n {description ? (\n <div className='ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]'>\n {description}\n </div>\n ) : null}\n </div>\n </div>\n </>\n )}\n {/* antd 兼容:保留 ant-drawer-body class */}\n <div\n className=\"ald-drawer-body ant-drawer-body tw-min-w-0 tw-flex-[1_0_0] tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]\"\n style={bodyStyle}\n >\n {children}\n </div>\n {footer && (\n <>\n <div className=\"ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]\" />\n {/* antd 兼容:antd .ant-drawer-footer 使用 text-align:right 让 inline 按钮右对齐,\n 此处加 tw-text-right 保持消费方传入 width:100% 子容器时 inline 元素仍右对齐。 */}\n <div\n className=\"ald-drawer-footer ant-drawer-footer tw-flex tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]\"\n style={footerStyle}\n >\n {footer}\n </div>\n </>\n )}\n </FloatingLayerProvider>\n </DialogPrimitive.Content>\n </div>\n );\n\n // modal={false}:禁用 Radix FocusScope 的 MutationObserver,\n // 避免表单校验触发 DOM 变动时劫持焦点。手动补偿滚动锁定、aria-hidden、Tab 循环。\n return (\n <DialogPrimitive.Root\n open={open}\n modal={false}\n onOpenChange={(val) => {\n if (!val) onClose?.();\n }}\n >\n {inline ? (\n open ? (\n drawerNode\n ) : null\n ) : (\n <DialogPrimitive.Portal container={portalContainer}>\n {drawerNode}\n </DialogPrimitive.Portal>\n )}\n </DialogPrimitive.Root>\n );\n}\n\nexport default Drawer;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/Drawer/index.tsx"],"sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { hideOthers } from 'aria-hidden';\nimport React, { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { CloseLightLine } from '../Icon';\nimport {\n FLOATING_LAYER_BASE_Z_INDEX,\n FLOATING_LAYER_STEP,\n FloatingLayerProvider,\n} from '../_utils/floatingLayer';\nimport {\n ModalLayerProvider,\n useModalLayer,\n} from '../_utils/overlayCoordinator';\nimport {\n focusInitialDialogTarget,\n markDialogEscapeEvent,\n useDialogAccessibility,\n} from '../_utils/dialogAccessibility';\nimport { cn } from '../lib/utils';\n\nexport interface DrawerProps {\n open?: boolean;\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void;\n title?: React.ReactNode;\n description?: React.ReactNode;\n placement?: 'top' | 'right' | 'bottom' | 'left';\n width?: number | string;\n height?: number | string;\n size?: 'default' | 'large';\n closable?: boolean;\n mask?: boolean;\n maskClosable?: boolean;\n destroyOnClose?: boolean;\n className?: string;\n rootClassName?: string;\n /** Pass through to the drawer content root for automated testing. */\n 'data-testid'?: string;\n /** Pass through to the drawer content root when no title is rendered. */\n 'aria-label'?: string;\n style?: React.CSSProperties;\n contentWrapperStyle?: React.CSSProperties;\n bodyStyle?: React.CSSProperties;\n headerStyle?: React.CSSProperties;\n maskStyle?: React.CSSProperties;\n footer?: React.ReactNode;\n footerStyle?: React.CSSProperties;\n extra?: React.ReactNode;\n children?: React.ReactNode;\n zIndex?: number;\n keyboard?: boolean;\n getContainer?:\n | string\n | Element\n | DocumentFragment\n | (() => Element | DocumentFragment)\n | false;\n}\n\nconst sizePresets: Record<string, number> = {\n default: 378,\n large: 736,\n};\n\nconst placementStyles: Record<string, string> = {\n right: 'tw-inset-y-0 tw-right-0 tw-h-full',\n left: 'tw-inset-y-0 tw-left-0 tw-h-full',\n top: 'tw-inset-x-0 tw-top-0 tw-w-full',\n bottom: 'tw-inset-x-0 tw-bottom-0 tw-w-full',\n};\n\nconst slideAnimationStyles: Record<string, string> = {\n right:\n 'data-[state=open]:tw-animate-drawer-in-right data-[state=closed]:tw-animate-drawer-out-right',\n left: 'data-[state=open]:tw-animate-drawer-in-left data-[state=closed]:tw-animate-drawer-out-left',\n top: 'data-[state=open]:tw-animate-drawer-in-top data-[state=closed]:tw-animate-drawer-out-top',\n bottom:\n 'data-[state=open]:tw-animate-drawer-in-bottom data-[state=closed]:tw-animate-drawer-out-bottom',\n};\n\nfunction Drawer(props: DrawerProps) {\n const {\n open = false,\n onClose,\n title,\n description,\n placement = 'right',\n width,\n height,\n size = 'default',\n closable = true,\n mask = true,\n maskClosable = true,\n className,\n rootClassName,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n style,\n contentWrapperStyle,\n bodyStyle,\n headerStyle,\n footer,\n footerStyle,\n extra,\n children,\n zIndex = 1000,\n keyboard = true,\n getContainer,\n } = props;\n\n const contentRef = useRef<HTMLDivElement>(null);\n const [portalContainer, setPortalContainer] = useState<\n Element | DocumentFragment\n >();\n const inline = getContainer === false;\n const drawerLayer = useModalLayer(open, zIndex);\n const { id: drawerLayerId, maskZIndex, contentZIndex, isTop } = drawerLayer;\n const nextFloatingLevel =\n (maskZIndex - FLOATING_LAYER_BASE_Z_INDEX) / FLOATING_LAYER_STEP + 1;\n\n useDialogAccessibility({\n open,\n isTop,\n keyboard,\n containerRef: contentRef,\n onEscape: (event) => onClose?.(event as unknown as React.KeyboardEvent),\n });\n\n useLayoutEffect(() => {\n if (getContainer === undefined || getContainer === false) {\n setPortalContainer(undefined);\n } else if (typeof getContainer === 'string') {\n setPortalContainer(document.querySelector(getContainer) ?? undefined);\n } else if (typeof getContainer === 'function') {\n setPortalContainer(getContainer());\n } else {\n setPortalContainer(getContainer);\n }\n }, [getContainer]);\n\n // ---- modal={false} 补偿:aria-hidden(屏幕阅读器只感知抽屉) ----\n useEffect(() => {\n if (!open || !isTop || !contentRef.current) return;\n return hideOthers(contentRef.current);\n }, [isTop, open]);\n\n const isHorizontal = placement === 'left' || placement === 'right';\n const preset = sizePresets[size] || sizePresets.default;\n const effectiveWidth = width ?? preset;\n const effectiveHeight = height ?? preset;\n const sizeStyle = isHorizontal\n ? { width: effectiveWidth, maxWidth: '100vw' }\n : { height: effectiveHeight, maxHeight: '100vh' };\n const drawerNode = (\n <div\n className={cn(\n 'ald-drawer-root tw-pointer-events-none tw-inset-0',\n inline ? 'tw-absolute' : 'tw-fixed',\n rootClassName,\n )}\n >\n {/* modal={false} 时 DialogPrimitive.Overlay 不渲染,用普通 div 替代 */}\n {mask && isTop && (\n <div\n data-state={open ? 'open' : 'closed'}\n className=\"ald-drawer-mask tw-pointer-events-auto tw-absolute tw-inset-0 tw-bg-[var(--background-inverted)] tw-opacity-45 data-[state=closed]:tw-animate-mask-out data-[state=open]:tw-animate-mask-in\"\n style={{ zIndex: maskZIndex }}\n onClick={maskClosable ? () => onClose?.() : undefined}\n />\n )}\n <DialogPrimitive.Content\n ref={contentRef}\n tabIndex={-1}\n role=\"dialog\"\n data-testid={dataTestId}\n aria-modal={isTop || undefined}\n aria-label={ariaLabel}\n {...(description ? {} : { 'aria-describedby': undefined })}\n {...(!isTop ? { inert: '' } : {})}\n aria-hidden={!isTop || undefined}\n className={cn(\n 'ald-drawer tw-pointer-events-auto tw-absolute tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-[var(--elevation-bottom-bottom-xl)]',\n placementStyles[placement],\n slideAnimationStyles[placement],\n className,\n )}\n style={{\n zIndex: contentZIndex,\n ...sizeStyle,\n ...contentWrapperStyle,\n ...style,\n }}\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n requestAnimationFrame(() => {\n const drawer = contentRef.current;\n if (drawer) focusInitialDialogTarget(drawer);\n });\n }}\n onEscapeKeyDown={(event) => {\n if (!event.defaultPrevented) markDialogEscapeEvent(event);\n event.preventDefault();\n }}\n onInteractOutside={(event) => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n >\n <ModalLayerProvider id={drawerLayerId}>\n <FloatingLayerProvider value={nextFloatingLevel}>\n {!title && (\n <DialogPrimitive.Title className=\"tw-sr-only\">\n {ariaLabel || 'Drawer'}\n </DialogPrimitive.Title>\n )}\n {(title || description || closable || extra) && (\n <>\n {/* antd 兼容:保留 ant-drawer-header / ant-drawer-header-title class,消费方 CSS 可能依赖这些选择器 */}\n <div\n className=\"ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--background-default)] tw-px-6 tw-pb-0 tw-pt-4\"\n style={headerStyle}\n >\n <div className=\"tw-flex tw-w-full tw-flex-col tw-gap-[6px]\">\n <div className=\"tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]\">\n <DialogPrimitive.Title className='ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]'>\n {title}\n </DialogPrimitive.Title>\n {extra || closable ? (\n <div className=\"ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]\">\n {extra}\n {closable && (\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n className=\"ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-[var(--action-ghost-normal)] hover:tw-bg-[var(--action-ghost-hover)] focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)] forced-colors:focus-visible:tw-outline-[Highlight]\"\n aria-label=\"Close\"\n data-overlay-close=\"true\"\n onClick={() => onClose?.()}\n >\n <CloseLightLine\n size={20}\n color=\"var(--content-secondary)\"\n />\n </button>\n </DialogPrimitive.Close>\n )}\n </div>\n ) : null}\n </div>\n {description ? (\n <DialogPrimitive.Description asChild>\n <div className='ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]'>\n {description}\n </div>\n </DialogPrimitive.Description>\n ) : null}\n </div>\n </div>\n </>\n )}\n {/* antd 兼容:保留 ant-drawer-body class */}\n <div\n className=\"ald-drawer-body ant-drawer-body tw-min-w-0 tw-flex-[1_0_0] tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]\"\n style={bodyStyle}\n >\n {children}\n </div>\n {footer && (\n <>\n <div className=\"ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]\" />\n {/* antd 兼容:antd .ant-drawer-footer 使用 text-align:right 让 inline 按钮右对齐,\n 此处加 tw-text-right 保持消费方传入 width:100% 子容器时 inline 元素仍右对齐。 */}\n <div\n className=\"ald-drawer-footer ant-drawer-footer tw-flex tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]\"\n style={footerStyle}\n >\n {footer}\n </div>\n </>\n )}\n </FloatingLayerProvider>\n </ModalLayerProvider>\n </DialogPrimitive.Content>\n </div>\n );\n\n // modal={false}:禁用 Radix FocusScope 的 MutationObserver,\n // 避免表单校验触发 DOM 变动时劫持焦点。手动补偿滚动锁定、aria-hidden、Tab 循环。\n return (\n <DialogPrimitive.Root open={open} modal={false}>\n {inline ? (\n open ? (\n drawerNode\n ) : null\n ) : (\n <DialogPrimitive.Portal container={portalContainer}>\n {drawerNode}\n </DialogPrimitive.Portal>\n )}\n </DialogPrimitive.Root>\n );\n}\n\nexport default Drawer;\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { FloatingFocusManagerProps, OffsetOptions, UseHoverProps } from '@floating-ui/react';
2
2
  import { default as React } from 'react';
3
3
  import { MenuProps } from '../Menu';
4
- export type ActionType = 'hover' | 'click';
4
+ export type ActionType = 'hover' | 'click' | 'contextMenu';
5
5
  export type PlacementType = 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
6
6
  export interface IDropdownProps {
7
7
  children: React.ReactNode;
@@ -3,13 +3,13 @@ import { useFloatingPopupZIndex } from "../_utils/floatingLayer.js";
3
3
  /* empty css */
4
4
  import Menu from "../Menu/index.js";
5
5
  import { ensureWeakRefFallback } from "../_utils/weakRefFallback.js";
6
- import { cloneElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
6
+ import { cloneElement, useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  import ReactDOM from "react-dom";
9
- import { FloatingFocusManager, FloatingNode, FloatingTree, autoUpdate, flip, offset, safePolygon, shift, size, useClick, useDismiss, useFloating, useFloatingNodeId, useFloatingParentNodeId, useHover, useId as useId$1, useInteractions, useRole } from "@floating-ui/react";
9
+ import { FloatingFocusManager, FloatingNode, FloatingTree, autoUpdate, flip, offset, safePolygon, shift, size, useClick, useDismiss, useFloating, useFloatingNodeId, useFloatingParentNodeId, useHover, useId as useId$1, useInteractions, useTransitionStyles } from "@floating-ui/react";
10
10
  import { useMemoizedFn } from "ahooks";
11
11
  //#region src/Dropdown/index.tsx
12
- var OVERLAY_EXIT_ANIMATION_MS = 200;
12
+ var OVERLAY_TRANSITION_MS = 200;
13
13
  ensureWeakRefFallback();
14
14
  function resolvePlacementSide(currentPlacement) {
15
15
  const [side] = currentPlacement.split("-");
@@ -42,83 +42,29 @@ function hasTrigger(trigger, action) {
42
42
  function Dropdown(props) {
43
43
  const { children, destroyPopupOnHide = true, getPopupContainer, menu, overlayClassName, placement = "bottom-start", trigger = "click", open, onOpenChange = () => {}, closeOnFocusOut = true, overlayStyle, overlayInnerStyle, dropdownRender, disabled, offset: offsetProps = 4, delay = 0, autoUpdatePos = false, initialFocus = -1, popupMatchTriggerWidth = false, allowOverlap = false } = props;
44
44
  const [isOpen, setIsOpen] = useState(open || false);
45
- const [isAnimatingOut, setIsAnimatingOut] = useState(false);
46
- const closeAnimationTimerRef = useRef(null);
47
45
  const lastResolvedFloatingStylesRef = useRef(null);
48
46
  const lastResolvedSideRef = useRef(resolvePlacementSide(placement));
49
47
  const currentFloatingStylesRef = useRef(null);
50
48
  const currentFloatingSideRef = useRef(resolvePlacementSide(placement));
51
49
  const [targetElement, setTargetElement] = useState(null);
50
+ const [menuInitialFocus, setMenuInitialFocus] = useState("first");
52
51
  const popupMatchTriggerWidthNumber = parsePopupMatchWidth(popupMatchTriggerWidth);
53
52
  const [matchedTriggerWidth, setMatchedTriggerWidth] = useState(popupMatchTriggerWidthNumber);
54
53
  const popupZIndex = useFloatingPopupZIndex();
55
54
  const onOpenChangeFn = useMemoizedFn(onOpenChange);
56
55
  const isOpenControlled = open !== void 0;
57
- const clearCloseAnimationTimer = useCallback(() => {
58
- if (closeAnimationTimerRef.current) {
59
- clearTimeout(closeAnimationTimerRef.current);
60
- closeAnimationTimerRef.current = null;
61
- }
62
- }, []);
63
- const stopCloseAnimation = useCallback(() => {
64
- clearCloseAnimationTimer();
65
- document.body.classList.remove("ald-dropdown-root-closing");
66
- setIsAnimatingOut(false);
67
- }, [clearCloseAnimationTimer]);
68
56
  const markRootClosing = useCallback(() => {
69
57
  document.body.classList.add("ald-dropdown-root-closing");
70
58
  lastResolvedFloatingStylesRef.current = currentFloatingStylesRef.current ?? lastResolvedFloatingStylesRef.current;
71
59
  lastResolvedSideRef.current = currentFloatingSideRef.current ?? lastResolvedSideRef.current;
72
60
  }, []);
73
- const startCloseAnimation = useCallback(() => {
74
- clearCloseAnimationTimer();
75
- markRootClosing();
76
- setIsAnimatingOut(true);
77
- closeAnimationTimerRef.current = setTimeout(() => {
78
- document.body.classList.remove("ald-dropdown-root-closing");
79
- setIsAnimatingOut(false);
80
- closeAnimationTimerRef.current = null;
81
- }, OVERLAY_EXIT_ANIMATION_MS);
82
- }, [clearCloseAnimationTimer, markRootClosing]);
83
61
  useLayoutEffect(() => {
84
- if (!isOpenControlled) return;
85
- if (open) {
86
- stopCloseAnimation();
87
- setIsOpen(true);
88
- return;
89
- }
90
- if (isOpen) startCloseAnimation();
91
- setIsOpen(false);
92
- }, [
93
- isOpen,
94
- isOpenControlled,
95
- open,
96
- startCloseAnimation,
97
- stopCloseAnimation
98
- ]);
99
- useEffect(() => {
100
- return () => {
101
- clearCloseAnimationTimer();
102
- };
103
- }, [clearCloseAnimationTimer]);
104
- useEffect(() => {
105
- return () => {
106
- document.body.classList.remove("ald-dropdown-root-closing");
107
- };
108
- }, []);
62
+ if (isOpenControlled) setIsOpen(!!open);
63
+ }, [isOpenControlled, open]);
109
64
  const onChangeOpen = useCallback((newOpen) => {
110
- if (!isOpenControlled) {
111
- if (newOpen) stopCloseAnimation();
112
- else startCloseAnimation();
113
- setIsOpen(newOpen);
114
- }
65
+ if (!isOpenControlled) setIsOpen(newOpen);
115
66
  onOpenChangeFn(newOpen);
116
- }, [
117
- isOpenControlled,
118
- onOpenChangeFn,
119
- startCloseAnimation,
120
- stopCloseAnimation
121
- ]);
67
+ }, [isOpenControlled, onOpenChangeFn]);
122
68
  const nodeId = useFloatingNodeId();
123
69
  const { refs, floatingStyles, context, placement: floatingPlacement, x, y } = useFloating({
124
70
  nodeId,
@@ -128,9 +74,7 @@ function Dropdown(props) {
128
74
  middleware: [
129
75
  offset(allowOverlap ? 0 : offsetProps),
130
76
  flip({
131
- altBoundary: true,
132
77
  padding: 8,
133
- fallbackAxisSideDirection: "end",
134
78
  ...allowOverlap && { fallbackStrategy: "bestFit" }
135
79
  }),
136
80
  shift({
@@ -142,15 +86,32 @@ function Dropdown(props) {
142
86
  ...allowOverlap && { padding: 8 },
143
87
  apply({ availableHeight, elements }) {
144
88
  if (!allowOverlap) return;
145
- Object.assign(elements.floating.style, {
146
- maxHeight: `${Math.max(100, availableHeight)}px`,
147
- overflowY: "auto"
148
- });
89
+ elements.floating.style.setProperty("--ald-dropdown-max-height", `${Math.max(100, availableHeight)}px`);
149
90
  }
150
91
  })
151
92
  ],
152
93
  whileElementsMounted: autoUpdatePos ? autoUpdate : void 0
153
94
  });
95
+ const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
96
+ duration: OVERLAY_TRANSITION_MS,
97
+ initial: {
98
+ opacity: 0,
99
+ transform: "var(--ald-dropdown-enter-transform)"
100
+ },
101
+ close: {
102
+ opacity: 0,
103
+ transform: "var(--ald-dropdown-exit-transform)"
104
+ },
105
+ common: { transitionTimingFunction: "ease-in-out" }
106
+ });
107
+ const isAnimatingOut = !isOpen && isMounted;
108
+ useLayoutEffect(() => {
109
+ if (isAnimatingOut) document.body.classList.add("ald-dropdown-root-closing");
110
+ else document.body.classList.remove("ald-dropdown-root-closing");
111
+ return () => {
112
+ if (isAnimatingOut) document.body.classList.remove("ald-dropdown-root-closing");
113
+ };
114
+ }, [isAnimatingOut]);
154
115
  const click = useClick(context, { enabled: hasTrigger(trigger, "click") });
155
116
  const hover = useHover(context, {
156
117
  enabled: hasTrigger(trigger, "hover"),
@@ -158,9 +119,8 @@ function Dropdown(props) {
158
119
  delay
159
120
  });
160
121
  const dismiss = useDismiss(context, {});
161
- const role = useRole(context);
162
122
  const { getReferenceProps, getFloatingProps } = useInteractions(useMemo(() => {
163
- const res = [dismiss, role];
123
+ const res = [dismiss];
164
124
  if (hasTrigger(trigger, "hover")) res.unshift(hover);
165
125
  if (hasTrigger(trigger, "click")) res.unshift(click);
166
126
  return res;
@@ -168,7 +128,6 @@ function Dropdown(props) {
168
128
  trigger,
169
129
  click,
170
130
  dismiss,
171
- role,
172
131
  hover
173
132
  ]));
174
133
  const headingId = useId$1();
@@ -221,15 +180,58 @@ function Dropdown(props) {
221
180
  return currentWidth === nextWidth ? currentWidth : nextWidth;
222
181
  });
223
182
  }, [popupMatchTriggerWidth, popupMatchTriggerWidthNumber]);
183
+ const openMenuFromKeyboard = useCallback((event, position) => {
184
+ if (disabled) return;
185
+ event.preventDefault();
186
+ refs.setPositionReference(event.currentTarget);
187
+ updateMatchedWidthFromElement(event.currentTarget);
188
+ setMenuInitialFocus(position);
189
+ onChangeOpen(true);
190
+ }, [
191
+ disabled,
192
+ onChangeOpen,
193
+ refs,
194
+ updateMatchedWidthFromElement
195
+ ]);
224
196
  const modifiedChild = cloneElement(child, {
225
197
  ...childProps,
226
198
  disabled,
227
199
  ...referenceProps,
200
+ "aria-haspopup": menu ? "menu" : void 0,
201
+ "aria-expanded": menu ? isOpen : void 0,
228
202
  onClick: (event) => {
203
+ refs.setPositionReference(event.currentTarget);
229
204
  updateMatchedWidthFromElement(event.currentTarget);
205
+ setMenuInitialFocus("first");
230
206
  childProps.onClick?.(event);
231
207
  const { onClick: referenceOnClick } = referenceProps;
232
208
  if (typeof referenceOnClick === "function") referenceOnClick(event);
209
+ },
210
+ onKeyDown: (event) => {
211
+ childProps.onKeyDown?.(event);
212
+ if (event.defaultPrevented) return;
213
+ if (event.key === "ArrowDown") {
214
+ openMenuFromKeyboard(event, "first");
215
+ return;
216
+ }
217
+ if (event.key === "ArrowUp") {
218
+ openMenuFromKeyboard(event, "last");
219
+ return;
220
+ }
221
+ if (event.key === "Enter" || event.key === " ") {
222
+ openMenuFromKeyboard(event, "first");
223
+ return;
224
+ }
225
+ const { onKeyDown: referenceOnKeyDown } = referenceProps;
226
+ if (typeof referenceOnKeyDown === "function") referenceOnKeyDown(event);
227
+ },
228
+ onContextMenu: (event) => {
229
+ childProps.onContextMenu?.(event);
230
+ if (event.defaultPrevented || disabled || !hasTrigger(trigger, "contextMenu")) return;
231
+ event.preventDefault();
232
+ refs.setPositionReference({ getBoundingClientRect: () => new DOMRect(event.clientX, event.clientY, 0, 0) });
233
+ setMenuInitialFocus("first");
234
+ onChangeOpen(true);
233
235
  }
234
236
  });
235
237
  const onMenuItemClick = useCallback((info) => {
@@ -240,6 +242,15 @@ function Dropdown(props) {
240
242
  }
241
243
  onChangeOpen(false);
242
244
  }, [menu, onChangeOpen]);
245
+ const onMenuRequestClose = useCallback((reason) => {
246
+ if (reason === "blur" && !closeOnFocusOut) return;
247
+ onChangeOpen(false);
248
+ if (reason === "cancel") window.setTimeout(() => targetElement?.focus(), 0);
249
+ }, [
250
+ closeOnFocusOut,
251
+ onChangeOpen,
252
+ targetElement
253
+ ]);
243
254
  const menuInstance = useMemo(() => {
244
255
  return /* @__PURE__ */ jsx(Menu, {
245
256
  ...menu,
@@ -254,6 +265,8 @@ function Dropdown(props) {
254
265
  },
255
266
  onBeforeLeafItemClick: isOpenControlled ? void 0 : markRootClosing,
256
267
  rootClosing: isAnimatingOut,
268
+ initialFocus: menuInitialFocus,
269
+ onRequestClose: onMenuRequestClose,
257
270
  onClick: onMenuItemClick,
258
271
  externalOverflow: allowOverlap
259
272
  });
@@ -264,7 +277,9 @@ function Dropdown(props) {
264
277
  markRootClosing,
265
278
  menu,
266
279
  onMenuItemClick,
267
- popupMatchTriggerWidth
280
+ onMenuRequestClose,
281
+ popupMatchTriggerWidth,
282
+ menuInitialFocus
268
283
  ]);
269
284
  const popupElement = useMemo(() => {
270
285
  return typeof dropdownRender === "function" ? dropdownRender(menuInstance) : menuInstance;
@@ -280,7 +295,7 @@ function Dropdown(props) {
280
295
  maxWidth: "none"
281
296
  } : void 0;
282
297
  const floatingSide = resolvePlacementSide(String(floatingPlacement));
283
- const shouldKeepMounted = !destroyPopupOnHide || isOpen || isAnimatingOut;
298
+ const shouldKeepMounted = !destroyPopupOnHide || isMounted;
284
299
  const isPositionReady = x !== null && y !== null;
285
300
  const overlayHidden = !isOpen && !isAnimatingOut || isOpen && !isPositionReady;
286
301
  const resolvedFloatingStyles = isAnimatingOut ? lastResolvedFloatingStylesRef.current ?? floatingStyles : floatingStyles;
@@ -292,34 +307,35 @@ function Dropdown(props) {
292
307
  lastResolvedSideRef.current = floatingSide;
293
308
  }
294
309
  const renderFloatingContent = useCallback(() => {
295
- const surface = /* @__PURE__ */ jsx("div", {
296
- ...isAnimatingOut ? {} : getFloatingProps(),
297
- className: cn("ald-dropdown-overlay", "tw-pointer-events-auto tw-z-[1001] tw-max-w-none tw-outline-none", overlayClassName, { "ald-dropdown-overlay-hidden": overlayHidden }),
298
- ref: refs.setFloating,
299
- style: {
300
- zIndex: popupZIndex,
301
- ...resolvedFloatingStyles,
302
- ...matchedOverlayStyle,
303
- ...overlayStyle
304
- },
305
- "aria-labelledby": headingId,
306
- children: /* @__PURE__ */ jsx("div", {
307
- className: cn("ald-dropdown-surface", "tw-flex tw-flex-col tw-items-start tw-text-sm"),
308
- style: {
309
- ...matchedSurfaceStyle,
310
- ...overlayInnerStyle
311
- },
312
- "data-state": isOpen ? "open" : "closed",
313
- "data-side": resolvedFloatingSide,
314
- children: popupElement
315
- })
316
- });
317
- const popupElem = isAnimatingOut && !isOpen ? surface : /* @__PURE__ */ jsx(FloatingFocusManager, {
310
+ const popupElem = /* @__PURE__ */ jsx(FloatingFocusManager, {
318
311
  context,
312
+ disabled: isAnimatingOut,
319
313
  modal: false,
320
314
  initialFocus,
321
315
  closeOnFocusOut,
322
- children: surface
316
+ children: /* @__PURE__ */ jsx("div", {
317
+ ...isAnimatingOut ? {} : getFloatingProps(),
318
+ className: cn("ald-dropdown-overlay", "tw-pointer-events-auto tw-z-[1001] tw-max-w-none tw-outline-none", overlayClassName, { "ald-dropdown-overlay-hidden": overlayHidden }),
319
+ ref: refs.setFloating,
320
+ style: {
321
+ zIndex: popupZIndex,
322
+ ...resolvedFloatingStyles,
323
+ ...matchedOverlayStyle,
324
+ ...overlayStyle
325
+ },
326
+ "aria-labelledby": headingId,
327
+ children: /* @__PURE__ */ jsx("div", {
328
+ className: cn("ald-dropdown-surface", allowOverlap && "ald-dropdown-surface-overlap", "tw-flex tw-flex-col tw-items-start tw-text-sm"),
329
+ style: {
330
+ ...matchedSurfaceStyle,
331
+ ...overlayInnerStyle,
332
+ ...transitionStyles
333
+ },
334
+ "data-state": isOpen ? "open" : "closed",
335
+ "data-side": resolvedFloatingSide,
336
+ children: popupElement
337
+ })
338
+ })
323
339
  });
324
340
  const popupContainer = typeof getPopupContainer === "function" ? getPopupContainer() : document.body;
325
341
  return ReactDOM.createPortal(popupElem, popupContainer);
@@ -335,6 +351,7 @@ function Dropdown(props) {
335
351
  overlayInnerStyle,
336
352
  matchedOverlayStyle,
337
353
  matchedSurfaceStyle,
354
+ transitionStyles,
338
355
  popupZIndex,
339
356
  overlayHidden,
340
357
  isAnimatingOut,
@@ -342,7 +359,8 @@ function Dropdown(props) {
342
359
  initialFocus,
343
360
  closeOnFocusOut,
344
361
  resolvedFloatingSide,
345
- resolvedFloatingStyles
362
+ resolvedFloatingStyles,
363
+ allowOverlap
346
364
  ]);
347
365
  const popup = shouldKeepMounted ? renderFloatingContent() : null;
348
366
  const { setReference } = refs;