This commit is contained in:
2026-05-25 17:08:18 +08:00
parent df501f6151
commit f1259b71b5
9178 changed files with 1626125 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
import type { TriggerProps, TriggerRef } from '@rc-component/trigger';
import type { ActionType, AlignType, AnimationType, BuildInPlacements } from '@rc-component/trigger/lib/interface';
import React from 'react';
import Placements from './placements';
export interface DropdownProps extends Pick<TriggerProps, 'getPopupContainer' | 'children' | 'mouseEnterDelay' | 'mouseLeaveDelay' | 'onPopupAlign' | 'builtinPlacements' | 'autoDestroy'> {
minOverlayWidthMatchTrigger?: boolean;
arrow?: boolean;
onVisibleChange?: (visible: boolean) => void;
onOverlayClick?: (e: Event) => void;
prefixCls?: string;
transitionName?: string;
overlayClassName?: string;
openClassName?: string;
animation?: AnimationType;
align?: AlignType;
overlayStyle?: React.CSSProperties;
placement?: keyof typeof Placements;
placements?: BuildInPlacements;
overlay?: (() => React.ReactElement) | React.ReactElement;
trigger?: ActionType | ActionType[];
alignPoint?: boolean;
showAction?: ActionType[];
hideAction?: ActionType[];
visible?: boolean;
autoFocus?: boolean;
}
declare const Dropdown: React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<TriggerRef>>;
export default Dropdown;
+122
View File
@@ -0,0 +1,122 @@
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import Trigger from '@rc-component/trigger';
import { composeRef, getNodeRef, supportRef } from "@rc-component/util/es/ref";
import { clsx } from 'clsx';
import React from 'react';
import useAccessibility from "./hooks/useAccessibility";
import Overlay from "./Overlay";
import Placements from "./placements";
const Dropdown = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
arrow = false,
prefixCls = 'rc-dropdown',
transitionName,
animation,
align,
placement = 'bottomLeft',
placements = Placements,
getPopupContainer,
showAction,
hideAction,
overlayClassName,
overlayStyle,
visible,
trigger = ['hover'],
autoFocus,
overlay,
children,
onVisibleChange,
...otherProps
} = props;
const [triggerVisible, setTriggerVisible] = React.useState();
const mergedVisible = 'visible' in props ? visible : triggerVisible;
const mergedMotionName = animation ? `${prefixCls}-${animation}` : transitionName;
const triggerRef = React.useRef(null);
const overlayRef = React.useRef(null);
const childRef = React.useRef(null);
React.useImperativeHandle(ref, () => triggerRef.current);
const handleVisibleChange = newVisible => {
setTriggerVisible(newVisible);
onVisibleChange?.(newVisible);
};
useAccessibility({
visible: mergedVisible,
triggerRef: childRef,
onVisibleChange: handleVisibleChange,
autoFocus,
overlayRef
});
const onClick = e => {
const {
onOverlayClick
} = props;
setTriggerVisible(false);
if (onOverlayClick) {
onOverlayClick(e);
}
};
const getMenuElement = () => /*#__PURE__*/React.createElement(Overlay, {
ref: overlayRef,
overlay: overlay,
prefixCls: prefixCls,
arrow: arrow
});
const getMenuElementOrLambda = () => {
if (typeof overlay === 'function') {
return getMenuElement;
}
return getMenuElement();
};
const getMinOverlayWidthMatchTrigger = () => {
const {
minOverlayWidthMatchTrigger,
alignPoint
} = props;
if ('minOverlayWidthMatchTrigger' in props) {
return minOverlayWidthMatchTrigger;
}
return !alignPoint;
};
const getOpenClassName = () => {
const {
openClassName
} = props;
if (openClassName !== undefined) {
return openClassName;
}
return `${prefixCls}-open`;
};
const childrenNode = /*#__PURE__*/React.cloneElement(children, {
className: clsx(children.props?.className, mergedVisible && getOpenClassName()),
ref: supportRef(children) ? composeRef(childRef, getNodeRef(children)) : undefined
});
let triggerHideAction = hideAction;
if (!triggerHideAction && trigger.indexOf('contextMenu') !== -1) {
triggerHideAction = ['click'];
}
return /*#__PURE__*/React.createElement(Trigger, _extends({
builtinPlacements: placements
}, otherProps, {
prefixCls: prefixCls,
ref: triggerRef,
popupClassName: clsx(overlayClassName, {
[`${prefixCls}-show-arrow`]: arrow
}),
popupStyle: overlayStyle,
action: trigger,
showAction: showAction,
hideAction: triggerHideAction,
popupPlacement: placement,
popupAlign: align,
popupMotion: {
motionName: mergedMotionName
},
popupVisible: mergedVisible,
stretch: getMinOverlayWidthMatchTrigger() ? 'minWidth' : '',
popup: getMenuElementOrLambda(),
onOpenChange: handleVisibleChange,
onPopupClick: onClick,
getPopupContainer: getPopupContainer
}), childrenNode);
});
export default Dropdown;
+5
View File
@@ -0,0 +1,5 @@
import React from 'react';
import type { DropdownProps } from './Dropdown';
export type OverlayProps = Pick<DropdownProps, 'overlay' | 'arrow' | 'prefixCls'>;
declare const Overlay: React.ForwardRefExoticComponent<OverlayProps & React.RefAttributes<HTMLElement>>;
export default Overlay;
+25
View File
@@ -0,0 +1,25 @@
import { composeRef, getNodeRef, supportRef } from "@rc-component/util/es/ref";
import React, { forwardRef, useMemo } from 'react';
const Overlay = /*#__PURE__*/forwardRef((props, ref) => {
const {
overlay,
arrow,
prefixCls
} = props;
const overlayNode = useMemo(() => {
let overlayElement;
if (typeof overlay === 'function') {
overlayElement = overlay();
} else {
overlayElement = overlay;
}
return overlayElement;
}, [overlay]);
const composedRef = composeRef(ref, getNodeRef(overlayNode));
return /*#__PURE__*/React.createElement(React.Fragment, null, arrow && /*#__PURE__*/React.createElement("div", {
className: `${prefixCls}-arrow`
}), /*#__PURE__*/React.cloneElement(overlayNode, {
ref: supportRef(overlayNode) ? composedRef : undefined
}));
});
export default Overlay;
@@ -0,0 +1,10 @@
import * as React from 'react';
interface UseAccessibilityProps {
visible: boolean;
triggerRef: React.RefObject<any>;
onVisibleChange?: (visible: boolean) => void;
autoFocus?: boolean;
overlayRef?: React.RefObject<any>;
}
export default function useAccessibility({ visible, triggerRef, onVisibleChange, autoFocus, overlayRef, }: UseAccessibilityProps): void;
export {};
@@ -0,0 +1,66 @@
import KeyCode from "@rc-component/util/es/KeyCode";
import raf from "@rc-component/util/es/raf";
import * as React from 'react';
const {
ESC,
TAB
} = KeyCode;
export default function useAccessibility({
visible,
triggerRef,
onVisibleChange,
autoFocus,
overlayRef
}) {
const focusMenuRef = React.useRef(false);
const handleCloseMenuAndReturnFocus = () => {
if (visible) {
triggerRef.current?.focus?.();
onVisibleChange?.(false);
}
};
const focusMenu = () => {
if (overlayRef.current?.focus) {
overlayRef.current.focus();
focusMenuRef.current = true;
return true;
}
return false;
};
const handleKeyDown = event => {
switch (event.keyCode) {
case ESC:
handleCloseMenuAndReturnFocus();
break;
case TAB:
{
let focusResult = false;
if (!focusMenuRef.current) {
focusResult = focusMenu();
}
if (focusResult) {
event.preventDefault();
} else {
handleCloseMenuAndReturnFocus();
}
break;
}
}
};
React.useEffect(() => {
if (visible) {
window.addEventListener('keydown', handleKeyDown);
if (autoFocus) {
// FIXME: hack with raf
raf(focusMenu, 3);
}
return () => {
window.removeEventListener('keydown', handleKeyDown);
focusMenuRef.current = false;
};
}
return () => {
focusMenuRef.current = false;
};
}, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
}
+5
View File
@@ -0,0 +1,5 @@
export type { TriggerProps } from '@rc-component/trigger';
export type { DropdownProps } from './Dropdown';
export type { OverlayProps } from './Overlay';
import Dropdown from './Dropdown';
export default Dropdown;
+2
View File
@@ -0,0 +1,2 @@
import Dropdown from "./Dropdown";
export default Dropdown;
+57
View File
@@ -0,0 +1,57 @@
declare const placements: {
topLeft: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
top: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
topRight: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
bottomLeft: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
bottom: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
bottomRight: {
points: string[];
overflow: {
adjustX: number;
adjustY: number;
};
offset: number[];
targetOffset: number[];
};
};
export default placements;
+44
View File
@@ -0,0 +1,44 @@
const autoAdjustOverflow = {
adjustX: 1,
adjustY: 1
};
const targetOffset = [0, 0];
const placements = {
topLeft: {
points: ['bl', 'tl'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset
},
top: {
points: ['bc', 'tc'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset
},
topRight: {
points: ['br', 'tr'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset
},
bottomLeft: {
points: ['tl', 'bl'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset
},
bottom: {
points: ['tc', 'bc'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset
},
bottomRight: {
points: ['tr', 'br'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset
}
};
export default placements;