feat(catalog): add post menu

This commit is contained in:
Tom (plebeius.eth)
2024-05-28 16:44:56 +02:00
parent 2859baf9c2
commit 572eb3b4e2
4 changed files with 58 additions and 82 deletions
@@ -79,36 +79,10 @@
text-decoration: none;
}
.postMenuBtnPadding {
left: 5px;
top: 5px;
position: relative;
.post .postMenu {
visibility: hidden;
}
.postMenuBtn {
position: absolute;
bottom: 5px;
line-height: 1em;
color: var(--catalog-post-menu-btn-color);
opacity: 0;
transition: transform 0.1s;
}
.post:hover .postMenuBtn {
opacity: var(--catalog-post-menu-btn-opacity);
}
.post:hover .postMenuBtn:hover {
cursor: pointer;
color: var(--catalog-post-menu-btn-hover-color);
opacity: var(--catalog-post-menu-btn-hover-opacity);
}
.postMenuBtn {
/* preserve unicode triangle for button, some browsers will use an emoji */
font-variant: normal;
font-feature-settings: "tnum";
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
-moz-font-feature-settings: "tnum"; /* Firefox */
font-variant-numeric: tabular-nums;
.post:hover .postMenu {
visibility: visible;
}
+5 -40
View File
@@ -1,4 +1,3 @@
import { useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Comment } from '@plebbit/plebbit-react-hooks';
@@ -7,6 +6,7 @@ import { isAllView } from '../../lib/utils/view-utils';
import useFetchGifFirstFrame from '../../hooks/use-fetch-gif-first-frame';
import useCountLinksInReplies from '../../hooks/use-count-links-in-replies';
import styles from './catalog-row.module.css';
import PostMenu from '../post/post-menu/post-menu';
export const CatalogPostMedia = ({ commentMediaInfo }: { commentMediaInfo: any }) => {
const { patternThumbnailUrl, thumbnail, type, url } = commentMediaInfo || {};
@@ -30,13 +30,7 @@ export const CatalogPostMedia = ({ commentMediaInfo }: { commentMediaInfo: any }
return thumbnailComponent;
};
interface CatalogPostProps {
post: Comment;
openMenu: boolean;
toggleMenu: () => void;
}
const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
const CatalogPost = ({ post }: { post: Comment }) => {
const { t } = useTranslation();
const { cid, content, isDescription, isRules, link, linkHeight, linkWidth, locked, pinned, replyCount, subplebbitAddress, title } = post || {};
const commentMediaInfo = getCommentMediaInfo(post);
@@ -76,19 +70,6 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
</div>
);
useEffect(() => {
const handlePageClick = () => {
if (openMenu) {
toggleMenu();
}
};
document.addEventListener('click', handlePageClick);
return () => {
document.removeEventListener('click', handlePageClick);
};
}, [openMenu, toggleMenu]);
return (
<div className={styles.post}>
{hasThumbnail ? (
@@ -111,18 +92,8 @@ const CatalogPost = ({ openMenu, post, toggleMenu }: CatalogPostProps) => {
/ L: <b>{linkCount}</b>
</span>
)}
<span className={styles.postMenuBtnPadding}>
<span
className={styles.postMenuBtn}
title='Thread Menu'
onClick={(e) => {
e.stopPropagation();
toggleMenu();
}}
style={{ transform: openMenu ? 'rotate(90deg)' : 'rotate(0deg)' }}
>
</span>
<span className={styles.postMenu}>
<PostMenu post={post} />
</span>
</div>
<Link to={postLink}>
@@ -141,16 +112,10 @@ interface CatalogRowProps {
}
const CatalogRow = ({ row }: CatalogRowProps) => {
const [postWithMenuOpen, setPostWithMenuOpen] = useState<number | null>(null);
const handleToggleMenu = (index: number) => {
setPostWithMenuOpen(postWithMenuOpen === index ? null : index);
};
return (
<div className={styles.row}>
{row.map((post, index) => (
<CatalogPost key={index} post={post} openMenu={postWithMenuOpen === index} toggleMenu={() => handleToggleMenu(index)} />
<CatalogPost key={index} post={post} />
))}
</div>
);
@@ -60,6 +60,36 @@
display: block;
}
.postMenuBtnCatalogWrapper {
left: 5px;
top: 5px;
position: relative;
}
.postMenuBtnCatalog {
position: absolute;
bottom: 5px;
line-height: 1em;
color: var(--catalog-post-menu-btn-color);
opacity: var(--catalog-post-menu-btn-opacity);
transition: transform 0.1s;
}
.postMenuBtnCatalog:hover {
cursor: pointer;
color: var(--catalog-post-menu-btn-hover-color);
opacity: var(--catalog-post-menu-btn-hover-opacity);
}
.postMenuBtnCatalog {
/* preserve unicode triangle for button, some browsers will use an emoji */
font-variant: normal;
font-feature-settings: "tnum";
-webkit-font-feature-settings: "tnum"; /* Chrome, Opera, and Safari */
-moz-font-feature-settings: "tnum"; /* Firefox */
font-variant-numeric: tabular-nums;
}
@media (max-width: 640px) {
.postMenuBtn {
width: 1em;
+19 -12
View File
@@ -1,9 +1,12 @@
import { useState } from 'react';
import { createPortal } from 'react-dom';
import { useLocation } from 'react-router-dom';
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';
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils';
import { isCatalogView } from '../../../lib/utils/view-utils';
interface PostMenuProps {
cid: string;
@@ -84,11 +87,13 @@ const PostMenu = ({ post }: { post: Comment }) => {
const { thumbnail, type, url } = commentMediaInfo || {};
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
const isInCatalogView = isCatalogView(useLocation().pathname);
const { refs, floatingStyles, context } = useFloating({
placement: 'bottom-start',
open: menuBtnRotated,
onOpenChange: setMenuBtnRotated,
middleware: [offset({ mainAxis: 10, crossAxis: 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
middleware: [offset({ mainAxis: isInCatalogView ? -2 : 6, crossAxis: isInCatalogView ? -1 : 5 }), flip({ fallbackAxisSideDirection: 'end' }), shift({ padding: 10 })],
whileElementsMounted: autoUpdate,
});
const click = useClick(context);
@@ -101,9 +106,9 @@ const PostMenu = ({ post }: { post: Comment }) => {
return (
<>
<span className={styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
<span className={isInCatalogView ? styles.postMenuBtnCatalogWrapper : styles.postMenuBtnWrapper} ref={refs.setReference} {...getReferenceProps()}>
<span
className={styles.postMenuBtn}
className={isInCatalogView ? styles.postMenuBtnCatalog : styles.postMenuBtn}
title='Post menu'
onClick={() => setMenuBtnRotated((prev) => !prev)}
style={{ transform: menuBtnRotated ? 'rotate(90deg)' : 'rotate(0deg)' }}
@@ -111,15 +116,17 @@ const PostMenu = ({ post }: { post: Comment }) => {
</span>
</span>
{menuBtnRotated && (
<FloatingFocusManager context={context} modal={false}>
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
</div>
</FloatingFocusManager>
)}
{menuBtnRotated &&
createPortal(
<FloatingFocusManager context={context} modal={false}>
<div className={styles.postMenu} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
{cid && subplebbitAddress && <CopyLinkButton cid={cid} subplebbitAddress={subplebbitAddress} onClose={handleClose} />}
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && <ImageSearchButton url={url} onClose={handleClose} />}
<ViewOnButton cid={cid} isDescription={isDescription} isRules={isRules} subplebbitAddress={subplebbitAddress} onClose={handleClose} />
</div>
</FloatingFocusManager>,
document.body,
)}
</>
);
};