First pass at Hoverable component
This commit is contained in:
parent
69de2aad55
commit
1742236074
|
@ -19,7 +19,7 @@ import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
||||||
|
|
||||||
import { openModal } from '../actions/modals';
|
import { openModal } from '../actions/modals';
|
||||||
|
|
||||||
import { IconButton, Text } from './ui';
|
import { IconButton, Text, Hoverable } from './ui';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -547,7 +547,6 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { status, intl, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorUnfocus, features, me } = this.props;
|
const { status, intl, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorUnfocus, features, me } = this.props;
|
||||||
const { emojiSelectorVisible } = this.state;
|
|
||||||
|
|
||||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
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>}
|
{reblogCount !== 0 && <Text size='xs' theme='muted' role='presentation' onClick={this.handleOpenReblogsModal}>{reblogCount}</Text>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<Hoverable
|
||||||
ref={this.setRef}
|
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'
|
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}
|
component={(
|
||||||
onMouseLeave={this.handleLikeButtonLeave}
|
<EmojiSelector
|
||||||
|
onReact={this.handleReact}
|
||||||
|
focused={emojiSelectorFocused}
|
||||||
|
onUnfocus={handleEmojiSelectorUnfocus}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<EmojiSelector
|
|
||||||
onReact={this.handleReact}
|
|
||||||
visible={features.emojiReacts && emojiSelectorVisible}
|
|
||||||
focused={emojiSelectorFocused}
|
|
||||||
onUnfocus={handleEmojiSelectorUnfocus}
|
|
||||||
/>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'text-gray-400 hover:text-gray-600 dark:hover:text-white': !meEmojiReact,
|
'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>
|
<span className='detailed-status__link'>{emojiReactCount}</span>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</Hoverable>
|
||||||
|
|
||||||
{shareButton}
|
{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 Form } from './form/form';
|
||||||
export { default as FormActions } from './form-actions/form-actions';
|
export { default as FormActions } from './form-actions/form-actions';
|
||||||
export { default as FormGroup } from './form-group/form-group';
|
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 HStack } from './hstack/hstack';
|
||||||
export { default as Icon } from './icon/icon';
|
export { default as Icon } from './icon/icon';
|
||||||
export { default as IconButton } from './icon-button/icon-button';
|
export { default as IconButton } from './icon-button/icon-button';
|
||||||
|
|
Loading…
Reference in New Issue