sforkowany z mirror/soapbox
First pass at Hoverable component
rodzic
69de2aad55
commit
1742236074
|
@ -19,7 +19,7 @@ import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
|||
|
||||
import { openModal } from '../actions/modals';
|
||||
|
||||
import { IconButton, Text } from './ui';
|
||||
import { IconButton, Text, Hoverable } from './ui';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -547,7 +547,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
render() {
|
||||
const { status, intl, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorUnfocus, features, me } = this.props;
|
||||
const { emojiSelectorVisible } = this.state;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
|
||||
|
@ -664,18 +663,17 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
{reblogCount !== 0 && <Text size='xs' theme='muted' role='presentation' onClick={this.handleOpenReblogsModal}>{reblogCount}</Text>}
|
||||
</div>
|
||||
|
||||
<div
|
||||
<Hoverable
|
||||
ref={this.setRef}
|
||||
className='flex relative items-center space-x-0.5 p-1 rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500'
|
||||
onMouseEnter={this.handleLikeButtonHover}
|
||||
onMouseLeave={this.handleLikeButtonLeave}
|
||||
component={(
|
||||
<EmojiSelector
|
||||
onReact={this.handleReact}
|
||||
focused={emojiSelectorFocused}
|
||||
onUnfocus={handleEmojiSelectorUnfocus}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<EmojiSelector
|
||||
onReact={this.handleReact}
|
||||
visible={features.emojiReacts && emojiSelectorVisible}
|
||||
focused={emojiSelectorFocused}
|
||||
onUnfocus={handleEmojiSelectorUnfocus}
|
||||
/>
|
||||
<IconButton
|
||||
className={classNames({
|
||||
'text-gray-400 hover:text-gray-600 dark:hover:text-white': !meEmojiReact,
|
||||
|
@ -698,7 +696,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
<span className='detailed-status__link'>{emojiReactCount}</span>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</Hoverable>
|
||||
|
||||
{shareButton}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import Portal from '@reach/portal';
|
||||
import React, { useState, useRef } from 'react';
|
||||
|
||||
interface IHoverable {
|
||||
component: React.Component,
|
||||
}
|
||||
|
||||
/** Wrapper to render a given component when hovered */
|
||||
const Hoverable: React.FC<IHoverable> = ({
|
||||
component,
|
||||
children,
|
||||
}): JSX.Element => {
|
||||
|
||||
const [portalActive, setPortalActive] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setPortalActive(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setPortalActive(false);
|
||||
};
|
||||
|
||||
const setPortalPosition = (): React.CSSProperties => {
|
||||
if (!ref.current) return {};
|
||||
|
||||
const { top, height, left, width } = ref.current.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
top: top + height,
|
||||
left,
|
||||
width,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
{portalActive && <Portal><div className='fixed' style={setPortalPosition()}>{component}</div></Portal>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hoverable;
|
|
@ -7,6 +7,7 @@ export { default as EmojiSelector } from './emoji-selector/emoji-selector';
|
|||
export { default as Form } from './form/form';
|
||||
export { default as FormActions } from './form-actions/form-actions';
|
||||
export { default as FormGroup } from './form-group/form-group';
|
||||
export { default as Hoverable } from './hoverable/hoverable';
|
||||
export { default as HStack } from './hstack/hstack';
|
||||
export { default as Icon } from './icon/icon';
|
||||
export { default as IconButton } from './icon-button/icon-button';
|
||||
|
|
Ładowanie…
Reference in New Issue