Skip to content

Commit

Permalink
feat(ui): add popover component
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Jul 25, 2022
1 parent 7b8e6bb commit 40c07ca
Show file tree
Hide file tree
Showing 30 changed files with 1,250 additions and 308 deletions.
13 changes: 1 addition & 12 deletions packages/ui/src/components/_popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export interface DPopupRenderProps {
'data-popup-triggerid': string;
pOnMouseEnter?: React.MouseEventHandler;
pOnMouseLeave?: React.MouseEventHandler;
pOnFocus?: React.FocusEventHandler;
pOnBlur?: React.FocusEventHandler;
pOnClick?: React.MouseEventHandler;
}

Expand All @@ -26,7 +24,7 @@ export interface DPopupProps {
dPopup: (props: DPopupPopupRenderProps) => JSX.Element | null;
dVisible?: boolean;
dContainer?: HTMLElement | null;
dTrigger?: 'hover' | 'focus' | 'click';
dTrigger?: 'hover' | 'click';
dDisabled?: boolean;
dEscClosable?: boolean;
dMouseEnterDelay?: number;
Expand Down Expand Up @@ -212,15 +210,6 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
};
break;

case 'focus':
childProps.pOnFocus = () => {
handleTrigger(true);
};
childProps.pOnBlur = () => {
handleTrigger(false);
};
break;

