mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(edit-menu): render mod edit modal in FloatingPortal to escape reply card containment
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import { autoUpdate, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
import { autoUpdate, FloatingFocusManager, FloatingPortal, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
|
||||||
import {
|
import {
|
||||||
Comment,
|
Comment,
|
||||||
PublishCommentEditOptions,
|
PublishCommentEditOptions,
|
||||||
@@ -271,135 +271,137 @@ const EditMenu = ({ post }: { post: Comment }) => {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
{isEditMenuOpen && (isAccountCommentAuthor || isAccountMod) && (
|
{isEditMenuOpen && (isAccountCommentAuthor || isAccountMod) && (
|
||||||
<FloatingFocusManager context={context} modal={false}>
|
<FloatingPortal>
|
||||||
<div className={styles.modal} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
<FloatingFocusManager context={context} modal={false}>
|
||||||
<div className={styles.editMenu}>
|
<div className={styles.modal} ref={refs.setFloating} style={floatingStyles} aria-labelledby={headingId} {...getFloatingProps()}>
|
||||||
{isAccountCommentAuthor && (
|
<div className={styles.editMenu}>
|
||||||
<>
|
{isAccountCommentAuthor && (
|
||||||
<div className={styles.menuItem}>
|
<>
|
||||||
<label>
|
<div className={styles.menuItem}>
|
||||||
[
|
|
||||||
<input onChange={onCheckbox} checked={publishCommentEditOptions.deleted ?? false} type='checkbox' id='deleted' />
|
|
||||||
{capitalize(t('delete'))}?]
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className={styles.menuItem}>
|
|
||||||
<label>
|
|
||||||
[
|
|
||||||
<input type='checkbox' onChange={() => setIsContentEditorOpen(!isContentEditorOpen)} checked={isContentEditorOpen} />
|
|
||||||
{capitalize(t('edit'))}?]
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{isContentEditorOpen && (
|
|
||||||
<div>
|
|
||||||
<textarea
|
|
||||||
className={styles.editTextarea}
|
|
||||||
value={publishCommentEditOptions.content || ''}
|
|
||||||
onChange={(e) => {
|
|
||||||
const newContent = e.target.value;
|
|
||||||
setPublishCommentEditOptions((state) => ({ ...state, content: newContent }));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isAccountMod && (
|
|
||||||
<>
|
|
||||||
<div className={styles.menuItem}>
|
|
||||||
<label>
|
|
||||||
[
|
|
||||||
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.removed ?? false} type='checkbox' id='removed' />
|
|
||||||
{capitalize(t('remove'))}?]
|
|
||||||
</label>{' '}
|
|
||||||
<span className={styles.purgeItem}>
|
|
||||||
<label>
|
<label>
|
||||||
[
|
[
|
||||||
<input onChange={onPurgeCheckbox} checked={publishCommentEditOptions.commentModeration?.purged ?? false} type='checkbox' id='purged' />
|
<input onChange={onCheckbox} checked={publishCommentEditOptions.deleted ?? false} type='checkbox' id='deleted' />
|
||||||
{capitalize(t('purge'))}?]
|
{capitalize(t('delete'))}?]
|
||||||
</label>
|
</label>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{!parentCid && (
|
|
||||||
<div className={styles.menuItem}>
|
|
||||||
[
|
|
||||||
<label>
|
|
||||||
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.locked ?? false} type='checkbox' id='locked' />
|
|
||||||
{capitalize(t('close_thread'))}?
|
|
||||||
</label>
|
|
||||||
]
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div className={styles.menuItem}>
|
|
||||||
[
|
|
||||||
<label>
|
|
||||||
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.spoiler ?? false} type='checkbox' id='spoiler' />
|
|
||||||
{capitalize(t('spoiler'))}?
|
|
||||||
</label>
|
|
||||||
]
|
|
||||||
</div>
|
|
||||||
<div className={styles.menuItem}>
|
|
||||||
[
|
|
||||||
<label>
|
|
||||||
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.pinned ?? false} type='checkbox' id='pinned' />
|
|
||||||
{capitalize(t('sticky'))}?
|
|
||||||
</label>
|
|
||||||
]
|
|
||||||
</div>
|
|
||||||
{!isCommentAuthorMod && isAccountMod && !isAccountCommentAuthor && (
|
|
||||||
<div className={styles.menuItem}>
|
<div className={styles.menuItem}>
|
||||||
[
|
|
||||||
<label>
|
<label>
|
||||||
<input
|
[
|
||||||
onChange={onCheckbox}
|
<input type='checkbox' onChange={() => setIsContentEditorOpen(!isContentEditorOpen)} checked={isContentEditorOpen} />
|
||||||
checked={publishCommentEditOptions.commentModeration?.author?.banExpiresAt !== undefined}
|
{capitalize(t('edit'))}?]
|
||||||
type='checkbox'
|
</label>
|
||||||
id='banUser'
|
</div>
|
||||||
/>
|
{isContentEditorOpen && (
|
||||||
<Trans
|
<div>
|
||||||
i18nKey='ban_user_for'
|
<textarea
|
||||||
shouldUnescape={true}
|
className={styles.editTextarea}
|
||||||
components={{
|
value={publishCommentEditOptions.content || ''}
|
||||||
1: (
|
onChange={(e) => {
|
||||||
<input
|
const newContent = e.target.value;
|
||||||
key='ban-duration-input'
|
setPublishCommentEditOptions((state) => ({ ...state, content: newContent }));
|
||||||
className={styles.banInput}
|
|
||||||
onChange={onBanDurationChange}
|
|
||||||
type='number'
|
|
||||||
min={1}
|
|
||||||
max={100}
|
|
||||||
value={banDuration || ''}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
?
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAccountMod && (
|
||||||
|
<>
|
||||||
|
<div className={styles.menuItem}>
|
||||||
|
<label>
|
||||||
|
[
|
||||||
|
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.removed ?? false} type='checkbox' id='removed' />
|
||||||
|
{capitalize(t('remove'))}?]
|
||||||
|
</label>{' '}
|
||||||
|
<span className={styles.purgeItem}>
|
||||||
|
<label>
|
||||||
|
[
|
||||||
|
<input onChange={onPurgeCheckbox} checked={publishCommentEditOptions.commentModeration?.purged ?? false} type='checkbox' id='purged' />
|
||||||
|
{capitalize(t('purge'))}?]
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{!parentCid && (
|
||||||
|
<div className={styles.menuItem}>
|
||||||
|
[
|
||||||
|
<label>
|
||||||
|
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.locked ?? false} type='checkbox' id='locked' />
|
||||||
|
{capitalize(t('close_thread'))}?
|
||||||
|
</label>
|
||||||
|
]
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.menuItem}>
|
||||||
|
[
|
||||||
|
<label>
|
||||||
|
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.spoiler ?? false} type='checkbox' id='spoiler' />
|
||||||
|
{capitalize(t('spoiler'))}?
|
||||||
</label>
|
</label>
|
||||||
]
|
]
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className={styles.menuItem}>
|
||||||
</>
|
[
|
||||||
)}
|
<label>
|
||||||
<div className={`${styles.menuItem} ${styles.menuReason}`}>
|
<input onChange={onCheckbox} checked={publishCommentEditOptions.commentModeration?.pinned ?? false} type='checkbox' id='pinned' />
|
||||||
{capitalize(t('reason'))}? ({t('optional')})
|
{capitalize(t('sticky'))}?
|
||||||
<input
|
</label>
|
||||||
type='text'
|
]
|
||||||
value={publishCommentEditOptions.reason || ''}
|
</div>
|
||||||
onChange={(e) => {
|
{!isCommentAuthorMod && isAccountMod && !isAccountCommentAuthor && (
|
||||||
const newReason = e.target.value;
|
<div className={styles.menuItem}>
|
||||||
setPublishCommentEditOptions((state) => ({ ...state, reason: newReason }));
|
[
|
||||||
}}
|
<label>
|
||||||
size={14}
|
<input
|
||||||
/>
|
onChange={onCheckbox}
|
||||||
</div>
|
checked={publishCommentEditOptions.commentModeration?.author?.banExpiresAt !== undefined}
|
||||||
<div className={styles.bottom}>
|
type='checkbox'
|
||||||
<button className={isMobile ? 'button' : ''} onClick={_publishCommentEdit}>
|
id='banUser'
|
||||||
{t('save')}
|
/>
|
||||||
</button>
|
<Trans
|
||||||
|
i18nKey='ban_user_for'
|
||||||
|
shouldUnescape={true}
|
||||||
|
components={{
|
||||||
|
1: (
|
||||||
|
<input
|
||||||
|
key='ban-duration-input'
|
||||||
|
className={styles.banInput}
|
||||||
|
onChange={onBanDurationChange}
|
||||||
|
type='number'
|
||||||
|
min={1}
|
||||||
|
max={100}
|
||||||
|
value={banDuration || ''}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
?
|
||||||
|
</label>
|
||||||
|
]
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className={`${styles.menuItem} ${styles.menuReason}`}>
|
||||||
|
{capitalize(t('reason'))}? ({t('optional')})
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
value={publishCommentEditOptions.reason || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newReason = e.target.value;
|
||||||
|
setPublishCommentEditOptions((state) => ({ ...state, reason: newReason }));
|
||||||
|
}}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.bottom}>
|
||||||
|
<button className={isMobile ? 'button' : ''} onClick={_publishCommentEdit}>
|
||||||
|
{t('save')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</FloatingFocusManager>
|
||||||
</FloatingFocusManager>
|
</FloatingPortal>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user