mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-22 10:21:37 +03:00
Merge pull request #220 from toeverything/feat/block-pendant
Feat/block pendant
This commit is contained in:
commit
451e490dfd
@ -23,7 +23,7 @@ export const CommandPanel = ({ app }: { app: TldrawApp }) => {
|
|||||||
? app.getScreenPoint([bounds.minX, bounds.minY])
|
? app.getScreenPoint([bounds.minX, bounds.minY])
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const anchor = getAnchor({
|
const anchorEl = getAnchor({
|
||||||
x: point?.[0] || 0,
|
x: point?.[0] || 0,
|
||||||
y: (point?.[1] || 0) + 40,
|
y: (point?.[1] || 0) + 40,
|
||||||
width: bounds?.width ? bounds.width * camera.zoom : 0,
|
width: bounds?.width ? bounds.width * camera.zoom : 0,
|
||||||
@ -101,7 +101,7 @@ export const CommandPanel = ({ app }: { app: TldrawApp }) => {
|
|||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
visible={!!point}
|
visible={!!point}
|
||||||
anchor={anchor}
|
anchorEl={anchorEl}
|
||||||
popoverDirection="none"
|
popoverDirection="none"
|
||||||
content={
|
content={
|
||||||
<PopoverContainer>
|
<PopoverContainer>
|
||||||
|
@ -4,7 +4,11 @@ import {
|
|||||||
Popover,
|
Popover,
|
||||||
type PopoverProps,
|
type PopoverProps,
|
||||||
PopperHandler,
|
PopperHandler,
|
||||||
|
Tag,
|
||||||
|
type PopperProps,
|
||||||
} from '@toeverything/components/ui';
|
} from '@toeverything/components/ui';
|
||||||
|
import { TagsIcon } from '@toeverything/components/icons';
|
||||||
|
|
||||||
import { CreatePendantPanel } from './pendant-operation-panel';
|
import { CreatePendantPanel } from './pendant-operation-panel';
|
||||||
import { IconButton } from './StyledComponent';
|
import { IconButton } from './StyledComponent';
|
||||||
import { AsyncBlock } from '../editor';
|
import { AsyncBlock } from '../editor';
|
||||||
@ -13,17 +17,21 @@ type Props = {
|
|||||||
block: AsyncBlock;
|
block: AsyncBlock;
|
||||||
onSure?: () => void;
|
onSure?: () => void;
|
||||||
iconStyle?: CSSProperties;
|
iconStyle?: CSSProperties;
|
||||||
|
useAddIcon?: boolean;
|
||||||
} & Omit<PopoverProps, 'content'>;
|
} & Omit<PopoverProps, 'content'>;
|
||||||
export const AddPendantPopover = ({
|
export const AddPendantPopover = ({
|
||||||
block,
|
block,
|
||||||
onSure,
|
onSure,
|
||||||
iconStyle,
|
iconStyle,
|
||||||
|
useAddIcon = true,
|
||||||
...popoverProps
|
...popoverProps
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const popoverHandlerRef = useRef<PopperHandler>();
|
const popoverHandlerRef = useRef<PopperHandler>();
|
||||||
|
const popperRef = useRef<any>();
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
ref={popoverHandlerRef}
|
popperHandlerRef={popoverHandlerRef}
|
||||||
|
popperRef={popperRef}
|
||||||
content={
|
content={
|
||||||
<CreatePendantPanel
|
<CreatePendantPanel
|
||||||
block={block}
|
block={block}
|
||||||
@ -31,6 +39,9 @@ export const AddPendantPopover = ({
|
|||||||
popoverHandlerRef.current?.setVisible(false);
|
popoverHandlerRef.current?.setVisible(false);
|
||||||
onSure?.();
|
onSure?.();
|
||||||
}}
|
}}
|
||||||
|
onTypeChange={() => {
|
||||||
|
popperRef.current?.update?.();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
@ -38,9 +49,25 @@ export const AddPendantPopover = ({
|
|||||||
style={{ padding: 0 }}
|
style={{ padding: 0 }}
|
||||||
{...popoverProps}
|
{...popoverProps}
|
||||||
>
|
>
|
||||||
<IconButton style={{ marginRight: 12, ...iconStyle }}>
|
{useAddIcon ? (
|
||||||
<Add sx={{ fontSize: 14 }} />
|
<IconButton style={{ marginRight: 12, ...iconStyle }}>
|
||||||
</IconButton>
|
<Add sx={{ fontSize: 14 }} />
|
||||||
|
</IconButton>
|
||||||
|
) : (
|
||||||
|
<Tag
|
||||||
|
style={{
|
||||||
|
background: '#F5F7F8',
|
||||||
|
color: '#98ACBD',
|
||||||
|
marginRight: 12,
|
||||||
|
marginBottom: 8,
|
||||||
|
}}
|
||||||
|
startElement={
|
||||||
|
<TagsIcon style={{ fontSize: 14, marginRight: 4 }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Tag App
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -98,8 +98,8 @@ export const pendantConfig: { [key: string]: PendantConfig } = {
|
|||||||
},
|
},
|
||||||
[PendantTypes.Status]: {
|
[PendantTypes.Status]: {
|
||||||
iconName: IconNames.STATUS,
|
iconName: IconNames.STATUS,
|
||||||
background: ['#C5FBE0', '#FFF5AB', '#FFCECE', '#E3DEFF'],
|
background: ['#FFCECE', '#FFF5AB', '#C5FBE0', '#E3DEFF'],
|
||||||
color: ['#05683D', '#896406', '#AF1212', '#511AAB'],
|
color: ['#AF1212', '#896406', '#05683D', '#511AAB'],
|
||||||
},
|
},
|
||||||
[PendantTypes.Select]: {
|
[PendantTypes.Select]: {
|
||||||
iconName: IconNames.SINGLE_SELECT,
|
iconName: IconNames.SINGLE_SELECT,
|
||||||
|
@ -91,7 +91,7 @@ export const PendantHistoryPanel = ({
|
|||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
key={item.id}
|
key={item.id}
|
||||||
ref={ref => {
|
popperHandlerRef={ref => {
|
||||||
popoverHandlerRef.current[item.id] = ref;
|
popoverHandlerRef.current[item.id] = ref;
|
||||||
}}
|
}}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
|
@ -11,6 +11,7 @@ export type ModifyPanelProps = {
|
|||||||
iconConfig?: PendantConfig;
|
iconConfig?: PendantConfig;
|
||||||
isStatusSelect?: boolean;
|
isStatusSelect?: boolean;
|
||||||
property?: RecastMetaProperty;
|
property?: RecastMetaProperty;
|
||||||
|
onTypeChange?: (type: PendantTypes) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ModifyPanelContentProps = {
|
export type ModifyPanelContentProps = {
|
||||||
|
@ -24,9 +24,11 @@ import { useOnCreateSure } from './hooks';
|
|||||||
export const CreatePendantPanel = ({
|
export const CreatePendantPanel = ({
|
||||||
block,
|
block,
|
||||||
onSure,
|
onSure,
|
||||||
|
onTypeChange,
|
||||||
}: {
|
}: {
|
||||||
block: AsyncBlock;
|
block: AsyncBlock;
|
||||||
onSure?: () => void;
|
onSure?: () => void;
|
||||||
|
onTypeChange?: (option: PendantOptions) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedOption, setSelectedOption] = useState<PendantOptions>();
|
const [selectedOption, setSelectedOption] = useState<PendantOptions>();
|
||||||
const [fieldName, setFieldName] = useState<string>('');
|
const [fieldName, setFieldName] = useState<string>('');
|
||||||
@ -37,6 +39,10 @@ export const CreatePendantPanel = ({
|
|||||||
setFieldName(generateRandomFieldName(selectedOption.type));
|
setFieldName(generateRandomFieldName(selectedOption.type));
|
||||||
}, [selectedOption]);
|
}, [selectedOption]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onTypeChange?.(selectedOption);
|
||||||
|
}, [selectedOption, onTypeChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPopoverWrapper>
|
<StyledPopoverWrapper>
|
||||||
<StyledOperationTitle>Add Field</StyledOperationTitle>
|
<StyledOperationTitle>Add Field</StyledOperationTitle>
|
||||||
|
@ -17,7 +17,7 @@ export const PendantPopover = (
|
|||||||
const popoverHandlerRef = useRef<PopperHandler>();
|
const popoverHandlerRef = useRef<PopperHandler>();
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
ref={popoverHandlerRef}
|
popperHandlerRef={popoverHandlerRef}
|
||||||
pointerEnterDelay={300}
|
pointerEnterDelay={300}
|
||||||
pointerLeaveDelay={200}
|
pointerLeaveDelay={200}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
@ -26,13 +26,13 @@ export const PendantPopover = (
|
|||||||
block={block}
|
block={block}
|
||||||
endElement={
|
endElement={
|
||||||
<AddPendantPopover
|
<AddPendantPopover
|
||||||
container={popoverProps.container}
|
|
||||||
block={block}
|
block={block}
|
||||||
onSure={() => {
|
onSure={() => {
|
||||||
popoverHandlerRef.current?.setVisible(false);
|
popoverHandlerRef.current?.setVisible(false);
|
||||||
}}
|
}}
|
||||||
offset={[0, -30]}
|
offset={[0, -30]}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
useAddIcon={false}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
@ -56,7 +56,7 @@ export const PendantRender = ({ block }: { block: AsyncBlock }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
ref={ref => {
|
popperHandlerRef={ref => {
|
||||||
popoverHandlerRef.current[id] = ref;
|
popoverHandlerRef.current[id] = ref;
|
||||||
}}
|
}}
|
||||||
container={blockRenderContainerRef.current}
|
container={blockRenderContainerRef.current}
|
||||||
@ -107,7 +107,7 @@ export const PendantRender = ({ block }: { block: AsyncBlock }) => {
|
|||||||
iconStyle={{ marginTop: 4 }}
|
iconStyle={{ marginTop: 4 }}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
// trigger={isKanbanView ? 'hover' : 'click'}
|
// trigger={isKanbanView ? 'hover' : 'click'}
|
||||||
container={blockRenderContainerRef.current}
|
// container={blockRenderContainerRef.current}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MuiFade>
|
</MuiFade>
|
||||||
|
@ -88,7 +88,7 @@ export const generateInitialOptions = (
|
|||||||
) => {
|
) => {
|
||||||
if (type === PendantTypes.Status) {
|
if (type === PendantTypes.Status) {
|
||||||
return [
|
return [
|
||||||
generateBasicOption({ index: 0, iconConfig, name: 'No Started' }),
|
generateBasicOption({ index: 0, iconConfig, name: 'Not Started' }),
|
||||||
generateBasicOption({
|
generateBasicOption({
|
||||||
index: 1,
|
index: 1,
|
||||||
iconConfig,
|
iconConfig,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { MuiPopperPlacementType as PopperPlacementType } from '../mui';
|
import type { MuiPopperPlacementType as PopperPlacementType } from '../mui';
|
||||||
import React, { forwardRef, type PropsWithChildren } from 'react';
|
import React, { type PropsWithChildren } from 'react';
|
||||||
import { type PopperHandler, Popper } from '../popper';
|
import { Popper } from '../popper';
|
||||||
import { PopoverContainer } from './Container';
|
import { PopoverContainer } from './Container';
|
||||||
import type { PopoverProps, PopoverDirection } from './interface';
|
import type { PopoverProps, PopoverDirection } from './interface';
|
||||||
|
|
||||||
@ -25,15 +25,11 @@ export const placementToContainerDirection: Record<
|
|||||||
'auto-end': 'none',
|
'auto-end': 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Popover = forwardRef<
|
export const Popover = (props: PropsWithChildren<PopoverProps>) => {
|
||||||
PopperHandler,
|
|
||||||
PropsWithChildren<PopoverProps>
|
|
||||||
>((props, ref) => {
|
|
||||||
const { popoverDirection, placement, content, children, style } = props;
|
const { popoverDirection, placement, content, children, style } = props;
|
||||||
return (
|
return (
|
||||||
<Popper
|
<Popper
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
|
||||||
content={
|
content={
|
||||||
<PopoverContainer
|
<PopoverContainer
|
||||||
style={style}
|
style={style}
|
||||||
@ -49,4 +45,4 @@ export const Popover = forwardRef<
|
|||||||
{children}
|
{children}
|
||||||
</Popper>
|
</Popper>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -1,184 +1,180 @@
|
|||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
/* eslint-disable no-restricted-imports */
|
||||||
MuiPopper,
|
import PopperUnstyled from '@mui/base/PopperUnstyled';
|
||||||
MuiClickAwayListener as ClickAwayListener,
|
/* eslint-disable no-restricted-imports */
|
||||||
MuiGrow as Grow,
|
import ClickAwayListener from '@mui/base/ClickAwayListener';
|
||||||
} from '../mui';
|
/* eslint-disable no-restricted-imports */
|
||||||
|
import Grow from '@mui/material/Grow';
|
||||||
|
|
||||||
import { styled } from '../styled';
|
import { styled } from '../styled';
|
||||||
|
|
||||||
import { PopperProps, PopperHandler, VirtualElement } from './interface';
|
import { PopperProps, VirtualElement } from './interface';
|
||||||
import { useTheme } from '../theme';
|
|
||||||
import { PopperArrow } from './PopoverArrow';
|
import { PopperArrow } from './PopoverArrow';
|
||||||
export const Popper = forwardRef<PopperHandler, PopperProps>(
|
export const Popper = ({
|
||||||
(
|
children,
|
||||||
{
|
content,
|
||||||
children,
|
anchorEl: propsAnchorEl,
|
||||||
content,
|
placement = 'top-start',
|
||||||
anchor: propsAnchor,
|
defaultVisible = false,
|
||||||
placement = 'top-start',
|
visible: propsVisible,
|
||||||
defaultVisible = false,
|
trigger = 'hover',
|
||||||
container,
|
pointerEnterDelay = 100,
|
||||||
keepMounted = false,
|
pointerLeaveDelay = 100,
|
||||||
visible: propsVisible,
|
onVisibleChange,
|
||||||
trigger = 'hover',
|
popoverStyle,
|
||||||
pointerEnterDelay = 100,
|
popoverClassName,
|
||||||
pointerLeaveDelay = 100,
|
anchorStyle,
|
||||||
onVisibleChange,
|
anchorClassName,
|
||||||
popoverStyle,
|
zIndex,
|
||||||
popoverClassName,
|
offset = [0, 5],
|
||||||
anchorStyle,
|
showArrow = false,
|
||||||
anchorClassName,
|
popperHandlerRef,
|
||||||
zIndex,
|
...popperProps
|
||||||
offset = [0, 5],
|
}: PopperProps) => {
|
||||||
showArrow = false,
|
const [anchorEl, setAnchorEl] = useState<VirtualElement>(null);
|
||||||
},
|
const [visible, setVisible] = useState(defaultVisible);
|
||||||
ref
|
const [arrowRef, setArrowRef] = useState<HTMLElement>(null);
|
||||||
) => {
|
const popperRef = useRef();
|
||||||
const [anchorEl, setAnchorEl] = useState<VirtualElement>(null);
|
const pointerLeaveTimer = useRef<number>();
|
||||||
const [visible, setVisible] = useState(defaultVisible);
|
const pointerEnterTimer = useRef<number>();
|
||||||
const [arrowRef, setArrowRef] = useState<HTMLElement>(null);
|
|
||||||
|
|
||||||
const pointerLeaveTimer = useRef<number>();
|
const visibleControlledByParent = typeof propsVisible !== 'undefined';
|
||||||
const pointerEnterTimer = useRef<number>();
|
const isAnchorCustom = typeof propsAnchorEl !== 'undefined';
|
||||||
|
|
||||||
const visibleControlledByParent = typeof propsVisible !== 'undefined';
|
|
||||||
const isAnchorCustom = typeof propsAnchor !== 'undefined';
|
|
||||||
|
|
||||||
const hasHoverTrigger = useMemo(() => {
|
|
||||||
return (
|
|
||||||
trigger === 'hover' ||
|
|
||||||
(Array.isArray(trigger) && trigger.includes('hover'))
|
|
||||||
);
|
|
||||||
}, [trigger]);
|
|
||||||
|
|
||||||
const hasClickTrigger = useMemo(() => {
|
|
||||||
return (
|
|
||||||
trigger === 'click' ||
|
|
||||||
(Array.isArray(trigger) && trigger.includes('click'))
|
|
||||||
);
|
|
||||||
}, [trigger]);
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const onPointerEnterHandler = () => {
|
|
||||||
if (!hasHoverTrigger || visibleControlledByParent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.clearTimeout(pointerLeaveTimer.current);
|
|
||||||
|
|
||||||
pointerEnterTimer.current = window.setTimeout(() => {
|
|
||||||
setVisible(true);
|
|
||||||
}, pointerEnterDelay);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPointerLeaveHandler = () => {
|
|
||||||
if (!hasHoverTrigger || visibleControlledByParent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.clearTimeout(pointerEnterTimer.current);
|
|
||||||
pointerLeaveTimer.current = window.setTimeout(() => {
|
|
||||||
setVisible(false);
|
|
||||||
}, pointerLeaveDelay);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onVisibleChange?.(visible);
|
|
||||||
}, [visible, onVisibleChange]);
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => {
|
|
||||||
return {
|
|
||||||
setVisible: (visible: boolean) => {
|
|
||||||
!visibleControlledByParent && setVisible(visible);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
const hasHoverTrigger = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<ClickAwayListener
|
trigger === 'hover' ||
|
||||||
onClickAway={() => {
|
(Array.isArray(trigger) && trigger.includes('hover'))
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Container>
|
|
||||||
{isAnchorCustom ? null : (
|
|
||||||
<div
|
|
||||||
ref={(dom: HTMLDivElement) => setAnchorEl(dom)}
|
|
||||||
onClick={() => {
|
|
||||||
if (
|
|
||||||
!hasClickTrigger ||
|
|
||||||
visibleControlledByParent
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setVisible(!visible);
|
|
||||||
}}
|
|
||||||
onPointerEnter={onPointerEnterHandler}
|
|
||||||
onPointerLeave={onPointerLeaveHandler}
|
|
||||||
style={anchorStyle}
|
|
||||||
className={anchorClassName}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<MuiPopper
|
|
||||||
open={
|
|
||||||
visibleControlledByParent ? propsVisible : visible
|
|
||||||
}
|
|
||||||
sx={{ zIndex: zIndex || theme.affine.zIndex.popover }}
|
|
||||||
anchorEl={isAnchorCustom ? propsAnchor : anchorEl}
|
|
||||||
placement={placement}
|
|
||||||
container={container}
|
|
||||||
keepMounted={keepMounted}
|
|
||||||
transition
|
|
||||||
modifiers={[
|
|
||||||
{
|
|
||||||
name: 'offset',
|
|
||||||
options: {
|
|
||||||
offset,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'arrow',
|
|
||||||
enabled: showArrow,
|
|
||||||
options: {
|
|
||||||
element: arrowRef,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{({ TransitionProps }) => (
|
|
||||||
<Grow {...TransitionProps}>
|
|
||||||
<div
|
|
||||||
onPointerEnter={onPointerEnterHandler}
|
|
||||||
onPointerLeave={onPointerLeaveHandler}
|
|
||||||
style={popoverStyle}
|
|
||||||
className={popoverClassName}
|
|
||||||
>
|
|
||||||
{showArrow && (
|
|
||||||
<PopperArrow
|
|
||||||
placement={placement}
|
|
||||||
ref={setArrowRef}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
</Grow>
|
|
||||||
)}
|
|
||||||
</MuiPopper>
|
|
||||||
</Container>
|
|
||||||
</ClickAwayListener>
|
|
||||||
);
|
);
|
||||||
}
|
}, [trigger]);
|
||||||
);
|
|
||||||
|
const hasClickTrigger = useMemo(() => {
|
||||||
|
return (
|
||||||
|
trigger === 'click' ||
|
||||||
|
(Array.isArray(trigger) && trigger.includes('click'))
|
||||||
|
);
|
||||||
|
}, [trigger]);
|
||||||
|
|
||||||
|
const onPointerEnterHandler = () => {
|
||||||
|
if (!hasHoverTrigger || visibleControlledByParent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearTimeout(pointerLeaveTimer.current);
|
||||||
|
|
||||||
|
pointerEnterTimer.current = window.setTimeout(() => {
|
||||||
|
setVisible(true);
|
||||||
|
}, pointerEnterDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerLeaveHandler = () => {
|
||||||
|
if (!hasHoverTrigger || visibleControlledByParent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.clearTimeout(pointerEnterTimer.current);
|
||||||
|
pointerLeaveTimer.current = window.setTimeout(() => {
|
||||||
|
setVisible(false);
|
||||||
|
}, pointerLeaveDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onVisibleChange?.(visible);
|
||||||
|
}, [visible, onVisibleChange]);
|
||||||
|
|
||||||
|
useImperativeHandle(popperHandlerRef, () => {
|
||||||
|
return {
|
||||||
|
setVisible: (visible: boolean) => {
|
||||||
|
!visibleControlledByParent && setVisible(visible);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClickAwayListener
|
||||||
|
onClickAway={() => {
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container>
|
||||||
|
{isAnchorCustom ? null : (
|
||||||
|
<div
|
||||||
|
ref={(dom: HTMLDivElement) => setAnchorEl(dom)}
|
||||||
|
onClick={() => {
|
||||||
|
if (!hasClickTrigger || visibleControlledByParent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setVisible(!visible);
|
||||||
|
}}
|
||||||
|
onPointerEnter={onPointerEnterHandler}
|
||||||
|
onPointerLeave={onPointerLeaveHandler}
|
||||||
|
style={anchorStyle}
|
||||||
|
className={anchorClassName}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<BasicStyledPopper
|
||||||
|
popperRef={popperRef}
|
||||||
|
open={visibleControlledByParent ? propsVisible : visible}
|
||||||
|
zIndex={zIndex}
|
||||||
|
anchorEl={isAnchorCustom ? propsAnchorEl : anchorEl}
|
||||||
|
placement={placement}
|
||||||
|
transition
|
||||||
|
modifiers={[
|
||||||
|
{
|
||||||
|
name: 'offset',
|
||||||
|
options: {
|
||||||
|
offset,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'arrow',
|
||||||
|
enabled: showArrow,
|
||||||
|
options: {
|
||||||
|
element: arrowRef,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
{...popperProps}
|
||||||
|
>
|
||||||
|
{({ TransitionProps }) => (
|
||||||
|
<Grow {...TransitionProps}>
|
||||||
|
<div
|
||||||
|
onPointerEnter={onPointerEnterHandler}
|
||||||
|
onPointerLeave={onPointerLeaveHandler}
|
||||||
|
style={popoverStyle}
|
||||||
|
className={popoverClassName}
|
||||||
|
>
|
||||||
|
{showArrow && (
|
||||||
|
<PopperArrow
|
||||||
|
placement={placement}
|
||||||
|
ref={setArrowRef}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</Grow>
|
||||||
|
)}
|
||||||
|
</BasicStyledPopper>
|
||||||
|
</Container>
|
||||||
|
</ClickAwayListener>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// The children of ClickAwayListener must be a DOM Node to judge whether the click is outside, use node.contains
|
// The children of ClickAwayListener must be a DOM Node to judge whether the click is outside, use node.contains
|
||||||
const Container = styled('div')({
|
const Container = styled('div')({
|
||||||
display: 'contents',
|
display: 'contents',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const BasicStyledPopper = styled(PopperUnstyled)<{
|
||||||
|
zIndex?: number;
|
||||||
|
}>(({ zIndex, theme }) => {
|
||||||
|
return {
|
||||||
|
zIndex: zIndex || theme.affine.zIndex.popover,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import type { CSSProperties, ReactNode } from 'react';
|
import type { CSSProperties, ReactNode, Ref } from 'react';
|
||||||
import type { MuiPopperPlacementType as PopperPlacementType } from '../mui';
|
/* eslint-disable no-restricted-imports */
|
||||||
|
import {
|
||||||
|
type PopperUnstyledProps,
|
||||||
|
type PopperPlacementType,
|
||||||
|
} from '@mui/base/PopperUnstyled';
|
||||||
export type VirtualElement = {
|
export type VirtualElement = {
|
||||||
getBoundingClientRect: () => ClientRect | DOMRect;
|
getBoundingClientRect: () => ClientRect | DOMRect;
|
||||||
contextElement?: Element;
|
contextElement?: Element;
|
||||||
@ -21,26 +24,12 @@ export type PopperProps = {
|
|||||||
// Popover trigger
|
// Popover trigger
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
|
||||||
// Position of Popover
|
|
||||||
placement?: PopperPlacementType;
|
|
||||||
|
|
||||||
// The popover will pop up based on the anchor position
|
|
||||||
// And if this parameter is passed, children will not be rendered
|
|
||||||
anchor?: VirtualElement | (() => VirtualElement);
|
|
||||||
|
|
||||||
// Whether the default is implicit
|
// Whether the default is implicit
|
||||||
defaultVisible?: boolean;
|
defaultVisible?: boolean;
|
||||||
|
|
||||||
// Used to manually control the visibility of the Popover
|
// Used to manually control the visibility of the Popover
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
|
|
||||||
// A HTML element or function that returns one. The container will have the portal children appended to it.
|
|
||||||
// By default, it uses the body of the top-level document object, so it's simply document.body most of the time.
|
|
||||||
container?: HTMLElement;
|
|
||||||
|
|
||||||
// Always keep the children in the DOM. This prop can be useful in SEO situation or when you want to maximize the responsiveness of the Popper
|
|
||||||
keepMounted?: boolean;
|
|
||||||
|
|
||||||
// TODO: support focus
|
// TODO: support focus
|
||||||
trigger?: 'hover' | 'click' | 'focus' | ('click' | 'hover' | 'focus')[];
|
trigger?: 'hover' | 'click' | 'focus' | ('click' | 'hover' | 'focus')[];
|
||||||
|
|
||||||
@ -71,4 +60,6 @@ export type PopperProps = {
|
|||||||
offset?: [number, number];
|
offset?: [number, number];
|
||||||
|
|
||||||
showArrow?: boolean;
|
showArrow?: boolean;
|
||||||
};
|
|
||||||
|
popperHandlerRef?: Ref<PopperHandler>;
|
||||||
|
} & Omit<PopperUnstyledProps, 'open' | 'ref'>;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { forwardRef, type PropsWithChildren, type CSSProperties } from 'react';
|
import { type PropsWithChildren, type CSSProperties } from 'react';
|
||||||
import { type PopperHandler, type PopperProps, Popper } from '../popper';
|
import { type PopperProps, Popper } from '../popper';
|
||||||
import { PopoverContainer, placementToContainerDirection } from '../popover';
|
import { PopoverContainer, placementToContainerDirection } from '../popover';
|
||||||
import type { TooltipProps } from './interface';
|
import type { TooltipProps } from './interface';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
@ -14,17 +14,15 @@ const useTooltipStyle = (): CSSProperties => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Tooltip = forwardRef<
|
export const Tooltip = (
|
||||||
PopperHandler,
|
props: PropsWithChildren<PopperProps & TooltipProps>
|
||||||
PropsWithChildren<PopperProps & TooltipProps>
|
) => {
|
||||||
>((props, ref) => {
|
|
||||||
const { content, placement = 'top-start' } = props;
|
const { content, placement = 'top-start' } = props;
|
||||||
const style = useTooltipStyle();
|
const style = useTooltipStyle();
|
||||||
// If there is no content, hide forever
|
// If there is no content, hide forever
|
||||||
const visibleProp = content ? {} : { visible: false };
|
const visibleProp = content ? {} : { visible: false };
|
||||||
return (
|
return (
|
||||||
<Popper
|
<Popper
|
||||||
ref={ref}
|
|
||||||
{...visibleProp}
|
{...visibleProp}
|
||||||
placement="top"
|
placement="top"
|
||||||
{...props}
|
{...props}
|
||||||
@ -39,4 +37,4 @@ export const Tooltip = forwardRef<
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user