mirror of
https://github.com/mastodon/mastodon.git
synced 2026-09-12 21:37:34 -05:00
Redesign: Menu and modal fixes (#40220)
This commit is contained in:
@@ -57,7 +57,11 @@ export const ModalTitle: React.FC<
|
||||
const Header = `h${level}` as const;
|
||||
if (!noFocus) {
|
||||
return (
|
||||
<NavigationFocusTarget as={Header} {...props}>
|
||||
<NavigationFocusTarget
|
||||
as={Header}
|
||||
{...props}
|
||||
className={classNames(className, classes.title)}
|
||||
>
|
||||
{children}
|
||||
</NavigationFocusTarget>
|
||||
);
|
||||
|
||||
@@ -59,12 +59,13 @@ export const ComposeHints = () => {
|
||||
defaultMessage="You're replying to a followers-only post. People not following {user} might see your reply without the context of what you’re replying to."
|
||||
values={{ user: `@${replyFollowersHandle}` }}
|
||||
/>,
|
||||
'followers-reply',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isDifferentLanguage) {
|
||||
messages.push(<LanguageHint guess={guess} />);
|
||||
messages.push(<LanguageHint guess={guess} key='different-language' />);
|
||||
}
|
||||
|
||||
if (missingAlt && count === 1) {
|
||||
@@ -74,6 +75,7 @@ export const ComposeHints = () => {
|
||||
id='compose.hints.missing-alt'
|
||||
defaultMessage='Your attachment is missing alt text.'
|
||||
/>,
|
||||
'missing-alt',
|
||||
),
|
||||
);
|
||||
} else if (missingAlt) {
|
||||
@@ -83,6 +85,7 @@ export const ComposeHints = () => {
|
||||
id='compose.hints.missing-alt-many'
|
||||
defaultMessage='One or more of your attachments are missing alt text.'
|
||||
/>,
|
||||
'missing-alts',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -94,8 +97,10 @@ export const ComposeHints = () => {
|
||||
return <div>{messages}</div>;
|
||||
};
|
||||
|
||||
const defaultWrapper = (children: React.ReactNode) => (
|
||||
<Callout icon={WarningIcon}>{children}</Callout>
|
||||
const defaultWrapper = (children: React.ReactNode, key: string) => (
|
||||
<Callout icon={WarningIcon} key={key}>
|
||||
{children}
|
||||
</Callout>
|
||||
);
|
||||
|
||||
const LanguageHint: React.FC<{ guess: string }> = ({ guess }) => {
|
||||
|
||||
@@ -4,6 +4,11 @@ import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { closeModal } from '@/mastodon/actions/modal';
|
||||
import { Button } from '@/mastodon/components/button/redesign';
|
||||
import {
|
||||
ModalActions,
|
||||
ModalShell,
|
||||
ModalTitle,
|
||||
} from '@/mastodon/components/modal_shell/redesign';
|
||||
import {
|
||||
focusComposerTextarea,
|
||||
openNewComposer,
|
||||
@@ -11,8 +16,6 @@ import {
|
||||
} from '@/mastodon/reducers/slices/composer';
|
||||
import { useAppDispatch } from '@/mastodon/store';
|
||||
|
||||
import classes from './modals.module.scss';
|
||||
|
||||
const ComposerModalCancelConfirm: React.FC<{ openNew?: boolean }> = ({
|
||||
openNew,
|
||||
}) => {
|
||||
@@ -37,20 +40,20 @@ const ComposerModalCancelConfirm: React.FC<{ openNew?: boolean }> = ({
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<h2 className={classes.title}>
|
||||
<ModalShell>
|
||||
<ModalTitle>
|
||||
<FormattedMessage
|
||||
id='compose.cancel_modal.title'
|
||||
defaultMessage='Discard draft'
|
||||
/>
|
||||
</h2>
|
||||
</ModalTitle>
|
||||
|
||||
<FormattedMessage
|
||||
id='compose.cancel_modal.body'
|
||||
defaultMessage='You have a draft already in progress. What would you like to do?'
|
||||
/>
|
||||
|
||||
<div className={classes.footer}>
|
||||
<ModalActions>
|
||||
<Button color='destructive' onClick={handleDelete}>
|
||||
<FormattedMessage
|
||||
id='compose.cancel_modal.delete'
|
||||
@@ -63,8 +66,8 @@ const ComposerModalCancelConfirm: React.FC<{ openNew?: boolean }> = ({
|
||||
defaultMessage='Continue draft'
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalActions>
|
||||
</ModalShell>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ import { DotsSixVerticalIcon } from '@phosphor-icons/react';
|
||||
|
||||
import { rearrangeComposeAttachments } from '@/mastodon/actions/compose_typed';
|
||||
import { Button, IconButton } from '@/mastodon/components/button/redesign';
|
||||
import {
|
||||
ModalActions,
|
||||
ModalShell,
|
||||
ModalTitle,
|
||||
} from '@/mastodon/components/modal_shell/redesign';
|
||||
import {
|
||||
SortableList,
|
||||
SortableListItem,
|
||||
@@ -28,26 +33,26 @@ const messages = defineMessages({
|
||||
},
|
||||
onDragStart: {
|
||||
id: 'compose.rearrange_modal.drag_start',
|
||||
defaultMessage: 'Picked up attachment at index {index, number}.',
|
||||
defaultMessage: 'Picked up attachment at index {item, number}.',
|
||||
},
|
||||
onDragMove: {
|
||||
id: 'compose.rearrange_modal.drag_move',
|
||||
defaultMessage: 'Attachment index {index, number} was moved.',
|
||||
defaultMessage: 'Attachment index {item, number} was moved.',
|
||||
},
|
||||
onDragMoveOver: {
|
||||
id: 'compose.rearrange_modal.drag_over',
|
||||
defaultMessage:
|
||||
'Attachment index {index, number} was moved over index {over, number}.',
|
||||
'Attachment index {item, number} was moved over index {over, number}.',
|
||||
},
|
||||
onDragEnd: {
|
||||
id: 'compose.rearrange_modal.drag_end',
|
||||
defaultMessage:
|
||||
'Attachment index {index, number} was moved to index {newIndex, number}.',
|
||||
'Attachment index {item, number} was moved to index {over, number}.',
|
||||
},
|
||||
onDragCancel: {
|
||||
id: 'compose.rearrange_modal.drag_cancel',
|
||||
defaultMessage:
|
||||
'Dragging was cancelled. Attachment index {index, number} was dropped.',
|
||||
'Dragging was cancelled. Attachment index {item, number} was dropped.',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -80,16 +85,13 @@ const ComposerModalRearrange: React.FC<{ onClose: () => void }> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(classes.root, classes.attachmentRoot)}
|
||||
onKeyUpCapture={onModalExit}
|
||||
>
|
||||
<h2 className={classes.title}>
|
||||
<ModalShell className={classes.attachmentRoot} onKeyUpCapture={onModalExit}>
|
||||
<ModalTitle className={classes.title}>
|
||||
<FormattedMessage
|
||||
id='compose.rearrange_modal.title'
|
||||
defaultMessage='Rearrange media'
|
||||
/>
|
||||
</h2>
|
||||
</ModalTitle>
|
||||
|
||||
<SortableList
|
||||
ids={attachmentIds}
|
||||
@@ -117,7 +119,7 @@ const ComposerModalRearrange: React.FC<{ onClose: () => void }> = ({
|
||||
))}
|
||||
</SortableList>
|
||||
|
||||
<div className={classes.footer}>
|
||||
<ModalActions className={classes.footer}>
|
||||
<Button onClick={onClose}>
|
||||
<FormattedMessage
|
||||
id='compose.rearrange_modal.cancel'
|
||||
@@ -131,8 +133,8 @@ const ComposerModalRearrange: React.FC<{ onClose: () => void }> = ({
|
||||
defaultMessage='Save'
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalActions>
|
||||
</ModalShell>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,29 +1,5 @@
|
||||
@use '@/styles/mastodon/mixins';
|
||||
|
||||
.root {
|
||||
@include mixins.elevation-2;
|
||||
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--color-bg-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md);
|
||||
pointer-events: auto;
|
||||
user-select: text;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include mixins.type-heading-sm;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// Attachment rearrange modal
|
||||
|
||||
.attachmentRoot {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type React from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
@@ -13,11 +13,13 @@ import type { ApiAudioAttachmentJSON } from '@/mastodon/api_types/media_attachme
|
||||
import { Blurhash } from '@/mastodon/components/blurhash';
|
||||
import { IconButton } from '@/mastodon/components/button/redesign';
|
||||
import {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuItemDivider,
|
||||
MenuList,
|
||||
useMenuContext,
|
||||
} from '@/mastodon/components/menu';
|
||||
import { useToggle } from '@/mastodon/hooks/useToggle';
|
||||
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
|
||||
|
||||
import classes from './attachments.module.scss';
|
||||
@@ -33,26 +35,6 @@ export const ComposeUpload: React.FC<{
|
||||
selectComposeAttachment(state, id),
|
||||
);
|
||||
const sensitive = useAppSelector((state) => !!state.compose.get('spoiler'));
|
||||
const [open, { onToggle, onFalse }] = useToggle();
|
||||
const [target, setTarget] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleEdit = useCallback(() => {
|
||||
if (id) {
|
||||
dispatch(
|
||||
openModal({ modalType: 'FOCAL_POINT', modalProps: { mediaId: id } }),
|
||||
);
|
||||
}
|
||||
}, [dispatch, id]);
|
||||
const handleRearrange = useCallback(() => {
|
||||
onFalse();
|
||||
dispatch(openModal({ modalType: 'COMPOSER_REARRANGE', modalProps: {} }));
|
||||
}, [dispatch, onFalse]);
|
||||
const handleDelete = useCallback(() => {
|
||||
if (id) {
|
||||
dispatch(undoUploadCompose(id));
|
||||
}
|
||||
}, [dispatch, id]);
|
||||
|
||||
if (!attachment || attachment.type === 'unknown') {
|
||||
return <div className={classNames(classes.mediaUpload, className)} />;
|
||||
@@ -90,64 +72,22 @@ export const ComposeUpload: React.FC<{
|
||||
<Blurhash hash={attachment.blurhash} className={classes.blurHash} />
|
||||
)}
|
||||
|
||||
<IconButton
|
||||
icon={DotsThreeIcon}
|
||||
size='sm'
|
||||
color='neutral'
|
||||
className={classes.mediaMenuButton}
|
||||
onClick={onToggle}
|
||||
ref={setTarget}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu'
|
||||
defaultMessage='Add alt text or remove the image'
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<MenuList
|
||||
isOpen={open}
|
||||
onClose={onFalse}
|
||||
reference={target}
|
||||
placement='bottom-end'
|
||||
offset={4}
|
||||
maxWidth={170}
|
||||
>
|
||||
<MenuItem onClick={handleEdit}>
|
||||
{attachment.description ? (
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.edit_alt'
|
||||
defaultMessage='Edit alt text'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.add_alt'
|
||||
defaultMessage='Add alt text'
|
||||
/>
|
||||
)}
|
||||
</MenuItem>
|
||||
|
||||
{!single && (
|
||||
<MenuItem onClick={handleRearrange}>
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.rearrange'
|
||||
defaultMessage='Rearrange…'
|
||||
/>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<MenuItemDivider />
|
||||
|
||||
<MenuItem
|
||||
className={classes.mediaMenuDelete}
|
||||
onClick={handleDelete}
|
||||
icon={TrashIcon}
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
icon={DotsThreeIcon}
|
||||
size='sm'
|
||||
color='neutral'
|
||||
className={classes.mediaMenuButton}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.delete'
|
||||
defaultMessage='Remove image'
|
||||
id='compose.upload.menu'
|
||||
defaultMessage='Add alt text or remove the image'
|
||||
/>
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</MenuButton>
|
||||
|
||||
<ComposeUploadMenu attachment={attachment} single={single} />
|
||||
</Menu>
|
||||
|
||||
{attachment.description && (
|
||||
<span className={classes.mediaAlt}>
|
||||
@@ -158,6 +98,71 @@ export const ComposeUpload: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const ComposeUploadMenu: React.FC<{
|
||||
attachment: ComposeAttachment;
|
||||
single?: boolean;
|
||||
}> = ({ attachment, single }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const id = attachment.id;
|
||||
|
||||
const { popover } = useMenuContext();
|
||||
|
||||
const handleEdit = useCallback(() => {
|
||||
popover.closeMenu();
|
||||
dispatch(
|
||||
openModal({ modalType: 'FOCAL_POINT', modalProps: { mediaId: id } }),
|
||||
);
|
||||
}, [dispatch, id, popover]);
|
||||
const handleRearrange = useCallback(() => {
|
||||
popover.closeMenu();
|
||||
dispatch(openModal({ modalType: 'COMPOSER_REARRANGE', modalProps: {} }));
|
||||
}, [dispatch, popover]);
|
||||
const handleDelete = useCallback(() => {
|
||||
popover.closeMenu();
|
||||
dispatch(undoUploadCompose(id));
|
||||
}, [dispatch, id, popover]);
|
||||
|
||||
return (
|
||||
<MenuList placement='bottom-end' offset={4} maxWidth={170}>
|
||||
<MenuItem onClick={handleEdit}>
|
||||
{attachment.description ? (
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.edit_alt'
|
||||
defaultMessage='Edit alt text'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.add_alt'
|
||||
defaultMessage='Add alt text'
|
||||
/>
|
||||
)}
|
||||
</MenuItem>
|
||||
|
||||
{!single && (
|
||||
<MenuItem onClick={handleRearrange}>
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.rearrange'
|
||||
defaultMessage='Rearrange…'
|
||||
/>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<MenuItemDivider />
|
||||
|
||||
<MenuItem
|
||||
className={classes.mediaMenuDelete}
|
||||
onClick={handleDelete}
|
||||
icon={TrashIcon}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='compose.upload.menu.delete'
|
||||
defaultMessage='Remove image'
|
||||
/>
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
);
|
||||
};
|
||||
|
||||
const ComposeAudioUpload: React.FC<{
|
||||
attachment: ComposeAttachment<ApiAudioAttachmentJSON>;
|
||||
}> = ({ attachment }) => {
|
||||
|
||||
@@ -524,12 +524,12 @@
|
||||
"compose.published.open": "Open",
|
||||
"compose.quotable": "Allow others to quote",
|
||||
"compose.rearrange_modal.cancel": "Cancel",
|
||||
"compose.rearrange_modal.drag_cancel": "Dragging was cancelled. Attachment index {index, number} was dropped.",
|
||||
"compose.rearrange_modal.drag_end": "Attachment index {index, number} was moved to index {newIndex, number}.",
|
||||
"compose.rearrange_modal.drag_cancel": "Dragging was cancelled. Attachment index {item, number} was dropped.",
|
||||
"compose.rearrange_modal.drag_end": "Attachment index {item, number} was moved to index {over, number}.",
|
||||
"compose.rearrange_modal.drag_instructions": "To rearrange attachments, press space or enter. While dragging, use the arrow keys to move the attachment up or down. Press space or enter again to drop the attachment in its new position, or press escape to cancel.",
|
||||
"compose.rearrange_modal.drag_move": "Attachment index {index, number} was moved.",
|
||||
"compose.rearrange_modal.drag_over": "Attachment index {index, number} was moved over index {over, number}.",
|
||||
"compose.rearrange_modal.drag_start": "Picked up attachment at index {index, number}.",
|
||||
"compose.rearrange_modal.drag_move": "Attachment index {item, number} was moved.",
|
||||
"compose.rearrange_modal.drag_over": "Attachment index {item, number} was moved over index {over, number}.",
|
||||
"compose.rearrange_modal.drag_start": "Picked up attachment at index {item, number}.",
|
||||
"compose.rearrange_modal.handle": "Drag attachment at position {index, number}",
|
||||
"compose.rearrange_modal.save": "Save",
|
||||
"compose.rearrange_modal.title": "Rearrange media",
|
||||
|
||||
Reference in New Issue
Block a user