Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"style2js": "style2js --out-dir dist dist/ReactContexify.min.css"
},
"peerDependencies": {
"react": ">=16",
"react-dom": ">=16"
"react": ">=19",
"react-dom": ">=19"
Comment thread
Aerilym marked this conversation as resolved.
Outdated
},
"prettier": {
"printWidth": 80,
Expand Down Expand Up @@ -66,15 +66,13 @@
"popup"
],
"devDependencies": {
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"cssnano": "^5.1.14",
"cssnano-cli": "^1.0.5",
"cypress": "^11.0.1",
"postcss": "^8.4.18",
"postcss-cli": "^10.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.56.1",
"style2js": "^1.0.1",
"tsup": "^6.4.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { contextMenu } from '../core';

export interface ItemProps
extends InternalProps,
Omit<React.HTMLAttributes<HTMLElement>, 'hidden' | 'disabled' | 'onClick'> {
Omit<React.HTMLAttributes<HTMLElement>, 'hidden' | 'disabled' | 'onClick'> {
/**
* Any valid node that can be rendered
*/
Expand Down Expand Up @@ -135,7 +135,7 @@ export const Item: React.FC<ItemProps> = ({
handlerEvent = 'onClick',
...rest
}) => {
const itemNode = useRef<HTMLElement>();
const itemNode = useRef<HTMLElement>(null);
const itemTracker = useItemTrackerContext();
const handlerParams = {
id,
Expand Down
89 changes: 74 additions & 15 deletions src/components/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, {
ReactNode,
Ref,
useEffect,
useImperativeHandle,
useReducer,
useRef,
useState,
Expand All @@ -20,6 +22,8 @@ import { ShowContextMenuParams } from '../core';

export interface MenuProps
extends Omit<React.HTMLAttributes<HTMLElement>, 'id'> {

ref?: Ref<HTMLDivElement>;
/**
* Unique id to identify the menu. Use to Trigger the corresponding menu
*/
Expand Down Expand Up @@ -62,8 +66,32 @@ export interface MenuProps

/**
* Used to track menu visibility
* @deprecated -- May be removed in the next major version
* - For the false case use `onHide`.
* - For the true case use `onShow`.
* NOTE: `onShow` behaves slightly differently. `onVisibilityChange` would trigger
* with `isVisible=true` only when transitioning from hidden to shown, this meant
* if the same context menu was re-triggered from a new position the event
* would not emit, so there was no way to know if the menu moved. These new events
* trigger regardless of current state and mirror the context menu events. This
* means you can now get `onShow`, `onShow`, then `onHide` if you open a context menu,
* move it, then click away.
* For the same behaviour `onShow` has a `fromHidden` parameter which is only true
* if the menu was previously in a hidden state.
*/
onVisibilityChange?: (isVisible: boolean) => void;

/**
* Triggers when a show event is triggered. This triggers even if the menu
* is already shown.
* @param fromHidden - True if the menu was previously hidden.
*/
onShow?: (fromHidden: boolean) => void;

/**
* Triggers when a hide event is triggered if the menu is currently shown.
*/
onHide?: () => void;
Comment thread
Aerilym marked this conversation as resolved.
Outdated
}

interface MenuState {
Expand All @@ -82,18 +110,21 @@ function reducer(
return { ...state, ...(isFn(payload) ? payload(state) : payload) };
}

export const Menu: React.FC<MenuProps> = ({
export const Menu = ({
id,
ref,
theme,
style,
className,
children,
animation = 'fade',
preventDefaultOnKeydown = true,
disableBoundariesCheck = false,
onShow,
onHide,
onVisibilityChange,
...rest
}) => {
}: MenuProps) => {
const [state, setState] = useReducer(reducer, {
x: 0,
y: 0,
Expand All @@ -105,8 +136,16 @@ export const Menu: React.FC<MenuProps> = ({
const nodeRef = useRef<HTMLDivElement>(null);
const itemTracker = useItemTracker();
const [menuController] = useState(() => createKeyboardController());
const wasVisible = useRef<boolean>();
const visibilityId = useRef<number>();

const wasVisible = useRef<boolean>(false);

// @deprecated -- NOTE: this is to keep backwards compatibility for onVisibilityChange
const wasVisibleDepricated = useRef<boolean>(false);
Comment thread
Aerilym marked this conversation as resolved.
Outdated
// @deprecated
const visibilityId = useRef<number>(0);

// allows the caller to assign their own ref, which is then synced with nodeRef
useImperativeHandle(ref, () => nodeRef.current as HTMLDivElement);
Comment thread
Aerilym marked this conversation as resolved.

// subscribe event manager
useEffect(() => {
Expand Down Expand Up @@ -210,10 +249,21 @@ export const Menu: React.FC<MenuProps> = ({
});
});

clearTimeout(visibilityId.current);
if (!wasVisible.current && isFn(onVisibilityChange)) {
onVisibilityChange(true);
wasVisible.current = true;

if (isFn(onShow)) {
onShow(!wasVisible.current)
}

if (!wasVisible.current) {
wasVisible.current = true
}
Comment thread
Aerilym marked this conversation as resolved.

// TODO: remove deprecated functionality
if (isFn(onVisibilityChange)) {
clearTimeout(visibilityId.current);
if (!wasVisibleDepricated.current) {
onVisibilityChange(true);
Comment thread
Aerilym marked this conversation as resolved.
}
}
}

Expand All @@ -232,13 +282,22 @@ export const Menu: React.FC<MenuProps> = ({
animation && (isStr(animation) || ('exit' in animation && animation.exit))
? setState((state) => ({ willLeave: state.visible }))
: setState((state) => ({
visible: state.visible ? false : state.visible,
}));
visible: state.visible ? false : state.visible,
}));

visibilityId.current = setTimeout(() => {
isFn(onVisibilityChange) && onVisibilityChange(false);
wasVisible.current = false;
});
if (isFn(onHide)) {
onHide();
}

wasVisible.current = false;

Comment thread
Aerilym marked this conversation as resolved.
// TODO: remove deprecated functionality
if (isFn(onVisibilityChange)) {
visibilityId.current = setTimeout(() => {
onVisibilityChange(false);
wasVisibleDepricated.current = false;
});
}
}

function handleAnimationEnd() {
Expand Down Expand Up @@ -300,4 +359,4 @@ export const Menu: React.FC<MenuProps> = ({
)}
</ItemTrackerProvider>
);
};
}
3 changes: 2 additions & 1 deletion tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export default defineConfig({
format: ['esm', 'cjs'],
sourcemap: true,
minify: true,
treeshake: true
treeshake: true,
external: ['react', 'react-dom', 'react/jsx-runtime'],
});
82 changes: 13 additions & 69 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -115,39 +115,17 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.33.tgz#8c29a0036771569662e4635790ffa9e057db379b"
integrity sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==

"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react-dom@^19.2.3":
version "19.2.3"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==

"@types/react-dom@^18.0.8":
version "18.0.8"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.8.tgz#d2606d855186cd42cc1b11e63a71c39525441685"
integrity sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==
"@types/react@^19.2.7":
version "19.2.10"
resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.10.tgz#f3ea799e6b4cebad6dfd231c238fc9de7652e2d2"
integrity sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==
dependencies:
"@types/react" "*"

"@types/react@*":
version "16.9.56"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.56.tgz#ea25847b53c5bec064933095fc366b1462e2adf0"
integrity sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"

"@types/react@^18.0.25":
version "18.0.25"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44"
integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"

"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
csstype "^3.2.2"

"@types/sinonjs__fake-timers@8.1.1":
version "8.1.1"
Expand Down Expand Up @@ -886,10 +864,10 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"

csstype@^3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.4.tgz#b156d7be03b84ff425c9a0a4b1e5f4da9c5ca888"
integrity sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==
csstype@^3.2.2:
version "3.2.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==

cypress@^11.0.1:
version "11.0.1"
Expand Down Expand Up @@ -1702,11 +1680,6 @@ js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==

"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
Expand Down Expand Up @@ -1850,13 +1823,6 @@ log-update@^4.0.0:
slice-ansi "^4.0.0"
wrap-ansi "^6.2.0"

loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"

lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
Expand Down Expand Up @@ -2665,21 +2631,6 @@ query-string@^4.1.0:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"

react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies:
loose-envify "^1.1.0"
scheduler "^0.23.0"

react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"

read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
Expand Down Expand Up @@ -2809,13 +2760,6 @@ sax@~1.2.1:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==

scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"

semver@7.x:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
Expand Down
Loading