All checks were successful
Build and Release / build-and-release (push) Successful in 13m12s
- Implemented Button component with various props for customization. - Created Modal component with header, content, and footer subcomponents. - Added Spinner component for loading indicators. - Developed Toast component for displaying notifications. - Introduced Tooltip component for contextual hints with keyboard shortcuts. - Added corresponding CSS modules for styling each component. - Updated index file to export new components. - Configured TypeScript settings for the UI package.
129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
/**
|
|
* MobileImageActionsSheet — three-dots menu opened from the mobile
|
|
* ImageLightbox header. Renders a Fluxer-style grouped action list
|
|
* inside a `BottomSheet`, same visual idiom as the existing
|
|
* MobileMessageActionsSheet so the two sheets feel like siblings.
|
|
*
|
|
* Group layout (matches the user's spec, not the richer reference):
|
|
* 1. Favorite / Remove from Favorites (single-row group)
|
|
* 2. Save Image • Open Link (two-row group)
|
|
* 3. Delete Message (danger, single-row group)
|
|
*
|
|
* Each row only renders when its callback is actually provided so
|
|
* callers with limited capability (e.g. the pinned-message popover,
|
|
* which can't delete the source message) can drop rows without
|
|
* leaving empty groups behind.
|
|
*/
|
|
import { BottomSheet } from '@brycord/ui';
|
|
import {
|
|
Star,
|
|
Download,
|
|
ArrowSquareOut,
|
|
Trash,
|
|
} from '@phosphor-icons/react';
|
|
import styles from './MobileImageActionsSheet.module.css';
|
|
|
|
interface MobileImageActionsSheetProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
isFavorited: boolean;
|
|
onToggleFavorite?: () => void;
|
|
onSaveImage?: () => void;
|
|
onOpenLink?: () => void;
|
|
/** When provided, a red "Delete Message" row is appended at the
|
|
* bottom. Lets callers hide the row entirely when the current
|
|
* user doesn't have permission or when the lightbox is rendered
|
|
* from a context with no parent message (pinned popover). */
|
|
onDeleteMessage?: () => void;
|
|
}
|
|
|
|
export function MobileImageActionsSheet({
|
|
isOpen,
|
|
onClose,
|
|
isFavorited,
|
|
onToggleFavorite,
|
|
onSaveImage,
|
|
onOpenLink,
|
|
onDeleteMessage,
|
|
}: MobileImageActionsSheetProps) {
|
|
// Wrap each action so tapping always closes the sheet after the
|
|
// handler fires. Mirrors the pattern in MobileMessageActionsSheet.
|
|
const wrap = (fn?: () => void) => () => {
|
|
if (!fn) return;
|
|
fn();
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<BottomSheet
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
disableDefaultHeader
|
|
showHandle
|
|
initialHeightSvh={40}
|
|
expandable
|
|
zIndex={17000}
|
|
>
|
|
<div className={styles.body}>
|
|
<div className={styles.title}>Media Options</div>
|
|
|
|
{onToggleFavorite && (
|
|
<div className={styles.actionList}>
|
|
<button
|
|
type="button"
|
|
className={styles.actionItem}
|
|
onClick={wrap(onToggleFavorite)}
|
|
>
|
|
<Star
|
|
size={20}
|
|
weight={isFavorited ? 'fill' : 'regular'}
|
|
/>
|
|
<span>
|
|
{isFavorited ? 'Remove from Favorites' : 'Add to Favorites'}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{(onSaveImage || onOpenLink) && (
|
|
<div className={styles.actionList}>
|
|
{onSaveImage && (
|
|
<button
|
|
type="button"
|
|
className={styles.actionItem}
|
|
onClick={wrap(onSaveImage)}
|
|
>
|
|
<Download size={20} weight="regular" />
|
|
<span>Save Image</span>
|
|
</button>
|
|
)}
|
|
{onOpenLink && (
|
|
<button
|
|
type="button"
|
|
className={styles.actionItem}
|
|
onClick={wrap(onOpenLink)}
|
|
>
|
|
<ArrowSquareOut size={20} weight="regular" />
|
|
<span>Open Link</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{onDeleteMessage && (
|
|
<div className={styles.actionList}>
|
|
<button
|
|
type="button"
|
|
className={`${styles.actionItem} ${styles.actionItemDanger}`}
|
|
onClick={wrap(onDeleteMessage)}
|
|
>
|
|
<Trash size={20} weight="fill" />
|
|
<span>Delete Message</span>
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</BottomSheet>
|
|
);
|
|
}
|