guild list context menu
(disconnect button)
This commit is contained in:
parent
22c258bafd
commit
9410fe3829
@ -25,7 +25,7 @@ const Overlay: FC<OverlayProps> = (props: OverlayProps) => {
|
||||
}, []);
|
||||
|
||||
if (childRootRef) {
|
||||
ReactHelper.useCloseWhenClickedOutsideEffect(childRootRef, () => { removeSelf(); });
|
||||
ReactHelper.useCloseWhenClickedOrContextOutsideEffect(childRootRef, () => { removeSelf(); });
|
||||
}
|
||||
|
||||
const keyDownHandler = useCallback((e: KeyboardEvent) => {
|
||||
|
@ -17,7 +17,7 @@ const ContextMenu: FC<ContextMenuProps> = (props: ContextMenuProps) => {
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
ReactHelper.useCloseWhenClickedOutsideEffect(rootRef, close);
|
||||
ReactHelper.useCloseWhenClickedOrContextOutsideEffect(rootRef, close);
|
||||
|
||||
return (
|
||||
<Context rootRef={rootRef} relativeToRef={relativeToRef} relativeToPos={relativeToPos} alignment={alignment}>
|
||||
|
@ -1,18 +1,21 @@
|
||||
import React, { FC, useMemo, useRef } from 'react';
|
||||
import React, { FC, MouseEvent, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import CombinedGuild from '../../../guild-combined';
|
||||
import GuildsManager from '../../../guilds-manager';
|
||||
import ContextMenu from '../../contexts/components/context-menu';
|
||||
import BasicHover, { BasicHoverSide } from '../../contexts/context-hover-basic';
|
||||
import BaseElements from '../../require/base-elements';
|
||||
import GuildSubscriptions from '../../require/guild-subscriptions';
|
||||
import ReactHelper from '../../require/react-helper';
|
||||
|
||||
export interface GuildListElementProps {
|
||||
guildsManager: GuildsManager;
|
||||
guild: CombinedGuild;
|
||||
activeGuild: CombinedGuild | null;
|
||||
setSelfActiveGuild: () => void;
|
||||
}
|
||||
|
||||
const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProps) => {
|
||||
const { guild, activeGuild, setSelfActiveGuild } = props;
|
||||
const { guildsManager, guild, activeGuild, setSelfActiveGuild } = props;
|
||||
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -42,6 +45,29 @@ const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProp
|
||||
)
|
||||
}, [ guildMeta, selfMember ]);
|
||||
|
||||
const leaveGuildCallable = useCallback(async () => {
|
||||
guild.disconnect();
|
||||
await guildsManager.removeGuild(guild);
|
||||
}, [ guildsManager, guild ])
|
||||
|
||||
const [ contextClickPos, setContextClickPos ] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
|
||||
const [ contextClickMenu, toggleMenu, closeMenu, openMenu ] = ReactHelper.useContextMenu((close: () => void) => {
|
||||
return (
|
||||
<ContextMenu
|
||||
alignment={{ top: 'centerY', left: 'centerX' }}
|
||||
relativeToPos={contextClickPos} close={close}
|
||||
>
|
||||
<div className="guild-context">
|
||||
<div className="item red leave-guild" onClick={leaveGuildCallable}>Leave Guild</div>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
)
|
||||
}, [ leaveGuildCallable ]);
|
||||
const onContextMenu = useCallback((e: MouseEvent<HTMLDivElement>) => {
|
||||
setContextClickPos({ x: e.clientX, y: e.clientY });
|
||||
openMenu();
|
||||
}, [ openMenu ]);
|
||||
|
||||
const className = useMemo(() => {
|
||||
console.log('active: ' + activeGuild?.id + '/ me: ' + guild.id);
|
||||
return activeGuild && guild.id === activeGuild.id ? 'guild active' : 'guild';
|
||||
@ -52,12 +78,14 @@ const GuildListElement: FC<GuildListElementProps> = (props: GuildListElementProp
|
||||
<div
|
||||
className={className} ref={rootRef}
|
||||
onClick={setSelfActiveGuild}
|
||||
onContextMenu={onContextMenu}
|
||||
onMouseEnter={mouseEnterCallable} onMouseLeave={mouseLeaveCallable}
|
||||
>
|
||||
<div className="pill" />
|
||||
<img src={iconSrc} alt="guild" />
|
||||
</div>
|
||||
{contextHover}
|
||||
{contextClickMenu}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ const GuildList: FC<GuildListProps> = (props: GuildListProps) => {
|
||||
}, [ activeGuild ]);
|
||||
|
||||
const guildElements = useMemo(() => {
|
||||
return guilds.map((guild: CombinedGuild) => <GuildListElement key={guild.id} guild={guild} activeGuild={activeGuild} setSelfActiveGuild={() => { setActiveGuild(guild); } } />);
|
||||
return guilds.map((guild: CombinedGuild) => <GuildListElement key={guild.id} guildsManager={guildsManager} guild={guild} activeGuild={activeGuild} setSelfActiveGuild={() => { setActiveGuild(guild); } } />);
|
||||
}, [ guilds ]);
|
||||
|
||||
return (
|
||||
|
@ -342,40 +342,49 @@ export default class ReactHelper {
|
||||
}
|
||||
|
||||
// Makes sure to also allow you to fly-out a click starting inside of the ref'd element but was dragged outside
|
||||
static useCloseWhenClickedOutsideEffect(ref: RefObject<HTMLElement>, close: () => void) {
|
||||
const [ mouseDownTarget, setMouseDownTarget ] = useState<EventTarget | null>(null);
|
||||
const [ mouseUpTarget, setMouseUpTarget ] = useState<EventTarget | null>(null);
|
||||
static useCloseWhenClickedOrContextOutsideEffect(ref: RefObject<HTMLElement>, close: () => void) {
|
||||
// Have to use a ref here and not states since we can't re-assign state between mouseup and click
|
||||
const mouseRef = useRef<{ mouseDownTarget: Node | null, mouseUpTarget: Node | null}>({ mouseDownTarget: null, mouseUpTarget: null });
|
||||
|
||||
const handleClickOutside = useCallback((event: MouseEvent) => {
|
||||
//console.log('current:', ref.current, 'target:', event.target, 'mouseDownTarget:', mouseDownTarget, 'mouseUpTarget:', mouseUpTarget);
|
||||
//console.log('current:', ref.current, 'target:', event.target, 'mouseDownTarget:', mouseRef.current.mouseDownTarget, 'mouseUpTarget:', mouseRef.current.mouseUpTarget);
|
||||
|
||||
if (!ref.current) return;
|
||||
|
||||
// Casting here is OK. https://stackoverflow.com/q/61164018
|
||||
if (ref.current.contains(event.target as Node)) return;
|
||||
|
||||
if (mouseDownTarget !== null || mouseUpTarget !== null) return;
|
||||
if (mouseRef.current.mouseDownTarget !== null || mouseRef.current.mouseUpTarget !== null) return;
|
||||
|
||||
close();
|
||||
}, [ ref, mouseDownTarget, mouseUpTarget, close ]);
|
||||
}, [ ref, mouseRef, close ]);
|
||||
|
||||
const handleMouseDown = useCallback((event: MouseEvent) => {
|
||||
if (!ref.current) return;
|
||||
//console.log('mouse down. Contains: ' + ref.current.contains(event.target as Node));
|
||||
if (ref.current.contains(event.target as Node)) {
|
||||
setMouseDownTarget(event.target);
|
||||
mouseRef.current.mouseDownTarget = event.target as Node;
|
||||
} else {
|
||||
setMouseDownTarget(null);
|
||||
mouseRef.current.mouseDownTarget = null;
|
||||
}
|
||||
}, [ ref ]);
|
||||
}, [ ref, mouseRef ]);
|
||||
|
||||
const handleMouseUp = useCallback((event: MouseEvent) => {
|
||||
if (!ref.current) return;
|
||||
//console.log('mouse up. Contains: ' + ref.current.contains(event.target as Node));
|
||||
if (ref.current.contains(event.target as Node)) {
|
||||
setMouseUpTarget(event.target);
|
||||
mouseRef.current.mouseUpTarget = event.target as Node;
|
||||
} else {
|
||||
setMouseUpTarget(null);
|
||||
mouseRef.current.mouseUpTarget = null;
|
||||
}
|
||||
}, [ ref ]);
|
||||
}, [ ref, mouseRef ]);
|
||||
|
||||
const handleContextMenu = useCallback((event: MouseEvent) => {
|
||||
if (!ref.current) return;
|
||||
if (ref.current.contains(event.target as Node)) return;
|
||||
// Context menu is fired on mouse-down so no need to do special checks.
|
||||
close();
|
||||
}, [ ref ])
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
@ -397,6 +406,13 @@ export default class ReactHelper {
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
}, [ handleMouseUp ]);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('contextmenu', handleContextMenu);
|
||||
return () => {
|
||||
document.removeEventListener('contextmenu', handleContextMenu);
|
||||
}
|
||||
}, [ handleClickOutside ]);
|
||||
}
|
||||
|
||||
static useAlignment(
|
||||
|
Loading…
Reference in New Issue
Block a user