@szhsin/react-menu
Advanced tools
Comparing version 2.0.0 to 2.0.1
@@ -109,2 +109,7 @@ import React = require('react'); | ||
interface Hoverable { | ||
disabled?: boolean; | ||
index?: number; | ||
} | ||
// Menu, SubMenu and ControlledMenu | ||
@@ -195,6 +200,5 @@ interface SharedMenuProps extends Omit<BaseProps, 'styles'> { | ||
export interface SubMenuProps extends SharedMenuProps { | ||
export interface SubMenuProps extends SharedMenuProps, Hoverable { | ||
itemProps?: BaseProps<SubMenuItemModifiers>; | ||
label?: RenderProp<SubMenuItemModifiers>; | ||
disabled?: boolean; | ||
onMenuChange?: EventHandler<MenuChangeEvent>; | ||
@@ -217,3 +221,3 @@ } | ||
export interface MenuItemProps extends Omit<BaseProps<MenuItemModifiers>, 'onClick'> { | ||
export interface MenuItemProps extends Omit<BaseProps<MenuItemModifiers>, 'onClick'>, Hoverable { | ||
value?: any; | ||
@@ -225,3 +229,2 @@ href?: string; | ||
checked?: boolean; | ||
disabled?: boolean; | ||
onClick?: EventHandler<ClickEvent>; | ||
@@ -242,4 +245,3 @@ children?: RenderProp<MenuItemModifiers>; | ||
export interface FocusableItemProps extends BaseProps<FocusableItemModifiers> { | ||
disabled?: boolean; | ||
export interface FocusableItemProps extends BaseProps<FocusableItemModifiers>, Hoverable { | ||
children: (modifiers: { | ||
@@ -246,0 +248,0 @@ disabled: boolean; |
@@ -1,2 +0,2 @@ | ||
import React, { forwardRef, useContext, useState, useMemo, useLayoutEffect, useEffect, useRef, useReducer, useCallback, memo } from 'react'; | ||
import React, { Children, cloneElement, forwardRef, useContext, useState, useMemo, useLayoutEffect, useEffect, useRef, useReducer, useCallback, memo } from 'react'; | ||
import ReactDOM, { unstable_batchedUpdates, createPortal } from 'react-dom'; | ||
@@ -36,9 +36,9 @@ import PropTypes from 'prop-types'; | ||
const HoverIndexActionTypes = Object.freeze({ | ||
'RESET': 'HOVER_INDEX_RESET', | ||
'SET': 'HOVER_INDEX_SET', | ||
'UNSET': 'HOVER_INDEX_UNSET', | ||
'INCREASE': 'HOVER_INDEX_INCREASE', | ||
'DECREASE': 'HOVER_INDEX_DECREASE', | ||
'FIRST': 'HOVER_INDEX_FIRST', | ||
'LAST': 'HOVER_INDEX_LAST' | ||
'RESET': 'HOVER_RESET', | ||
'SET': 'HOVER_SET', | ||
'UNSET': 'HOVER_UNSET', | ||
'INCREASE': 'HOVER_INCREASE', | ||
'DECREASE': 'HOVER_DECREASE', | ||
'FIRST': 'HOVER_FIRST', | ||
'LAST': 'HOVER_LAST' | ||
}); | ||
@@ -68,2 +68,9 @@ const SubmenuActionTypes = Object.freeze({ | ||
const batchedUpdates = unstable_batchedUpdates || (callback => callback()); | ||
const values = Object.values || (obj => Object.keys(obj).map(key => obj[key])); | ||
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff; | ||
const isProd = process.env.NODE_ENV === 'production'; | ||
const isMenuOpen = state => state === 'open' || state === 'opening'; | ||
const getTransition = (transition, name) => Boolean(transition && transition[name]) || transition === true; | ||
const safeCall = (fn, ...args) => typeof fn === 'function' ? fn(...args) : fn; | ||
const getName = component => component && component['_szhsinMenu']; | ||
const defineName = (component, name) => name ? Object.defineProperty(component, '_szhsinMenu', { | ||
@@ -73,6 +80,4 @@ value: name, | ||
}) : component; | ||
const getName = component => component && component['_szhsinMenu']; | ||
const applyHOC = HOC => (...args) => defineName(HOC(...args), getName(args[0])); | ||
const applyStatics = sourceComponent => wrappedComponent => defineName(wrappedComponent, getName(sourceComponent)); | ||
const safeCall = (fn, ...args) => typeof fn === 'function' ? fn(...args) : fn; | ||
const attachHandlerProps = (handlers, props) => { | ||
@@ -131,68 +136,56 @@ if (!props) return handlers; | ||
}; | ||
const values = Object.values || (obj => Object.keys(obj).map(key => obj[key])); | ||
const floatEqual = (a, b, diff = 0.0001) => Math.abs(a - b) < diff; | ||
const isProd = process.env.NODE_ENV === 'production'; | ||
const isMenuOpen = state => state === 'open' || state === 'opening'; | ||
const getTransition = (transition, name) => Boolean(transition && transition[name]) || transition === true; | ||
const validateChildren = (parent, child, permitted) => { | ||
if (!child) return false; | ||
if (!permitted.includes(getName(child.type))) { | ||
!isProd && console.warn(`[react-menu] ${child.type || child} is ignored.\n`, `The permitted children inside a ${parent} are ${permitted.join(', ')}.`, 'If you create HOC of these components, you can use the applyHOC or applyStatics helper, see more at: https://szhsin.github.io/react-menu/docs#utils-apply-hoc'); | ||
return false; | ||
const validateIndex = (index, isDisabled, node) => { | ||
if (!isProd && index === undefined && !isDisabled) { | ||
const error = `[react-menu] Validate item '${node && node.toString()}' failed. | ||
You're probably creating your own components or HOC over MenuItem, SubMenu or FocusableItem. | ||
To create wrapping components, see: https://codesandbox.io/s/react-menu-wrapping-q0b59 | ||
To create HOCs, see: https://codesandbox.io/s/react-menu-hoc-0bipn`; | ||
throw new Error(error); | ||
} | ||
return true; | ||
}; | ||
const cloneChildren = (children, startIndex = 0) => { | ||
const cloneChildren = (children, startIndex = 0, inRadioGroup) => { | ||
let index = startIndex; | ||
let descendOverflow = false; | ||
const permittedChildren = ['MenuDivider', 'MenuGroup', 'MenuHeader', 'MenuItem', 'FocusableItem', 'MenuRadioGroup', 'SubMenu']; | ||
const items = React.Children.map(children, child => { | ||
if (!validateChildren('Menu or SubMenu', child, permittedChildren)) return null; | ||
const items = Children.map(children, child => { | ||
if (child === undefined || child === null) return null; | ||
if (!child.type) return child; | ||
const name = getName(child.type); | ||
switch (getName(child.type)) { | ||
case 'MenuDivider': | ||
case 'MenuHeader': | ||
return child; | ||
case 'MenuRadioGroup': | ||
switch (name) { | ||
case 'MenuItem': | ||
{ | ||
const permittedChildren = ['MenuItem']; | ||
const props = { | ||
type: 'radio' | ||
}; | ||
const radioItems = React.Children.map(child.props.children, radioChild => { | ||
if (!validateChildren('MenuRadioGroup', radioChild, permittedChildren)) return null; | ||
return radioChild.props.disabled ? /*#__PURE__*/React.cloneElement(radioChild, props) : /*#__PURE__*/React.cloneElement(radioChild, { ...props, | ||
index: index++ | ||
}); | ||
}); | ||
return /*#__PURE__*/React.cloneElement(child, { | ||
children: radioItems | ||
}); | ||
if (inRadioGroup) { | ||
const props = { | ||
type: 'radio' | ||
}; | ||
if (!child.props.disabled) props.index = index++; | ||
return /*#__PURE__*/cloneElement(child, props); | ||
} | ||
} | ||
case 'MenuGroup': | ||
case 'SubMenu': | ||
case 'FocusableItem': | ||
return child.props.disabled ? child : /*#__PURE__*/cloneElement(child, { | ||
index: index++ | ||
}); | ||
default: | ||
{ | ||
const { | ||
items, | ||
endIndex, | ||
descendOverflow: descOverflow | ||
} = cloneChildren(child.props.children, index); | ||
index = endIndex; | ||
const takeOverflow = Boolean(child.props.takeOverflow); | ||
if (!isProd && (descendOverflow === descOverflow ? descOverflow : takeOverflow)) throw new Error('[react-menu] Only one MenuGroup in a menu is allowed to have takeOverflow prop.'); | ||
descendOverflow = descendOverflow || descOverflow || takeOverflow; | ||
return /*#__PURE__*/React.cloneElement(child, { | ||
children: items | ||
const innerChildren = child.props.children; | ||
if (innerChildren === null || typeof innerChildren !== "object") return child; | ||
const desc = cloneChildren(innerChildren, index, inRadioGroup || name === 'MenuRadioGroup'); | ||
index = desc.index; | ||
if (name === 'MenuGroup') { | ||
const takeOverflow = Boolean(child.props.takeOverflow); | ||
const descOverflow = desc.descendOverflow; | ||
if (!isProd && (descendOverflow === descOverflow ? descOverflow : takeOverflow)) throw new Error('[react-menu] Only one MenuGroup in a menu is allowed to have takeOverflow prop.'); | ||
descendOverflow = descendOverflow || descOverflow || takeOverflow; | ||
} | ||
return /*#__PURE__*/cloneElement(child, { | ||
children: desc.items | ||
}); | ||
} | ||
default: | ||
return child.props.disabled ? child : /*#__PURE__*/React.cloneElement(child, { | ||
index: index++ | ||
}); | ||
} | ||
@@ -202,3 +195,3 @@ }); | ||
items, | ||
endIndex: index, | ||
index, | ||
descendOverflow | ||
@@ -222,4 +215,3 @@ }; | ||
position: PropTypes.oneOf(['auto', 'anchor', 'initial']), | ||
overflow: PropTypes.oneOf(['auto', 'visible', 'hidden']), | ||
children: PropTypes.node.isRequired | ||
overflow: PropTypes.oneOf(['auto', 'visible', 'hidden']) | ||
}; | ||
@@ -525,6 +517,3 @@ const menuPropTypesBase = { ...sharedMenuPropTypes, | ||
isOpen: PropTypes.bool, | ||
disabled: PropTypes.bool, | ||
children: PropTypes.node.isRequired, | ||
onClick: PropTypes.func, | ||
onKeyDown: PropTypes.func | ||
disabled: PropTypes.bool | ||
}; | ||
@@ -1035,6 +1024,6 @@ | ||
items, | ||
endIndex, | ||
index, | ||
descendOverflow | ||
} = cloneChildren(children); | ||
menuItemsCount.current = endIndex; | ||
menuItemsCount.current = index; | ||
descendOverflowRef.current = descendOverflow; | ||
@@ -1642,2 +1631,4 @@ return items; | ||
}) { | ||
const isDisabled = Boolean(disabled); | ||
validateIndex(index, isDisabled, label); | ||
const { | ||
@@ -1674,3 +1665,2 @@ initialMounted, | ||
const isOpen = isMenuOpen(state); | ||
const isDisabled = Boolean(disabled); | ||
const { | ||
@@ -1834,3 +1824,3 @@ isActive, | ||
disabled: PropTypes.bool, | ||
label: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, | ||
label: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), | ||
itemProps: PropTypes.shape({ ...stylePropTypes() | ||
@@ -1860,2 +1850,3 @@ }), | ||
const isDisabled = Boolean(disabled); | ||
validateIndex(index, isDisabled, children); | ||
const ref = useRef(); | ||
@@ -1971,3 +1962,3 @@ const { | ||
disabled: PropTypes.bool, | ||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired, | ||
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), | ||
onClick: PropTypes.func | ||
@@ -1987,2 +1978,3 @@ }; | ||
const isDisabled = Boolean(disabled); | ||
validateIndex(index, isDisabled, children); | ||
const ref = useRef(null); | ||
@@ -2030,6 +2022,6 @@ const { | ||
disabled: PropTypes.bool, | ||
children: PropTypes.func.isRequired | ||
children: PropTypes.func | ||
}; | ||
const MenuDivider = defineName( /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function MenuDivider({ | ||
const MenuDivider = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function MenuDivider({ | ||
className, | ||
@@ -2050,7 +2042,7 @@ styles, | ||
})); | ||
})), 'MenuDivider'); | ||
})); | ||
MenuDivider.propTypes = { ...stylePropTypes() | ||
}; | ||
const MenuHeader = defineName( /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function MenuHeader({ | ||
const MenuHeader = /*#__PURE__*/memo( /*#__PURE__*/forwardRef(function MenuHeader({ | ||
className, | ||
@@ -2071,3 +2063,3 @@ styles, | ||
})); | ||
})), 'MenuHeader'); | ||
})); | ||
MenuHeader.propTypes = { ...stylePropTypes() | ||
@@ -2126,3 +2118,2 @@ }; | ||
value, | ||
children, | ||
onRadioChange, | ||
@@ -2136,3 +2127,5 @@ ...restProps | ||
}), [name, value, onRadioChange]); | ||
return /*#__PURE__*/React.createElement("li", { | ||
return /*#__PURE__*/React.createElement(RadioGroupContext.Provider, { | ||
value: contextValue | ||
}, /*#__PURE__*/React.createElement("li", { | ||
role: "presentation" | ||
@@ -2150,5 +2143,3 @@ }, /*#__PURE__*/React.createElement("ul", Object.assign({ | ||
style: useFlatStyles(styles) | ||
}), /*#__PURE__*/React.createElement(RadioGroupContext.Provider, { | ||
value: contextValue | ||
}, children))); | ||
})))); | ||
}), 'MenuRadioGroup'); | ||
@@ -2158,3 +2149,2 @@ MenuRadioGroup.propTypes = { ...stylePropTypes(), | ||
value: PropTypes.any, | ||
children: PropTypes.node.isRequired, | ||
onRadioChange: PropTypes.func | ||
@@ -2161,0 +2151,0 @@ }; |
{ | ||
"name": "@szhsin/react-menu", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "React component for building accessible menu, dropdown, submenu, context menu and more.", | ||
@@ -13,5 +13,2 @@ "author": "Zheng Song", | ||
"source": "src/index.js", | ||
"engines": { | ||
"node": ">=10" | ||
}, | ||
"scripts": { | ||
@@ -41,3 +38,3 @@ "bundle": "microbundle-crl --no-compress --css-modules false --sourcemap false", | ||
"prop-types": "^15.7.2", | ||
"react-transition-state": "^0.2.0" | ||
"react-transition-state": "^0.3.0" | ||
}, | ||
@@ -50,7 +47,7 @@ "devDependencies": { | ||
"@types/estree": "^0.0.50", | ||
"@types/react": "^17.0.14", | ||
"@types/react": "^17.0.19", | ||
"babel-eslint": "^10.1.0", | ||
"babel-jest": "^27.0.6", | ||
"babel-jest": "^27.1.0", | ||
"cross-env": "^7.0.3", | ||
"dtslint": "^4.1.4", | ||
"dtslint": "^4.1.6", | ||
"eslint": "^7.32.0", | ||
@@ -60,4 +57,4 @@ "eslint-plugin-react": "^7.24.0", | ||
"gh-pages": "^2.2.0", | ||
"jest": "^27.0.6", | ||
"jest-environment-jsdom": "^27.0.6", | ||
"jest": "^27.1.0", | ||
"jest-environment-jsdom": "^27.1.0", | ||
"microbundle-crl": "^0.13.11", | ||
@@ -69,4 +66,3 @@ "npm-run-all": "^4.1.5", | ||
"react-scripts": "^4.0.3", | ||
"react-test-renderer": "^17.0.2", | ||
"sass": "^1.38.0", | ||
"sass": "^1.39.0", | ||
"typescript": "^4.3.5" | ||
@@ -82,4 +78,5 @@ }, | ||
"submenu", | ||
"contextmenu", | ||
"context menu", | ||
"dropdown", | ||
"tooltip", | ||
"checkbox", | ||
@@ -86,0 +83,0 @@ "radio button", |
@@ -22,3 +22,3 @@ # React-Menu | ||
## Installation | ||
## Install | ||
@@ -64,2 +64,2 @@ ```bash | ||
MIT Licensed. Copyright (c) 2021 Zheng Song. | ||
[MIT](https://github.com/szhsin/react-menu/blob/master/LICENSE) Licensed. |
Sorry, the diff of this file is too big to display
24
149864
4579
+ Addedreact-transition-state@0.3.0(transitive)
- Removedreact-transition-state@0.2.0(transitive)