@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.
- package/dist/AProgress/index.js +10 -0
- package/dist/AProgress/index.js.map +1 -1
- package/dist/Alert/index.js +3 -1
- package/dist/Alert/index.js.map +1 -1
- package/dist/Badge/index.js +37 -11
- package/dist/Badge/index.js.map +1 -1
- package/dist/Breadcrumb/index.js +12 -2
- package/dist/Breadcrumb/index.js.map +1 -1
- package/dist/Button/index.d.ts +1 -1
- package/dist/Button/index.js +29 -9
- package/dist/Button/index.js.map +1 -1
- package/dist/Card/index.js +1 -1
- package/dist/Card/index.js.map +1 -1
- package/dist/Carousel/index.js +9 -1
- package/dist/Carousel/index.js.map +1 -1
- package/dist/Checkbox/index.js +25 -4
- package/dist/Checkbox/index.js.map +1 -1
- package/dist/Checkbox/type.d.ts +10 -2
- package/dist/Collapse/index.js +68 -22
- package/dist/Collapse/index.js.map +1 -1
- package/dist/ColorPicker/index.js +112 -46
- package/dist/ColorPicker/index.js.map +1 -1
- package/dist/DataPreviewTable/components/Body/Cell.d.ts +2 -0
- package/dist/DataPreviewTable/components/Body/Cell.js +9 -1
- package/dist/DataPreviewTable/components/Body/Cell.js.map +1 -1
- package/dist/DataPreviewTable/components/Body/index.d.ts +1 -0
- package/dist/DataPreviewTable/components/Body/index.js +35 -23
- package/dist/DataPreviewTable/components/Body/index.js.map +1 -1
- package/dist/DataPreviewTable/components/Header/index.d.ts +1 -0
- package/dist/DataPreviewTable/components/Header/index.js +36 -28
- package/dist/DataPreviewTable/components/Header/index.js.map +1 -1
- package/dist/DataPreviewTable/index.js +19 -2
- package/dist/DataPreviewTable/index.js.map +1 -1
- package/dist/Drawer/index.d.ts +4 -0
- package/dist/Drawer/index.js +91 -78
- package/dist/Drawer/index.js.map +1 -1
- package/dist/Dropdown/index.d.ts +1 -1
- package/dist/Dropdown/index.js +116 -98
- package/dist/Dropdown/index.js.map +1 -1
- package/dist/Form/FormItemContext.d.ts +1 -1
- package/dist/Form/FormItemContext.js.map +1 -1
- package/dist/Form/index.js +50 -7
- package/dist/Form/index.js.map +1 -1
- package/dist/IconButton/index.d.ts +2 -2
- package/dist/IconButton/index.js +9 -3
- package/dist/IconButton/index.js.map +1 -1
- package/dist/Input/components/Input/index.js +23 -7
- package/dist/Input/components/Input/index.js.map +1 -1
- package/dist/Input/components/TextArea/index.js +20 -2
- package/dist/Input/components/TextArea/index.js.map +1 -1
- package/dist/Input/privateSemanticOwner.d.ts +2 -0
- package/dist/Input/privateSemanticOwner.js +1 -0
- package/dist/InputNumber/index.js +19 -5
- package/dist/InputNumber/index.js.map +1 -1
- package/dist/InputNumber/type.d.ts +2 -2
- package/dist/LogicTree/components/LogicItem/index.d.ts +2 -1
- package/dist/LogicTree/components/LogicItem/index.js +13 -2
- package/dist/LogicTree/components/LogicItem/index.js.map +1 -1
- package/dist/LogicTree/index.js +2 -0
- package/dist/LogicTree/index.js.map +1 -1
- package/dist/MemberPicker/components/MemberSelection.d.ts +1 -0
- package/dist/MemberPicker/components/MemberSelection.js +83 -94
- package/dist/MemberPicker/components/MemberSelection.js.map +1 -1
- package/dist/MemberPicker/components/MultipleOption.js +12 -0
- package/dist/MemberPicker/components/MultipleOption.js.map +1 -1
- package/dist/MemberPicker/components/Panel.d.ts +1 -0
- package/dist/MemberPicker/components/Panel.js +4 -1
- package/dist/MemberPicker/components/Panel.js.map +1 -1
- package/dist/MemberPicker/components/SelectedMemberTags.js +14 -6
- package/dist/MemberPicker/components/SelectedMemberTags.js.map +1 -1
- package/dist/MemberPicker/components/SingleOption.js +12 -0
- package/dist/MemberPicker/components/SingleOption.js.map +1 -1
- package/dist/MemberPicker/components/UserGroupSelection.d.ts +2 -1
- package/dist/MemberPicker/components/UserGroupSelection.js +61 -48
- package/dist/MemberPicker/components/UserGroupSelection.js.map +1 -1
- package/dist/MemberPicker/index.js +134 -108
- package/dist/MemberPicker/index.js.map +1 -1
- package/dist/Menu/index.d.ts +8 -0
- package/dist/Menu/index.js +33 -3
- package/dist/Menu/index.js.map +1 -1
- package/dist/Modal/index.d.ts +4 -0
- package/dist/Modal/index.js +55 -66
- package/dist/Modal/index.js.map +1 -1
- package/dist/OverflowCount/index.js +10 -0
- package/dist/OverflowCount/index.js.map +1 -1
- package/dist/Pagination/PageSizeChanger.d.ts +1 -0
- package/dist/Pagination/PageSizeChanger.js +3 -2
- package/dist/Pagination/PageSizeChanger.js.map +1 -1
- package/dist/Pagination/index.js +34 -19
- package/dist/Pagination/index.js.map +1 -1
- package/dist/Pagination/types.d.ts +3 -0
- package/dist/Popconfirm/index.js +9 -1
- package/dist/Popconfirm/index.js.map +1 -1
- package/dist/Popover/index.js +52 -1
- package/dist/Popover/index.js.map +1 -1
- package/dist/Progress/index.js +4 -0
- package/dist/Progress/index.js.map +1 -1
- package/dist/Radio/components/Group/index.d.ts +5 -1
- package/dist/Radio/components/Group/index.js +7 -2
- package/dist/Radio/components/Group/index.js.map +1 -1
- package/dist/Radio/components/Radio/index.js +27 -1
- package/dist/Radio/components/Radio/index.js.map +1 -1
- package/dist/Radio/interface/radio.d.ts +7 -0
- package/dist/Radio/interface/radioGroup.d.ts +2 -0
- package/dist/RenameInput/index.js +11 -0
- package/dist/RenameInput/index.js.map +1 -1
- package/dist/Select/BaseSelect.d.ts +3 -0
- package/dist/Select/BaseSelect.js +235 -38
- package/dist/Select/BaseSelect.js.map +1 -1
- package/dist/Select/SelectTrigger.d.ts +2 -0
- package/dist/Select/SelectTrigger.js +13 -5
- package/dist/Select/SelectTrigger.js.map +1 -1
- package/dist/Select/Selector/FixedOverflow.d.ts +1 -0
- package/dist/Select/Selector/FixedOverflow.js +3 -3
- package/dist/Select/Selector/FixedOverflow.js.map +1 -1
- package/dist/Select/Selector/MultipleSelector.js +1 -0
- package/dist/Select/Selector/MultipleSelector.js.map +1 -1
- package/dist/Select/components/OptionCollection.d.ts +41 -0
- package/dist/Select/components/OptionCollection.js +124 -0
- package/dist/Select/components/OptionCollection.js.map +1 -0
- package/dist/Select/index.js +45 -3
- package/dist/Select/index.js.map +1 -1
- package/dist/Select/interface.d.ts +1 -1
- package/dist/Skeleton/index.js +1 -1
- package/dist/Skeleton/index.js.map +1 -1
- package/dist/Spin/index.d.ts +1 -1
- package/dist/Spin/index.js +22 -7
- package/dist/Spin/index.js.map +1 -1
- package/dist/Steps/index.js +66 -14
- package/dist/Steps/index.js.map +1 -1
- package/dist/Switch/index.d.ts +5 -0
- package/dist/Switch/index.js +22 -5
- package/dist/Switch/index.js.map +1 -1
- package/dist/Table/components/Cell.js +8 -3
- package/dist/Table/components/Cell.js.map +1 -1
- package/dist/Table/components/ExpandCell.js +6 -2
- package/dist/Table/components/ExpandCell.js.map +1 -1
- package/dist/Table/components/Footer/index.js +2 -1
- package/dist/Table/components/Footer/index.js.map +1 -1
- package/dist/Table/components/Header.js +25 -18
- package/dist/Table/components/Header.js.map +1 -1
- package/dist/Table/hooks/useRowSelection.js +14 -7
- package/dist/Table/hooks/useRowSelection.js.map +1 -1
- package/dist/Table/index.js +50 -29
- package/dist/Table/index.js.map +1 -1
- package/dist/Tabs/index.d.ts +1 -1
- package/dist/Tabs/index.js +92 -11
- package/dist/Tabs/index.js.map +1 -1
- package/dist/Tag/index.js +20 -1
- package/dist/Tag/index.js.map +1 -1
- package/dist/Tooltip/index.js +34 -5
- package/dist/Tooltip/index.js.map +1 -1
- package/dist/Tour/index.js +13 -1
- package/dist/Tour/index.js.map +1 -1
- package/dist/Tree/Tree2.js +2 -0
- package/dist/Tree/Tree2.js.map +1 -1
- package/dist/_utils/dialogAccessibility.d.ts +17 -0
- package/dist/_utils/dialogAccessibility.js +110 -0
- package/dist/_utils/dialogAccessibility.js.map +1 -0
- package/dist/_utils/formControlSemanticOwner.d.ts +3 -0
- package/dist/_utils/formControlSemanticOwner.js +17 -0
- package/dist/_utils/formControlSemanticOwner.js.map +1 -0
- package/dist/_utils/overlayCoordinator.js +1 -11
- package/dist/_utils/overlayCoordinator.js.map +1 -1
- package/dist/aloudata-design.css +1 -1
- package/dist/locale/default.d.ts +6 -0
- package/dist/locale/en_US.d.ts +6 -0
- package/dist/locale/en_US.js +6 -0
- package/dist/locale/en_US.js.map +1 -1
- package/dist/locale/zh_CN.d.ts +6 -0
- package/dist/locale/zh_CN.js +6 -0
- package/dist/locale/zh_CN.js.map +1 -1
- package/dist/theme/contract/baselineResolvedTokenValueManifest.d.ts +1 -1
- package/dist/theme/contract/controlStateColorArtifact.d.ts +73 -0
- package/dist/theme/contract/controlStateColorArtifact.js +2 -0
- package/dist/theme/contract/darkResolvedTokenValueManifest.d.ts +1 -1
- package/dist/theme/contract/focusRingArtifact.d.ts +51 -0
- package/dist/theme/contract/focusRingArtifact.js +2 -0
- package/dist/theme/contract/tokenOutputContract.d.ts +1 -1
- package/dist/theme/contract/tokenOutputContract.js +4 -0
- package/dist/theme/contract/tokenOutputContract.js.map +1 -1
- package/dist/theme/generator/acceptedColorValues.d.ts +2 -1
- package/dist/theme/generator/baselineTokenValueDelivery.d.ts +9 -1
- package/dist/theme/generator/minimalAcceptedColorValues.d.ts +2 -2
- package/dist/theme/generator/neutralSourceResolver.d.ts +28 -0
- package/dist/theme/generator/neutralSourceResolver.js +2 -0
- package/dist/theme/generator/neutralSourceValues.d.ts +27 -0
- package/dist/theme/generator/neutralSourceValues.js +2 -0
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.js +1 -0
- package/dist/theme/neutralSourceContract.d.ts +3 -0
- package/dist/theme/neutralSourceContract.js +11 -0
- package/dist/theme/neutralSourceContract.js.map +1 -0
- package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.d.ts +1 -1
- package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.js +47 -44
- package/dist/theme/runtime/colorValueMode/generated/darkBaselineDeclarations.js.map +1 -1
- package/dist/theme/runtime/colorValueMode/generated/slateDarkDeclarations.d.ts +3 -0
- package/dist/theme/runtime/colorValueMode/generated/slateDarkDeclarations.js +2 -0
- package/dist/theme/runtime/colorValueMode/generated/slateLightDeclarations.d.ts +3 -0
- package/dist/theme/runtime/colorValueMode/generated/slateLightDeclarations.js +2 -0
- package/dist/theme/runtime/colorValueMode/generated/stoneDarkDeclarations.d.ts +3 -0
- package/dist/theme/runtime/colorValueMode/generated/stoneDarkDeclarations.js +2 -0
- package/dist/theme/runtime/colorValueMode/generated/stoneLightDeclarations.d.ts +3 -0
- package/dist/theme/runtime/colorValueMode/generated/stoneLightDeclarations.js +2 -0
- package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.d.ts +1 -1
- package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.js +43 -40
- package/dist/theme/runtime/colorValueMode/generated/v3BaselineDeclarations.js.map +1 -1
- package/dist/theme/runtime/colorValueMode/index.js +2 -12
- package/dist/theme/runtime/colorValueMode/index.js.map +1 -1
- package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.d.ts +2 -0
- package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.js +48 -0
- package/dist/theme/runtime/colorValueMode/legacyNeutralCompatibility.js.map +1 -0
- package/dist/theme/runtime/colorValueMode/registry.d.ts +1 -1
- package/dist/theme/runtime/colorValueMode/registry.js +2 -1
- package/dist/theme/runtime/colorValueMode/registry.js.map +1 -1
- package/dist/theme/runtime/initAldTheme.js +1 -3
- package/dist/theme/runtime/initAldTheme.js.map +1 -1
- package/dist/theme/runtime/neutralSource/index.d.ts +6 -0
- package/dist/theme/runtime/neutralSource/index.js +11 -0
- package/dist/theme/runtime/neutralSource/index.js.map +1 -0
- package/dist/theme/runtime/neutralSource.d.ts +2 -0
- package/dist/theme/runtime/themeToCssVars.js +3 -0
- package/dist/theme/runtime/themeToCssVars.js.map +1 -1
- package/package.json +1 -1
package/dist/Drawer/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
2
|
import Memo from "../Icon/components/CloseLightLine.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
95
|
-
|
|
96
|
-
children:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
})
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
className: "ald-drawer-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
package/dist/Drawer/index.js.map
CHANGED
|
@@ -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":";;;;;;;;;AA6CA,IAAM,cAAsC;CAC1C,SAAS;CACT,OAAO;CACR;AAED,IAAM,kBAA0C;CAC9C,OAAO;CACP,MAAM;CACN,KAAK;CACL,QAAQ;CACT;AAED,IAAM,uBAA+C;CACnD,OACE;CACF,MAAM;CACN,KAAK;CACL,QACE;CACH;AAED,SAAS,OAAO,OAAoB;CAClC,MAAM,EACJ,OAAO,OACP,SACA,OACA,aACA,YAAY,SACZ,OACA,QACA,OAAO,WACP,WAAW,MACX,OAAO,MACP,eAAe,MACf,WACA,eACA,OACA,qBACA,WACA,aACA,QACA,aACA,OACA,UACA,SAAS,KACT,iBACE;CAEJ,MAAM,aAAa,OAAuB,KAAK;CAC/C,MAAM,CAAC,iBAAiB,sBAAsB,UAE3C;CACH,MAAM,SAAS,iBAAiB;AAChC,mBAAkB,KAAK;AAEvB,uBAAsB;AACpB,MAAI,iBAAiB,UAAa,iBAAiB,MACjD,oBAAmB,OAAU;WACpB,OAAO,iBAAiB,SACjC,oBAAmB,SAAS,cAAc,aAAa,IAAI,OAAU;WAC5D,OAAO,iBAAiB,WACjC,oBAAmB,cAAc,CAAC;MAElC,oBAAmB,aAAa;IAEjC,CAAC,aAAa,CAAC;AAGlB,iBAAgB;AACd,MAAI,CAAC,QAAQ,CAAC,WAAW,QAAS;AAClC,SAAO,WAAW,WAAW,QAAQ;IACpC,CAAC,KAAK,CAAC;AAGV,iBAAgB;AACd,MAAI,CAAC,QAAQ,CAAC,WAAW,QAAS;EAClC,MAAM,YAAY,WAAW;EAC7B,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,EAAE,QAAQ,MAAO;GACrB,MAAM,YAAY,UAAU,iBAC1B,8JACD;AACD,OAAI,UAAU,WAAW,EAAG;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,OAAO,UAAU,UAAU,SAAS;AAC1C,OAAI,EAAE,UACJ;QACE,SAAS,kBAAkB,SAC3B,CAAC,UAAU,SAAS,SAAS,cAAc,EAC3C;AACA,OAAE,gBAAgB;AAClB,UAAK,OAAO;;cAIZ,SAAS,kBAAkB,QAC3B,CAAC,UAAU,SAAS,SAAS,cAAc,EAC3C;AACA,MAAE,gBAAgB;AAClB,UAAM,OAAO;;;AAInB,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa,SAAS,oBAAoB,WAAW,cAAc;IAClE,CAAC,KAAK,CAAC;CAEV,MAAM,eAAe,cAAc,UAAU,cAAc;CAC3D,MAAM,SAAS,YAAY,SAAS,YAAY;CAGhD,MAAM,YAAY,eACd;EAAE,OAHiB,SAAS;EAGH,UAAU;EAAS,GAC5C;EAAE,QAHkB,UAAU;EAGH,WAAW;EAAS;CACnD,MAAM,EAAE,YAAY,eAAe,cAAc,iBAAiB,OAAO;CAEzE,MAAM,aACJ,qBAAC,OAAD;EACE,WAAW,GACT,qDACA,SAAS,gBAAgB,YACzB,cACD;YALH,CAQG,QACC,oBAAC,OAAD;GACE,cAAY,OAAO,SAAS;GAC5B,WAAU;GACV,OAAO,EAAE,QAAQ,YAAY;GAC7B,SAAS,qBAAqB,WAAW,GAAG;GAC5C,CAAA,EAEJ,oBAAC,gBAAgB,SAAjB;GACE,KAAK;GACL,WAAW,GACT,oHACA,gBAAgB,YAChB,qBAAqB,YACrB,UACD;GACD,OAAO;IACL,QAAQ;IACR,GAAG;IACH,GAAG;IACH,GAAG;IACJ;GACD,uBAAuB,WAAW;GAClC,oBAAoB,UAAU;AAC5B,QAAI,CAAC,aACH,OAAM,gBAAgB;;aAI1B,qBAAC,uBAAD;IAAuB,OAAO;cAA9B;MACI,SAAS,eAAe,YAAY,UACpC,oBAAA,UAAA,EAAA,UAEE,oBAAC,OAAD;MACE,WAAU;MACV,OAAO;gBAEP,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,oBAAC,gBAAgB,OAAjB;SAAuB,WAAU;mBAC9B;SACqB,CAAA,EACvB,SAAS,WACR,qBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,OACA,YACC,oBAAC,gBAAgB,OAAjB;UAAuB,SAAA;oBACrB,oBAAC,UAAD;WACE,MAAK;WACL,WAAU;WACV,eAAe,WAAW;qBAE1B,oBAAC,MAAD;YACE,MAAM;YACN,OAAM;YACN,CAAA;WACK,CAAA;UACa,CAAA,CAEtB;aACJ,KACA;WACL,cACC,oBAAC,OAAD;QAAK,WAAU;kBACZ;QACG,CAAA,GACJ,KACA;;MACF,CAAA,EACL,CAAA;KAGL,oBAAC,OAAD;MACE,WAAU;MACV,OAAO;MAEN;MACG,CAAA;KACL,UACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD,EAAK,WAAU,mEAAoE,CAAA,EAGnF,oBAAC,OAAD;MACE,WAAU;MACV,OAAO;gBAEN;MACG,CAAA,CACL,EAAA,CAAA;KAEiB;;GACA,CAAA,CACtB;;AAKR,QACE,oBAAC,gBAAgB,MAAjB;EACQ;EACN,OAAO;EACP,eAAe,QAAQ;AACrB,OAAI,CAAC,IAAK,YAAW;;YAGtB,SACC,OACE,aACE,OAEJ,oBAAC,gBAAgB,QAAjB;GAAwB,WAAW;aAChC;GACsB,CAAA;EAEN,CAAA"}
|
|
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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"}
|
package/dist/Dropdown/index.d.ts
CHANGED
|
@@ -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;
|
package/dist/Dropdown/index.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
|
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 (
|
|
85
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 ||
|
|
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
|
|
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:
|
|
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;
|