1
This commit is contained in:
+28
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
Generated
Vendored
+10
@@ -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 {};
|
||||
+66
@@ -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
@@ -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
@@ -0,0 +1,2 @@
|
||||
import Dropdown from "./Dropdown";
|
||||
export default Dropdown;
|
||||
+57
@@ -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
@@ -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;
|
||||
Reference in New Issue
Block a user