case 'click':
childProps.pOnClick = () => {
clickOut.current = false;
Expand Down
33 changes: 18 additions & 15 deletions packages/ui/src/components/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, { useEffect, useId, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

import { usePrefixConfig, useComponentConfig, useElement, useLockScroll, useMaxIndex, useDValue } from '../../hooks';
import { registerComponentMate, getClassName, toPx } from '../../utils';
import { registerComponentMate, getClassName, toPx, handleModalKeyDown } from '../../utils';
import { TTANSITION_DURING_BASE } from '../../utils/global';
import { DMask } from '../_mask';
import { DTransition } from '../_transition';
Expand Down Expand Up @@ -66,12 +66,11 @@ export function DDrawer(props: DDrawerProps): JSX.Element | null {

//#region Ref
const drawerRef = useRef<HTMLDivElement>(null);
const drawerContentRef = useRef<HTMLDivElement>(null);
//#endregion

const uniqueId = useId();
const headerId = `${dPrefix}drawer-header-${uniqueId}`;
const contentId = `${dPrefix}drawer-content-${uniqueId}`;
const bodyId = `${dPrefix}drawer-content-${uniqueId}`;

const [distance, setDistance] = useState<{ visible: boolean; top: number; right: number; bottom: number; left: number }>({
visible: false,
Expand Down Expand Up @@ -157,8 +156,8 @@ export function DDrawer(props: DDrawerProps): JSX.Element | null {
if (visible) {
prevActiveEl.current = document.activeElement as HTMLElement | null;

if (drawerContentRef.current) {
drawerContentRef.current.focus({ preventScroll: true });
if (drawerRef.current) {
drawerRef.current.focus({ preventScroll: true });
}
} else if (prevActiveEl.current) {
prevActiveEl.current.focus({ preventScroll: true });
Expand Down Expand Up @@ -233,10 +232,20 @@ export function DDrawer(props: DDrawerProps): JSX.Element | null {
position: isFixed ? undefined : 'absolute',
zIndex,
}}
tabIndex={-1}
role={restProps.role ?? 'dialog'}
aria-modal={restProps['aria-modal'] ?? 'true'}
aria-labelledby={restProps['aria-labelledby'] ?? (headerNode ? headerId : undefined)}
aria-describedby={restProps['aria-describedby'] ?? contentId}
aria-describedby={restProps['aria-describedby'] ?? bodyId}
onKeyDown={(e) => {
restProps.onKeyDown?.(e);

if (dEscClosable && e.code === 'Escape') {
changeVisible(false);
}

handleModalKeyDown(e);
}}
>
{dMask && (
<DMask
Expand All @@ -249,23 +258,17 @@ export function DDrawer(props: DDrawerProps): JSX.Element | null {
/>
)}
<div
ref={drawerContentRef}
id={contentId}
className={getClassName(`${dPrefix}drawer__content`, `${dPrefix}drawer__content--${dPlacement}`)}
style={{
width: dPlacement === 'left' || dPlacement === 'right' ? dWidth : undefined,
height: dPlacement === 'bottom' || dPlacement === 'top' ? dHeight : undefined,
...transitionStyles[state],
}}
tabIndex={-1}
onKeyDown={(e) => {
if (dEscClosable && e.code === 'Escape') {
changeVisible(false);
}
}}
>
{headerNode}
<div className={`${dPrefix}drawer__body`}>{children}</div>
<div id={bodyId} className={`${dPrefix}drawer__body`}>
{children}
</div>
{dFooter &&
React.cloneElement<DDrawerFooterPropsWithPrivate>(dFooter, {
...dFooter.props,
Expand Down
4 changes: 1 addition & 3 deletions packages/ui/src/components/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ function Dropdown<ID extends DId, T extends DDropdownOption<ID>>(
dUpdatePosition={updatePosition}
onVisibleChange={changeVisible}
>
{({ pOnClick, pOnFocus, pOnBlur, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
{({ pOnClick, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
<DFocusVisible onFocusVisibleChange={setFocusVisible}>
{({ fvOnFocus, fvOnBlur, fvOnKeyDown }) =>
React.cloneElement<React.HTMLAttributes<HTMLElement>>(children, {
Expand All @@ -461,15 +461,13 @@ function Dropdown<ID extends DId, T extends DDropdownOption<ID>>(
},
onFocus: (e) => {
children.props.onFocus?.(e);
pOnFocus?.(e);
fvOnFocus(e);

setIsFocus(true);
initFocus();
},
onBlur: (e) => {
children.props.onBlur?.(e);
pOnBlur?.(e);
fvOnBlur(e);

setIsFocus(false);
Expand Down
4 changes: 1 addition & 3 deletions packages/ui/src/components/dropdown/DropdownSub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export function DDropdownSub(props: DDropdownSubProps): JSX.Element | null {
dUpdatePosition={updatePosition}
onVisibleChange={onVisibleChange}
>
{({ pOnClick, pOnFocus, pOnBlur, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
{({ pOnClick, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
<li
{...restPProps}
ref={liRef}
Expand All @@ -165,8 +165,6 @@ export function DDropdownSub(props: DDropdownSubProps): JSX.Element | null {
aria-expanded={dPopupVisible}
aria-disabled={dDisabled}
onClick={pOnClick}
onFocus={pOnFocus}
onBlur={pOnBlur}
onMouseEnter={pOnMouseEnter}
onMouseLeave={pOnMouseLeave}
>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export { NotificationService } from './notification';
export type { DPaginationProps } from './pagination';
export { DPagination } from './pagination';

export type { DPopoverProps, DPopoverHeaderProps, DPopoverFooterProps } from './popover';
export { DPopover, DPopoverHeader, DPopoverFooter } from './popover';

export type { DProgressProps } from './progress';
export { DProgress } from './progress';

Expand Down
4 changes: 1 addition & 3 deletions packages/ui/src/components/menu/MenuSub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export function DMenuSub(props: DMenuSubProps): JSX.Element | null {
dUpdatePosition={updatePosition}
onVisibleChange={onVisibleChange}
>
{({ pOnClick, pOnFocus, pOnBlur, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
{({ pOnClick, pOnMouseEnter, pOnMouseLeave, ...restPProps }) => (
<li
{...restPProps}
ref={liRef}
Expand All @@ -224,8 +224,6 @@ export function DMenuSub(props: DMenuSubProps): JSX.Element | null {

onClick(e);
}}
onFocus={pOnFocus}
onBlur={pOnBlur}
onMouseEnter={pOnMouseEnter}
onMouseLeave={pOnMouseLeave}
>
Expand Down
29 changes: 16 additions & 13 deletions packages/ui/src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ReactDOM from 'react-dom';

import { usePrefixConfig, useComponentConfig, useElement, useLockScroll, useMaxIndex, useAsync, useDValue } from '../../hooks';
import { CheckCircleOutlined, CloseCircleOutlined, ExclamationCircleOutlined, WarningOutlined } from '../../icons';
import { registerComponentMate, getClassName } from '../../utils';
import { registerComponentMate, getClassName, handleModalKeyDown } from '../../utils';
import { TTANSITION_DURING_BASE } from '../../utils/global';
import { DMask } from '../_mask';
import { DTransition } from '../_transition';
Expand Down Expand Up @@ -73,7 +73,7 @@ export function DModal(props: DModalProps): JSX.Element | null {

const uniqueId = useId();
const headerId = `${dPrefix}modal-header-${uniqueId}`;
const contentId = `${dPrefix}modal-content-${uniqueId}`;
const bodyId = `${dPrefix}modal-content-${uniqueId}`;

const topStyle = dTop + (isNumber(dTop) ? 'px' : '');

Expand Down Expand Up @@ -104,8 +104,8 @@ export function DModal(props: DModalProps): JSX.Element | null {
if (visible) {
prevActiveEl.current = document.activeElement as HTMLElement | null;

if (modalContentRef.current) {
modalContentRef.current.focus({ preventScroll: true });
if (modalRef.current) {
modalRef.current.focus({ preventScroll: true });
}
} else if (prevActiveEl.current) {
prevActiveEl.current.focus({ preventScroll: true });
Expand Down Expand Up @@ -206,10 +206,20 @@ export function DModal(props: DModalProps): JSX.Element | null {
display: state === 'leaved' ? 'none' : undefined,
zIndex,
}}
tabIndex={-1}
role={restProps.role ?? 'dialog'}
aria-modal={restProps['aria-modal'] ?? 'true'}
aria-labelledby={restProps['aria-labelledby'] ?? (headerNode ? headerId : undefined)}
aria-describedby={restProps['aria-describedby'] ?? contentId}
aria-describedby={restProps['aria-describedby'] ?? bodyId}
onKeyDown={(e) => {
restProps.onKeyDown?.(e);

if (dEscClosable && e.code === 'Escape') {
changeVisible(false);
}

handleModalKeyDown(e);
}}
>
{dMask && (
<DMask
Expand All @@ -223,23 +233,16 @@ export function DModal(props: DModalProps): JSX.Element | null {
)}
<div
ref={modalContentRef}
id={contentId}
className={`${dPrefix}modal__content`}
style={{
...transitionStyle,
width: dWidth,
top: dTop === 'center' ? undefined : dTop,
maxHeight: dTop === 'center' ? undefined : `calc(100% - ${topStyle} - 20px)`,
}}
tabIndex={-1}
onKeyDown={(e) => {
if (dEscClosable && e.code === 'Escape') {
changeVisible(false);
}
}}
>
{headerNode}
<div className={`${dPrefix}modal__body`}>
<div id={bodyId} className={`${dPrefix}modal__body`}>
{dType ? (
<>
<div className={`${dPrefix}modal__icon`}>
Expand Down
Loading

0 comments on commit 40c07ca

Please sign in to comment.