Skip to content

Commit 2eaaef9

Browse files
committed
feat: add position to onShow and viewportMargin to Menu
1 parent 875516d commit 2eaaef9

2 files changed

Lines changed: 34 additions & 11 deletions

File tree

src/components/Menu.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import cx from 'clsx';
1212
import { ItemTrackerProvider } from './ItemTrackerProvider';
1313

1414
import { eventManager } from '../core/eventManager';
15-
import { TriggerEvent, MenuId, MenuAnimation, Theme } from '../types';
15+
import { TriggerEvent, MenuId, MenuAnimation, Theme, MenuOnShowCallback, MenuOnHideCallback, StyleMargin, StyleMarginObject } from '../types';
1616
import { useItemTracker } from '../hooks';
1717
import { createKeyboardController } from './keyboardController';
1818
import { CssClass, EVENT, hideOnEvents } from '../constants';
@@ -41,6 +41,14 @@ export interface MenuProps
4141
*/
4242
theme?: Theme;
4343

44+
/**
45+
* Apply a margin to the viewport for boundary detection. If a context menu
46+
* would appear within this viewport margin it counts as being outside the
47+
* viewport and moves within the margin limits.
48+
* This only works if `disableBoundariesCheck` is not true.
49+
*/
50+
viewportMargin?: StyleMargin;
51+
4452
/**
4553
* Animation is appended to
4654
* - `.contexify_willEnter-${given animation}`
@@ -85,15 +93,16 @@ export interface MenuProps
8593
* Triggers when a show event is triggered. This triggers even if the menu
8694
* is already shown.
8795
* @param fromHidden - True if the menu was previously hidden.
96+
* @param position - An object of `{x: number, y:number}` for the position the
97+
* context menu is appearing.
8898
*/
89-
onShow?: (fromHidden: boolean) => void;
90-
99+
onShow?: MenuOnShowCallback;
91100
/**
92101
* Triggers when a hide event is triggered. This triggers even if the menu
93102
* is already hidden.
94103
* @param fromVisible - True if the menu was previously visible.
95104
*/
96-
onHide?: (fromVisible: boolean) => void;
105+
onHide?: MenuOnHideCallback;
97106
}
98107

99108
interface MenuState {
@@ -122,6 +131,7 @@ export const Menu = ({
122131
animation = 'fade',
123132
preventDefaultOnKeydown = true,
124133
disableBoundariesCheck = false,
134+
viewportMargin = 0,
125135
onShow,
126136
onHide,
127137
onVisibilityChange,
@@ -140,12 +150,14 @@ export const Menu = ({
140150
const [menuController] = useState(() => createKeyboardController());
141151

142152
const wasVisible = useRef<boolean>(false);
153+
const viewPortMargin = useRef<StyleMarginObject>(typeof viewportMargin === 'number' ? { bottom: viewportMargin, top: viewportMargin, left: viewportMargin, right: viewportMargin } : viewportMargin)
143154

144155
// @deprecated -- NOTE: this is to keep backwards compatibility for onVisibilityChange
145156
const wasVisibleDeprecated = useRef<boolean>(false);
146157
// @deprecated
147158
const visibilityId = useRef<number>(0);
148159

160+
149161
// allows the caller to assign their own ref, which is then synced with nodeRef
150162
useImperativeHandle(ref, () => nodeRef.current as HTMLDivElement);
151163

@@ -169,9 +181,14 @@ export const Menu = ({
169181
const { innerWidth, innerHeight } = window;
170182
const { offsetWidth, offsetHeight } = nodeRef.current;
171183

172-
if (x + offsetWidth > innerWidth) x -= x + offsetWidth - innerWidth;
184+
const xMax = innerWidth - (viewPortMargin.current?.right ?? 0);
185+
// const xMin = viewPortMargin.current.left;
186+
const yMax = innerHeight - (viewPortMargin.current?.bottom ?? 0);
187+
// const yMin = viewPortMargin.current.top;
188+
189+
if (x + offsetWidth > xMax) x -= x + offsetWidth - xMax;
173190

174-
if (y + offsetHeight > innerHeight) y -= y + offsetHeight - innerHeight;
191+
if (y + offsetHeight > yMax) y -= y + offsetHeight - yMax;
175192
}
176193

177194
return { x, y };
@@ -253,7 +270,7 @@ export const Menu = ({
253270

254271

255272
if (isFn(onShow)) {
256-
onShow(!wasVisible.current);
273+
onShow(!wasVisible.current, { x, y });
257274
}
258275

259276
if (!wasVisible.current) {

src/types/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ export type PredicateParams<Props = any, Data = any> = HandlerParams<
9797
export interface ItemParams<Props = any, Data = any>
9898
extends HandlerParams<Props, Data> {
9999
event:
100-
| React.MouseEvent<HTMLElement>
101-
| React.TouchEvent<HTMLElement>
102-
| React.KeyboardEvent<HTMLElement>
103-
| KeyboardEvent;
100+
| React.MouseEvent<HTMLElement>
101+
| React.TouchEvent<HTMLElement>
102+
| React.KeyboardEvent<HTMLElement>
103+
| KeyboardEvent;
104104
}
105105

106106
export interface InternalProps {
@@ -139,3 +139,9 @@ export type MenuAnimation =
139139
| { enter: Animation | false; exit: Animation | false };
140140

141141
type Animation = BuiltInOrString<'fade' | 'scale' | 'flip' | 'slide'>;
142+
143+
export type Position = { x: number, y: number };
144+
export type MenuOnShowCallback = (fromHidden: boolean, position: Position) => void;
145+
export type MenuOnHideCallback = (fromVisible: boolean) => void;
146+
export type StyleMarginObject = { top?: number, bottom?: number, left?: number, right?: number }
147+
export type StyleMargin = number | StyleMarginObject

0 commit comments

Comments
 (0)