feat(post): add post menu component with links to other clients

This commit is contained in:
Tom (plebeius.eth)
2024-05-27 12:28:18 +02:00
parent 7537066202
commit 08cb0736da
7 changed files with 223 additions and 95 deletions
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useLocation, useParams } from 'react-router-dom';
import { Link } from 'react-router-dom';
@@ -17,6 +17,7 @@ import LoadingEllipsis from '../../loading-ellipsis';
import Markdown from '../../markdown';
import { PostProps } from '../post';
import styles from '../post.module.css';
import PostMenu from '../post-menu';
import _ from 'lodash';
const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
@@ -41,30 +42,6 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
const account = useAccount();
const accountShortAddress = account?.author?.shortAddress;
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const handleMenuButtonClick = () => {
setMenuBtnRotated((prev) => !prev);
};
const handleClickOutside = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setMenuBtnRotated(false);
}
};
useEffect(() => {
if (menuBtnRotated) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [menuBtnRotated]);
return (
<div className={styles.postInfo}>
<span className={styles.checkbox}>
@@ -125,11 +102,7 @@ const PostInfo = ({ openReplyModal, post, roles }: PostProps) => {
</span>
)}
</span>
<span className={styles.postMenuBtnWrapper} ref={menuRef}>
<span className={styles.postMenuBtn} title='Post menu' onClick={handleMenuButtonClick} style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}>
</span>
</span>
<PostMenu post={post} />
</div>
);
};
+1
View File
@@ -0,0 +1 @@
export { default } from './post-menu';
@@ -0,0 +1,86 @@
.postMenu {
position: absolute;
background-color: var(--post-menu-background-color);
border-bottom: 2px solid var(--post-menu-border-color);
border-right: 1px solid var(--post-menu-border-color);
outline: none;
}
.postMenuBtnWrapper {
position: relative;
display: inline-block;
padding-left: 15px;
}
.postMenuBtn {
color: var(--post-menu-button-color);
transition: transform 0.1s;
transform: rotate(90deg);
position: absolute;
bottom: -4px;
}
.postMenuBtn:hover {
cursor: pointer;
color: var(--post-menu-button-color-hover);
}
.postMenuItem {
cursor: pointer;
padding: 2px 4px;
border: 1px solid var(--post-menu-item-border-color);
background-color: var(--post-menu-item-background-color);
border-bottom: none;
}
.postMenuItem:hover {
background-color: var(--post-menu-item-hover-background-color);
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdownMenu {
position: absolute;
left: 100%;
top: -1px;
display: none;
border-bottom: 2px solid var(--post-menu-border-color);
border-right: 1px solid var(--post-menu-border-color);
}
.dropdown a {
text-decoration: none;
color: inherit;
}
.dropdown:hover .dropdownMenu {
display: block;
}
@media (max-width: 640px) {
.postMenuBtn {
width: 1em;
text-align: center;
font-weight: 700;
font-size: 16px;
}
.postMenuBtnWrapper {
position: relative;
padding-right: 12px;
}
}
@media (min-width: 640px) {
.postMenuBtn {
right: -2px;
opacity: var(--post-menu-btn-opacity);
}
.stickyIconWrapper {
padding-right: 18px;
}
}
@@ -0,0 +1,72 @@
import { useState } from 'react';
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
import { Comment } from '@plebbit/plebbit-react-hooks';
import styles from './post-menu.module.css';
const PostMenu = ({ post }: Comment) => {
const { cid, isDescription, isRules, link, subplebbitAddress } = post;
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
const [isClientRedirectMenuOpen, setIsClientRedirectMenuOpen] = useState(false);
const { refs, floatingStyles, context } = useFloating({
placement: 'bottom-start',
open: menuBtnRotated,
onOpenChange: setMenuBtnRotated,
middleware: [offset({ mainAxis: 10, crossAxis: 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
whileElementsMounted: autoUpdate,
});
const click = useClick(context);
const dismiss = useDismiss(context);
const role = useRole(context);
const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]);
const headingId = useId();
const viewOnOtherClientLink = `p/${subplebbitAddress}${isDescription || isRules ? '' : `/c/${cid}`}`;
return (
<>
<span className={styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
<span
className={styles.postMenuBtn}
title='Post menu'
onClick={() => setMenuBtnRotated((prev) => !prev)}
style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
>
</span>
</span>
{menuBtnRotated && (
<FloatingFocusManager context={context} modal={false}>
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
<div
className={`${styles.postMenuItem} ${styles.dropdown}`}
onMouseOver={() => setIsClientRedirectMenuOpen(true)}
onMouseLeave={() => setIsClientRedirectMenuOpen(false)}
>
View on »
{isClientRedirectMenuOpen && (
<div className={styles.dropdownMenu}>
<div className={styles.postMenuItem}>
<a href={`https://seedit.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
Seedit
</a>
</div>
<div className={styles.postMenuItem}>
<a href={`https://plebones.netlify.app/#/${viewOnOtherClientLink}`} target='_blank' rel='noreferrer'>
Plebones
</a>
</div>
</div>
)}
</div>
</div>
</FloatingFocusManager>
)}
</>
);
};
export default PostMenu;
-49
View File
@@ -131,37 +131,6 @@
color: var(--button-desktop-text-color-hover);
}
.postMenuBtnWrapper {
position: relative;
display: inline-block;
padding-left: 5px;
}
.replyDesktop .postMenuBtnWrapper {
padding-right: 12px;
}
.replyDesktop .stickyIconWrapper {
padding-right: 18px;
}
.postDesktop .postMenuBtn, .replyDesktop .postMenuBtn {
color: var(--post-menu-button-color);
transition: transform 0.1s;
position: absolute;
opacity: var(--post-menu-btn-opacity);
bottom: -4px;
}
.replyDesktop .postMenuBtn {
right: -2px;
}
.postDesktop .postMenuBtn:hover, .replyDesktop .postMenuBtn:hover {
cursor: pointer;
color: var(--post-menu-button-color-hover);
}
.postDesktop .spacer {
width: 100%;
padding: 5px;
@@ -260,20 +229,6 @@
clear: left;
}
.postMobile .postMenuBtn {
color: var(--post-mobile-menu-button-color);
line-height: 1em;
width: 1em;
text-align: center;
transition: transform 0.1s;
transform: rotate(90deg);
-webkit-transform: rotate(90deg);
float: left;
font-weight: 700;
font-size: 16px;
height: 100%;
}
.postMobile .nameBlock {
clear: none;
}
@@ -408,10 +363,6 @@
color: var(--post-mobile-info-text-color);
}
.replyMobile .postInfo .postMenuBtn {
width: 1em;
}
.replyMobile .nameBlock {
display: inline;
float: left;