useContextMenu hook
This commit is contained in:
parent
2bae6c72cd
commit
54536c1b03
@ -4,11 +4,10 @@ import CombinedGuild from '../../guild-combined';
|
||||
import ChannelOverlay from '../overlays/overlay-channel';
|
||||
import GuildSettingsOverlay from '../overlays/overlay-guild-settings';
|
||||
import BaseElements from '../require/base-elements';
|
||||
import ElementsUtil, { IAlignment } from '../require/elements-util';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import ContextMenu from './components/context-menu';
|
||||
|
||||
export interface GuildTitleContextMenuProps {
|
||||
alignment: IAlignment;
|
||||
close: () => void;
|
||||
relativeToRef: RefObject<HTMLElement>
|
||||
guild: CombinedGuild;
|
||||
@ -17,7 +16,7 @@ export interface GuildTitleContextMenuProps {
|
||||
}
|
||||
|
||||
const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitleContextMenuProps) => {
|
||||
const { alignment, close, relativeToRef, guild, guildMeta, selfMember } = props;
|
||||
const { close, relativeToRef, guild, guildMeta, selfMember } = props;
|
||||
|
||||
const openGuildSettings = useCallback(() => {
|
||||
close();
|
||||
@ -51,6 +50,10 @@ const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitle
|
||||
|
||||
if (guildSettingsElement === null && createChannelElement === null) return null;
|
||||
|
||||
const alignment = useMemo(() => {
|
||||
return { top: 'bottom', centerX: 'centerX' }
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef}>
|
||||
<div className="guild-title-context-menu">
|
||||
|
@ -1,13 +1,11 @@
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import * as electron from 'electron';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
import ContextMenu from './components/context-menu';
|
||||
import * as FileType from 'file-type';
|
||||
import sharp from 'sharp';
|
||||
import { IAlignment } from '../require/elements-util';
|
||||
|
||||
export interface ImageContextMenuProps {
|
||||
alignment: IAlignment;
|
||||
relativeToPos: { x: number, y: number };
|
||||
close: () => void;
|
||||
resourceName: string;
|
||||
@ -16,7 +14,7 @@ export interface ImageContextMenuProps {
|
||||
}
|
||||
|
||||
const ImageContextMenu: FC<ImageContextMenuProps> = (props: ImageContextMenuProps) => {
|
||||
const { alignment, relativeToPos, close, resourceName, resourceBuff, isPreview } = props;
|
||||
const { relativeToPos, close, resourceName, resourceBuff, isPreview } = props;
|
||||
|
||||
const previewText = isPreview ? ' Preview' : '';
|
||||
|
||||
@ -56,6 +54,8 @@ const ImageContextMenu: FC<ImageContextMenuProps> = (props: ImageContextMenuProp
|
||||
}
|
||||
);
|
||||
|
||||
const alignment = useMemo(() => ({ top: 'centerY', left: 'centerX' }), []);
|
||||
|
||||
return (
|
||||
<ContextMenu alignment={alignment} relativeToPos={relativeToPos} close={close}>
|
||||
<div className="image">
|
||||
|
@ -53,7 +53,7 @@ const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageEl
|
||||
const [ imgSrc ] = GuildSubscriptions.useSoftImageSrcResourceSubscription(guild, resourcePreviewId);
|
||||
|
||||
const openImageOverlay = useCallback(() => {
|
||||
// Note: document here isn't 100% guaranteed but we should be getting rid of this eventually anyway
|
||||
// Note: document here isn't 100% guaranteed (I think) but we should be getting rid of this eventually anyway
|
||||
ElementsUtil.presentReactOverlay(document, <ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} />);
|
||||
}, [ guild, resourceId, resourceName ]);
|
||||
|
||||
|
@ -3,7 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useCallback, useMemo, useState, MouseEvent, useRef } from 'react';
|
||||
import React, { FC, useMemo, useState, useRef, MouseEvent, useCallback } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ElementsUtil from '../require/elements-util';
|
||||
import DownloadButton from '../components/button-download';
|
||||
@ -31,33 +31,30 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
'./img/loading.svg',
|
||||
[ guild, resource ]
|
||||
);
|
||||
|
||||
const [ contextMenuOpen, setContextMenuOpen ] = useState<boolean>(false);
|
||||
const alignment = useMemo(() => ({ top: 'centerY', left: 'centerX' }), []);
|
||||
|
||||
const [ relativeToPos, setRelativeToPos ] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
|
||||
|
||||
const contextMenu = useMemo(() => {
|
||||
const [ contextMenu, toggleContextMenu ] = ReactHelper.useContextMenu((close: () => void) => {
|
||||
if (!resource) return null;
|
||||
return (
|
||||
<ImageContextMenu
|
||||
alignment={alignment} relativeToPos={relativeToPos} close={() => { setContextMenuOpen(false); }}
|
||||
relativeToPos={relativeToPos} close={close}
|
||||
resourceName={resourceName} resourceBuff={resource.data} isPreview={false}
|
||||
/>
|
||||
);
|
||||
}, [ resource, alignment, relativeToPos, resourceName ]);
|
||||
}, [ resource, relativeToPos, resourceName ]);
|
||||
|
||||
const toggleContextMenu = useCallback((e: MouseEvent<HTMLImageElement>) => {
|
||||
LOG.debug('toggle context menu');
|
||||
setRelativeToPos({ x: e.clientX, y: e.clientY });
|
||||
setContextMenuOpen(oldContextMenuOpen => !!contextMenu && !oldContextMenuOpen);
|
||||
}, [ contextMenu ]);
|
||||
const onContextMenu = useCallback((event: MouseEvent<HTMLImageElement>) => {
|
||||
setRelativeToPos({ x: event.clientX, y: event.clientY });
|
||||
toggleContextMenu();
|
||||
}, [ toggleContextMenu ]);
|
||||
|
||||
const sizeText = useMemo(() => resource ? ElementsUtil.humanSize(resource.data.length) : 'Loading Size...', [ resource ]);
|
||||
|
||||
return (
|
||||
<Overlay childRootRef={rootRef}>
|
||||
<div ref={rootRef} className="content popup-image">
|
||||
<img src={resourceImgSrc} alt={resourceName} title={resourceName} onContextMenu={toggleContextMenu}></img>
|
||||
<img src={resourceImgSrc} alt={resourceName} title={resourceName} onContextMenu={onContextMenu}></img>
|
||||
<div className="download">
|
||||
<div className="info">
|
||||
<div className="name">{resourceName}</div>
|
||||
@ -67,7 +64,7 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
|
||||
downloadBuff={resource?.data} downloadBuffErr={!!resourceError}
|
||||
resourceName={resourceName}></DownloadButton>
|
||||
</div>
|
||||
{contextMenuOpen ? contextMenu : null}
|
||||
{contextMenu}
|
||||
</div>
|
||||
</Overlay>
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import { DependencyList, Dispatch, MutableRefObject, RefObject, SetStateAction, UIEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { DependencyList, Dispatch, MutableRefObject, ReactNode, RefObject, SetStateAction, UIEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { ShouldNeverHappenError } from "../../data-types";
|
||||
import Util from '../../util';
|
||||
@ -395,4 +395,33 @@ export default class ReactHelper {
|
||||
}
|
||||
}, [ handleMouseUp ]);
|
||||
}
|
||||
|
||||
static useContextMenu(
|
||||
createContextMenu: (close: () => void) => ReactNode,
|
||||
createContextMenuDeps: DependencyList
|
||||
): [
|
||||
contextMenu: ReactNode,
|
||||
toggle: () => void,
|
||||
close: () => void,
|
||||
open: () => void,
|
||||
isOpen: boolean
|
||||
] {
|
||||
const [ isOpen, setIsOpen ] = useState<boolean>(false);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
}, []);
|
||||
const contextMenu = useMemo(() => {
|
||||
return createContextMenu(close);
|
||||
}, createContextMenuDeps);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setIsOpen(oldIsOpen => !!contextMenu && !oldIsOpen);
|
||||
}, [ contextMenu ]);
|
||||
const open = useCallback(() => {
|
||||
setIsOpen(!!contextMenu);
|
||||
}, [ contextMenu ]);
|
||||
|
||||
return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ];
|
||||
}
|
||||
}
|
@ -3,12 +3,13 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { FC, useMemo, useRef } from 'react';
|
||||
import { Member } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import MemberElement, { DummyMember } from '../lists/components/member-element';
|
||||
import GuildSubscriptions from '../require/guild-subscriptions';
|
||||
import ConnectionInfoContextMenu from '../context-menus/context-menu-connection-info';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
|
||||
export interface ConnectionInfoProps {
|
||||
guild: CombinedGuild;
|
||||
@ -19,8 +20,6 @@ const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) =>
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [ contextMenuOpen, setContextMenuOpen ] = useState<boolean>(false);
|
||||
|
||||
const [ selfMember ] = GuildSubscriptions.useSelfMemberSubscription(guild);
|
||||
|
||||
const displayMember = useMemo((): Member | DummyMember => {
|
||||
@ -36,19 +35,19 @@ const ConnectionInfo: FC<ConnectionInfoProps> = (props: ConnectionInfoProps) =>
|
||||
return selfMember;
|
||||
}, [ selfMember ]);
|
||||
|
||||
const contextMenu = useMemo(() => {
|
||||
const [ contextMenu, toggleContextMenu ] = ReactHelper.useContextMenu((close: () => void) => {
|
||||
if (!selfMember) return null;
|
||||
return <ConnectionInfoContextMenu guild={guild} selfMember={selfMember} relativeToRef={rootRef} close={() => { setContextMenuOpen(false); }} />
|
||||
return (
|
||||
<ConnectionInfoContextMenu
|
||||
guild={guild} selfMember={selfMember} relativeToRef={rootRef} close={close}
|
||||
/>
|
||||
);
|
||||
}, [ guild, selfMember, rootRef ]);
|
||||
|
||||
const toggleContextMenu = useCallback(() => {
|
||||
setContextMenuOpen(oldContextMenuOpen => !!contextMenu && !oldContextMenuOpen);
|
||||
}, [ contextMenu ]);
|
||||
|
||||
return (
|
||||
<div ref={rootRef} className="connection-info">
|
||||
<div onClick={toggleContextMenu}><MemberElement guild={guild} member={displayMember} /></div>
|
||||
{contextMenuOpen ? contextMenu : null}
|
||||
{contextMenu}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { FC, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { FC, useRef } from 'react';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import GuildTitleContextMenu from '../context-menus/context-menu-guild-title';
|
||||
import GuildSubscriptions from '../require/guild-subscriptions';
|
||||
import ReactHelper from '../require/react-helper';
|
||||
|
||||
export interface GuildTitleProps {
|
||||
guild: CombinedGuild;
|
||||
@ -12,37 +13,27 @@ const GuildTitle: FC<GuildTitleProps> = (props: GuildTitleProps) => {
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [ contextMenuOpen, setContextMenuOpen ] = useState<boolean>(false);
|
||||
|
||||
// TODO: Handle fetch error
|
||||
const [ guildMeta, fetchError ] = GuildSubscriptions.useGuildMetadataSubscription(guild);
|
||||
const [ selfMember ] = GuildSubscriptions.useSelfMemberSubscription(guild);
|
||||
|
||||
const alignment = useMemo(() => {
|
||||
return { top: 'bottom', centerX: 'centerX' }
|
||||
}, []);
|
||||
|
||||
const contextMenu = useMemo(() => {
|
||||
const [ contextMenu, toggleContextMenu ] = ReactHelper.useContextMenu((close: () => void) => {
|
||||
if (!guildMeta) return null;
|
||||
if (!selfMember) return null;
|
||||
return (
|
||||
<GuildTitleContextMenu
|
||||
alignment={alignment} relativeToRef={rootRef} close={() => { setContextMenuOpen(false); }}
|
||||
relativeToRef={rootRef} close={close}
|
||||
guild={guild} guildMeta={guildMeta} selfMember={selfMember}
|
||||
/>
|
||||
);
|
||||
}, [ guild, guildMeta, selfMember, rootRef ]);
|
||||
|
||||
const toggleContextMenu = useCallback(() => {
|
||||
setContextMenuOpen(oldContextMenuOpen => !!contextMenu && !oldContextMenuOpen);
|
||||
}, [ contextMenu ]);
|
||||
|
||||
return (
|
||||
<div className="guild-title" ref={rootRef}>
|
||||
<div className="guild-name-container" onClick={toggleContextMenu}>
|
||||
<span className="guild-name">{guildMeta?.name ?? null}</span>
|
||||
</div>
|
||||
{contextMenuOpen ? contextMenu : null}
|
||||
{contextMenu}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user