mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-06 03:51:08 +08:00
158 lines
4.3 KiB
TypeScript
158 lines
4.3 KiB
TypeScript
import React, { FunctionComponent, ReactNode, useState, useEffect } from 'react';
|
|
import { styled } from '@storybook/theming';
|
|
import { logger } from '@storybook/client-logger';
|
|
// import { withState, lifecycle } from 'recompose';
|
|
import { document } from 'global';
|
|
|
|
import TooltipTrigger from 'react-popper-tooltip';
|
|
import { Modifiers, Placement } from 'popper.js';
|
|
import { Tooltip } from './Tooltip';
|
|
|
|
// A target that doesn't speak popper
|
|
const TargetContainer = styled.div<{ mode: string }>`
|
|
display: inline-block;
|
|
cursor: ${props => (props.mode === 'hover' ? 'default' : 'pointer')};
|
|
`;
|
|
|
|
const TargetSvgContainer = styled.g<{ mode: string }>`
|
|
cursor: ${props => (props.mode === 'hover' ? 'default' : 'pointer')};
|
|
`;
|
|
|
|
interface WithHideFn {
|
|
onHide: () => void;
|
|
}
|
|
|
|
export interface WithTooltipPureProps {
|
|
svg?: boolean;
|
|
trigger?: 'none' | 'hover' | 'click' | 'right-click';
|
|
closeOnClick?: boolean;
|
|
placement?: Placement;
|
|
modifiers?: Modifiers;
|
|
hasChrome?: boolean;
|
|
tooltip: ReactNode | ((p: WithHideFn) => ReactNode);
|
|
children: ReactNode;
|
|
tooltipShown?: boolean;
|
|
onVisibilityChange?: (visibility: boolean) => void;
|
|
onDoubleClick?: () => void;
|
|
}
|
|
|
|
// Pure, does not bind to the body
|
|
const WithTooltipPure: FunctionComponent<WithTooltipPureProps> = ({
|
|
svg,
|
|
trigger,
|
|
closeOnClick,
|
|
placement,
|
|
modifiers,
|
|
hasChrome,
|
|
tooltip,
|
|
children,
|
|
tooltipShown,
|
|
onVisibilityChange,
|
|
...props
|
|
}) => {
|
|
const Container = svg ? TargetSvgContainer : TargetContainer;
|
|
|
|
return (
|
|
<TooltipTrigger
|
|
placement={placement}
|
|
trigger={trigger}
|
|
modifiers={modifiers}
|
|
tooltipShown={tooltipShown}
|
|
onVisibilityChange={onVisibilityChange}
|
|
tooltip={({
|
|
getTooltipProps,
|
|
getArrowProps,
|
|
tooltipRef,
|
|
arrowRef,
|
|
placement: tooltipPlacement,
|
|
}) => (
|
|
<Tooltip
|
|
hasChrome={hasChrome}
|
|
placement={tooltipPlacement}
|
|
tooltipRef={tooltipRef}
|
|
arrowRef={arrowRef}
|
|
arrowProps={getArrowProps()}
|
|
{...getTooltipProps()}
|
|
>
|
|
{typeof tooltip === 'function'
|
|
? tooltip({ onHide: () => onVisibilityChange(false) })
|
|
: tooltip}
|
|
</Tooltip>
|
|
)}
|
|
>
|
|
{({ getTriggerProps, triggerRef }) => (
|
|
// @ts-ignore
|
|
<Container ref={triggerRef} {...getTriggerProps()} {...props}>
|
|
{children}
|
|
</Container>
|
|
)}
|
|
</TooltipTrigger>
|
|
);
|
|
};
|
|
|
|
WithTooltipPure.defaultProps = {
|
|
svg: false,
|
|
trigger: 'hover',
|
|
closeOnClick: false,
|
|
placement: 'top',
|
|
modifiers: {},
|
|
hasChrome: true,
|
|
tooltipShown: false,
|
|
};
|
|
|
|
const WithToolTipState: FunctionComponent<WithTooltipPureProps & {
|
|
startOpen?: boolean;
|
|
}> = ({ startOpen, ...rest }) => {
|
|
const [tooltipShown, onVisibilityChange] = useState(startOpen || false);
|
|
|
|
useEffect(() => {
|
|
const hide = () => onVisibilityChange(false);
|
|
document.addEventListener('keydown', hide, false);
|
|
|
|
// Find all iframes on the screen and bind to clicks inside them (waiting until the iframe is ready)
|
|
const iframes: HTMLIFrameElement[] = Array.from(document.getElementsByTagName('iframe'));
|
|
const unbinders: (() => void)[] = [];
|
|
iframes.forEach(iframe => {
|
|
const bind = () => {
|
|
try {
|
|
if (iframe.contentWindow.document) {
|
|
iframe.contentWindow.document.addEventListener('click', hide);
|
|
unbinders.push(() => {
|
|
try {
|
|
iframe.contentWindow.document.removeEventListener('click', hide);
|
|
} catch (e) {
|
|
logger.debug('Removing a click listener from iframe failed: ', e);
|
|
}
|
|
});
|
|
}
|
|
} catch (e) {
|
|
logger.debug('Adding a click listener to iframe failed: ', e);
|
|
}
|
|
};
|
|
|
|
bind(); // I don't know how to find out if it's already loaded so I potentially will bind twice
|
|
iframe.addEventListener('load', bind);
|
|
unbinders.push(() => {
|
|
iframe.removeEventListener('load', bind);
|
|
});
|
|
});
|
|
|
|
return () => {
|
|
document.removeEventListener('keydown', hide);
|
|
unbinders.forEach(unbind => {
|
|
unbind();
|
|
});
|
|
};
|
|
});
|
|
|
|
return (
|
|
<WithTooltipPure
|
|
{...rest}
|
|
tooltipShown={tooltipShown}
|
|
onVisibilityChange={onVisibilityChange}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export { WithTooltipPure, WithToolTipState, WithToolTipState as WithTooltip };
|