UNPKG

@steambrew/client

Version:
163 lines (130 loc) 4.51 kB
import { FC, ReactNode } from 'react'; import { Export, findModuleByExport, findModuleDetailsByExport, findModuleExport } from '../webpack'; import { FooterLegendProps } from './FooterLegend'; interface PopupCreationOptions { /** * Initially hidden, make it appear with {@link ContextMenuInstance.Show}. */ bCreateHidden?: boolean; bModal?: boolean; /** * Document title. */ title?: string; } // Separate interface, since one of webpack module exports uses this exact object, // so maybe it could be reused elsewhere. interface MonitorOptions { targetMonitor: { flMonitorScale: number; nScreenLeft: number; nScreenTop: number; nScreenWidth: number; nScreenHeight: number; }; flGamepadScale: number; } export interface ContextMenuPositionOptions extends PopupCreationOptions, Partial<MonitorOptions> { /** * When {@link bForcePopup} is true, makes the window appear above everything else. */ bAlwaysOnTop?: boolean; /** * Disables the mouse overlay, granting the ability to click anywhere while * the menu's active. */ bDisableMouseOverlay?: boolean; /** * Disables the {@link bPreferPopTop} behavior. */ bDisablePopTop?: boolean; bFitToWindow?: boolean; /** * Forces the menu to open in a separate window instead of inside the parent. */ bForcePopup?: boolean; /** * Like {@link bMatchWidth}, but don't shrink below the menu's minimum width. */ bGrowToElementWidth?: boolean; /** * Match the parent's exact height. */ bMatchHeight?: boolean; /** * Match the parent's exact width. */ bMatchWidth?: boolean; bNoFocusWhenShown?: boolean; /** * Makes the menu **invisible**, instead of getting removed from the DOM. */ bRetainOnHide?: boolean; bScreenCoordinates?: boolean; /** * Set to `true` to not account for the parent's width. */ bOverlapHorizontal?: boolean; /** * Set to `true` to not account for the parent's height. */ bOverlapVertical?: boolean; /** * Set to `true` to make the entire menu try to appear on the left side from * the parent. */ bPreferPopLeft?: boolean; /** * Set to `true` to make the entire menu try to appear above the parent. */ bPreferPopTop?: boolean; bShiftToFitWindow?: boolean; // different window creation flags (StandaloneContextMenu vs PopupContextMenu) bStandalone?: boolean; /** * Class name **replacement** for the container element, i.e. it replaces the * default class. */ strClassName?: string; } export interface ContextMenuInstance { Hide(): void; Show(): void; } export const showContextMenu: (children: ReactNode, parent?: EventTarget, options?: ContextMenuPositionOptions) => ContextMenuInstance = findModuleExport( (e: Export) => typeof e === 'function' && e.toString().includes('GetContextMenuManagerFromWindow(') && e.toString().includes('.CreateContextMenuInstance('), ); export interface MenuProps extends FooterLegendProps { label: string; onCancel?(): void; cancelText?: string; children?: ReactNode; } const MenuModule = findModuleDetailsByExport((e: Export) => e?.render?.toString()?.includes('bPlayAudio:') || (e?.prototype?.OnOKButton && e?.prototype?.OnMouseEnter)); export const Menu: FC<MenuProps> = findModuleExport((e: Export) => e?.prototype?.HideIfSubmenu && e?.prototype?.HideMenu) || // Legacy Menu (Object.values(MenuModule?.[0] ?? {}).find((e) => e?.toString()?.includes?.(`useId`) && e?.toString()?.includes?.(`labelId`)) as FC<MenuProps>); // New Menu 6/15/2025 export interface MenuGroupProps { label: string; disabled?: boolean; children?: ReactNode; } const MenuGoupModule = findModuleByExport( (e) => e?.prototype?.Focus && e?.prototype?.OnOKButton && e?.prototype?.render?.toString().includes?.(`"emphasis"==this.props.tone`), ); export const MenuGroup: FC<MenuGroupProps> = MenuGoupModule && Object.values(MenuGoupModule).find((e: Export) => typeof e == 'function' && e?.toString?.()?.includes('bInGamepadUI:')); export interface MenuItemProps extends FooterLegendProps { bInteractableItem?: boolean; onClick?(evt: Event): void; onSelected?(evt: Event): void; onMouseEnter?(evt: MouseEvent): void; onMoveRight?(): void; selected?: boolean; disabled?: boolean; bPlayAudio?: boolean; tone?: 'positive' | 'emphasis' | 'destructive'; children?: ReactNode; } export const MenuItem: FC<MenuItemProps> = MenuModule?.[1]; export const MenuSeparator: FC = findModuleExport((e: Export) => typeof e === 'function' && /className:.+?\.ContextMenuSeparator/.test(e.toString()));