diff --git a/docs/src/components/CarbonAd/CarbonAd.js b/docs/src/components/CarbonAd/CarbonAd.js index 67783fce68..124e251ff0 100644 --- a/docs/src/components/CarbonAd/CarbonAd.js +++ b/docs/src/components/CarbonAd/CarbonAd.js @@ -1,4 +1,3 @@ -import _ from 'lodash' import PropTypes from 'prop-types' import React, { Component } from 'react' import { withRouter } from 'react-router-dom' @@ -53,7 +52,7 @@ class CarbonAd extends Component { // https://github.com/Semantic-Org/Semantic-UI-React/pull/3215 if (!isLoading) { isLoading = true - _.invoke(window._carbonads, 'refresh') + window._carbonads.refresh?.() waitForLoad() } }) diff --git a/docs/src/components/CodeEditor/CodeEditor.js b/docs/src/components/CodeEditor/CodeEditor.js index e0188ce109..3120cef378 100644 --- a/docs/src/components/CodeEditor/CodeEditor.js +++ b/docs/src/components/CodeEditor/CodeEditor.js @@ -113,7 +113,7 @@ class CodeEditor extends React.Component { } handleChange = _.debounce((value, e) => { - _.invoke(this.props, 'onChange', value, e) + this.props.onChange?.(value, e) }, 300) setCursorVisibility = (visible) => { @@ -150,6 +150,7 @@ class CodeEditor extends React.Component { CodeEditor.propTypes = { active: PropTypes.bool, + onChange: PropTypes.func, showCursor: PropTypes.bool, value: PropTypes.string.isRequired, } diff --git a/docs/src/components/ComponentDoc/ComponentSidebar/ComponentSidebarSection.js b/docs/src/components/ComponentDoc/ComponentSidebar/ComponentSidebarSection.js index 6a9a70bab4..6d7a76d03a 100644 --- a/docs/src/components/ComponentDoc/ComponentSidebar/ComponentSidebarSection.js +++ b/docs/src/components/ComponentDoc/ComponentSidebar/ComponentSidebarSection.js @@ -20,7 +20,7 @@ export default class ComponentSidebarSection extends PureComponent { } handleItemClick = (examplePath) => (e) => { - _.invoke(this.props, 'onItemClick', e, { examplePath }) + this.props.onItemClick?.(e, { examplePath }) } handleTitleClick = () => { diff --git a/src/addons/Confirm/Confirm.js b/src/addons/Confirm/Confirm.js index c32f9653a0..18285fa20e 100644 --- a/src/addons/Confirm/Confirm.js +++ b/src/addons/Confirm/Confirm.js @@ -22,20 +22,20 @@ const Confirm = React.forwardRef(function (props, ref) { const rest = getUnhandledProps(Confirm, props) const handleCancel = (e) => { - _.invoke(props, 'onCancel', e, props) + props.onCancel?.(e, props) } const handleCancelOverrides = (predefinedProps) => ({ onClick: (e, buttonProps) => { - _.invoke(predefinedProps, 'onClick', e, buttonProps) + predefinedProps.onClick?.(e, buttonProps) handleCancel(e) }, }) const handleConfirmOverrides = (predefinedProps) => ({ onClick: (e, buttonProps) => { - _.invoke(predefinedProps, 'onClick', e, buttonProps) - _.invoke(props, 'onConfirm', e, props) + predefinedProps.onClick?.(e, buttonProps) + props.onConfirm?.(e, props) }, }) diff --git a/src/addons/Pagination/Pagination.js b/src/addons/Pagination/Pagination.js index dc5d2a1269..8e70a21cca 100644 --- a/src/addons/Pagination/Pagination.js +++ b/src/addons/Pagination/Pagination.js @@ -55,7 +55,7 @@ const Pagination = React.forwardRef(function (props, ref) { } setActivePage(nextActivePage) - _.invoke(props, 'onPageChange', e, { ...props, activePage: nextActivePage }) + props.onPageChange?.(e, { ...props, activePage: nextActivePage }) } const handleItemOverrides = (active, type, value) => (predefinedProps) => ({ @@ -63,7 +63,7 @@ const Pagination = React.forwardRef(function (props, ref) { type, key: `${type}-${value}`, onClick: (e, itemProps) => { - _.invoke(predefinedProps, 'onClick', e, itemProps) + predefinedProps.onClick?.(e, itemProps) if (itemProps.type !== 'ellipsisItem') { handleItemClick(e, itemProps) diff --git a/src/addons/Pagination/PaginationItem.js b/src/addons/Pagination/PaginationItem.js index 6cb9af2b91..4c72875e73 100644 --- a/src/addons/Pagination/PaginationItem.js +++ b/src/addons/Pagination/PaginationItem.js @@ -1,5 +1,4 @@ import keyboardKey from 'keyboard-key' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -14,14 +13,14 @@ const PaginationItem = React.forwardRef(function (props, ref) { const disabled = props.disabled || type === 'ellipsisItem' const handleClick = (e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } const handleKeyDown = (e) => { - _.invoke(props, 'onKeyDown', e, props) + props.onKeyDown?.(e, props) if (keyboardKey.getCode(e) === keyboardKey.Enter) { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } } diff --git a/src/addons/Portal/Portal.js b/src/addons/Portal/Portal.js index 4ad57da0cd..9bd0a91e6c 100644 --- a/src/addons/Portal/Portal.js +++ b/src/addons/Portal/Portal.js @@ -1,6 +1,5 @@ import EventStack from '@semantic-ui-react/event-stack' import keyboardKey from 'keyboard-key' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -61,7 +60,7 @@ function Portal(props) { debug('open()') setOpen(true) - _.invoke(props, 'onOpen', e, { ...props, open: true }) + props.onOpen?.(e, { ...props, open: true }) } const openPortalWithTimeout = (e, delay) => { @@ -77,7 +76,7 @@ function Portal(props) { debug('close()') setOpen(false) - _.invoke(props, 'onClose', e, { ...props, open: false }) + props.onClose?.(e, { ...props, open: false }) } const closePortalWithTimeout = (e, delay) => { @@ -173,12 +172,12 @@ function Portal(props) { const handleTriggerBlur = (e, ...rest) => { // Call original event handler - _.invoke(trigger, 'props.onBlur', e, ...rest) + trigger.props.onBlur?.(e, ...rest) // IE 11 doesn't work with relatedTarget in blur events const target = e.relatedTarget || document.activeElement // do not close if focus is given to the portal - const didFocusPortal = _.invoke(contentRef.current, 'contains', target) + const didFocusPortal = contentRef.current.contains?.(target) if (!closeOnTriggerBlur || didFocusPortal) { return @@ -190,7 +189,7 @@ function Portal(props) { const handleTriggerClick = (e, ...rest) => { // Call original event handler - _.invoke(trigger, 'props.onClick', e, ...rest) + trigger.props.onClick?.(e, ...rest) if (open && closeOnTriggerClick) { debug('handleTriggerClick() - close') @@ -204,7 +203,7 @@ function Portal(props) { const handleTriggerFocus = (e, ...rest) => { // Call original event handler - _.invoke(trigger, 'props.onFocus', e, ...rest) + trigger.props.onFocus?.(e, ...rest) if (!openOnTriggerFocus) { return @@ -218,7 +217,7 @@ function Portal(props) { clearTimeout(mouseEnterTimer.current) // Call original event handler - _.invoke(trigger, 'props.onMouseLeave', e, ...rest) + trigger.props.onMouseLeave?.(e, ...rest) if (!closeOnTriggerMouseLeave) { return @@ -232,7 +231,7 @@ function Portal(props) { clearTimeout(mouseLeaveTimer.current) // Call original event handler - _.invoke(trigger, 'props.onMouseEnter', e, ...rest) + trigger.props.onMouseEnter?.(e, ...rest) if (!openOnTriggerMouseEnter) { return @@ -248,8 +247,8 @@ function Portal(props) { <> _.invoke(props, 'onMount', null, props)} - onUnmount={() => _.invoke(props, 'onUnmount', null, props)} + onMount={() => props.onMount?.(null, props)} + onUnmount={() => props.onUnmount?.(null, props)} ref={contentRef} > {children} diff --git a/src/addons/Portal/PortalInner.js b/src/addons/Portal/PortalInner.js index 854818f379..5fe212eeaa 100644 --- a/src/addons/Portal/PortalInner.js +++ b/src/addons/Portal/PortalInner.js @@ -1,4 +1,3 @@ -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' import { createPortal } from 'react-dom' @@ -12,8 +11,8 @@ const debug = makeDebugger('PortalInner') * An inner component that allows you to render children outside their parent. */ const PortalInner = React.forwardRef(function (props, ref) { - const handleMount = useEventCallback(() => _.invoke(props, 'onMount', null, props)) - const handleUnmount = useEventCallback(() => _.invoke(props, 'onUnmount', null, props)) + const handleMount = useEventCallback(() => props.onMount?.(null, props)) + const handleUnmount = useEventCallback(() => props.onUnmount?.(null, props)) const element = usePortalElement(props.children, ref) diff --git a/src/addons/TextArea/TextArea.js b/src/addons/TextArea/TextArea.js index eacaab263a..a2dd2915d4 100644 --- a/src/addons/TextArea/TextArea.js +++ b/src/addons/TextArea/TextArea.js @@ -15,13 +15,13 @@ const TextArea = React.forwardRef(function (props, ref) { const handleChange = (e) => { const newValue = _.get(e, 'target.value') - _.invoke(props, 'onChange', e, { ...props, value: newValue }) + props.onChange?.(e, { ...props, value: newValue }) } const handleInput = (e) => { const newValue = _.get(e, 'target.value') - _.invoke(props, 'onInput', e, { ...props, value: newValue }) + props.onInput?.(e, { ...props, value: newValue }) } const rest = getUnhandledProps(TextArea, props) diff --git a/src/addons/TransitionablePortal/TransitionablePortal.js b/src/addons/TransitionablePortal/TransitionablePortal.js index c7637d07f9..fa4b2ea002 100644 --- a/src/addons/TransitionablePortal/TransitionablePortal.js +++ b/src/addons/TransitionablePortal/TransitionablePortal.js @@ -78,8 +78,8 @@ function TransitionablePortal(props) { debug('handleTransitionHide()') setTransitionVisible(false) - _.invoke(props, 'onClose', null, { ...data, portalOpen: false, transitionVisible: false }) - _.invoke(props, 'onHide', null, { ...data, portalOpen, transitionVisible: false }) + props.onClose?.(null, { ...data, portalOpen: false, transitionVisible: false }) + props.onHide?.(null, { ...data, portalOpen, transitionVisible: false }) } const handleTransitionStart = (nothing, data) => { @@ -87,7 +87,7 @@ function TransitionablePortal(props) { const { status } = data const nextTransitionVisible = status === TRANSITION_STATUS_ENTERING - _.invoke(props, 'onStart', null, { + props.onStart?.(null, { ...data, portalOpen, transitionVisible: nextTransitionVisible, @@ -99,7 +99,7 @@ function TransitionablePortal(props) { } setTransitionVisible(nextTransitionVisible) - _.invoke(props, 'onOpen', null, { + props.onOpen?.(null, { ...data, transitionVisible: nextTransitionVisible, portalOpen: true, diff --git a/src/collections/Breadcrumb/BreadcrumbSection.js b/src/collections/Breadcrumb/BreadcrumbSection.js index b48f3eaf49..7c5e91919a 100644 --- a/src/collections/Breadcrumb/BreadcrumbSection.js +++ b/src/collections/Breadcrumb/BreadcrumbSection.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -27,7 +26,7 @@ const BreadcrumbSection = React.forwardRef(function (props, ref) { }, }) - const handleClick = useEventCallback((e) => _.invoke(props, 'onClick', e, props)) + const handleClick = useEventCallback((e) => props.onClick?.(e, props)) return ( diff --git a/src/collections/Form/Form.js b/src/collections/Form/Form.js index 8da9b9cd65..226194f1a0 100644 --- a/src/collections/Form/Form.js +++ b/src/collections/Form/Form.js @@ -43,8 +43,8 @@ const Form = React.forwardRef(function (props, ref) { const handleSubmit = (e, ...args) => { // Heads up! Third party libs can pass own data as first argument, we need to check that it has preventDefault() // method. - if (typeof action !== 'string') _.invoke(e, 'preventDefault') - _.invoke(props, 'onSubmit', e, props, ...args) + if (typeof action !== 'string') e.preventDefault?.() + props.onSubmit?.(e, props, ...args) } const classes = cx( diff --git a/src/collections/Menu/Menu.js b/src/collections/Menu/Menu.js index 149383557d..f713d92c5c 100644 --- a/src/collections/Menu/Menu.js +++ b/src/collections/Menu/Menu.js @@ -102,8 +102,8 @@ const Menu = React.forwardRef(function (props, ref) { setActiveIndex(itemIndex) - _.invoke(predefinedProps, 'onClick', e, itemProps) - _.invoke(props, 'onItemClick', e, itemProps) + predefinedProps.onClick?.(e, itemProps) + props.onItemClick?.(e, itemProps) }, }), }), diff --git a/src/collections/Menu/MenuItem.js b/src/collections/Menu/MenuItem.js index 6420541536..fa1a419dd4 100644 --- a/src/collections/Menu/MenuItem.js +++ b/src/collections/Menu/MenuItem.js @@ -57,7 +57,7 @@ const MenuItem = React.forwardRef(function (props, ref) { const handleClick = useEventCallback((e) => { if (!disabled) { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } }) diff --git a/src/collections/Message/Message.js b/src/collections/Message/Message.js index c3b940151d..5ed690d3f9 100644 --- a/src/collections/Message/Message.js +++ b/src/collections/Message/Message.js @@ -71,7 +71,7 @@ const Message = React.forwardRef(function (props, ref) { const ElementType = getComponentType(props) const handleDismiss = useEventCallback((e) => { - _.invoke(props, 'onDismiss', e, props) + props.onDismiss?.(e, props) }) const dismissIcon = onDismiss && diff --git a/src/elements/Button/Button.js b/src/elements/Button/Button.js index 5e54992429..297eff8741 100644 --- a/src/elements/Button/Button.js +++ b/src/elements/Button/Button.js @@ -139,7 +139,7 @@ const Button = React.forwardRef(function (props, ref) { return } - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } if (!_.isNil(label)) { diff --git a/src/elements/Icon/Icon.d.ts b/src/elements/Icon/Icon.d.ts index 18881a1937..41325e4afe 100644 --- a/src/elements/Icon/Icon.d.ts +++ b/src/elements/Icon/Icon.d.ts @@ -48,6 +48,14 @@ export interface StrictIconProps { /** Name of the icon. */ name?: SemanticICONS + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick?: (event: React.MouseEvent, data: IconProps) => void + /** Icon can rotated. */ rotated?: 'clockwise' | 'counterclockwise' diff --git a/src/elements/Icon/Icon.js b/src/elements/Icon/Icon.js index a702cdcb4c..9e22c24c25 100644 --- a/src/elements/Icon/Icon.js +++ b/src/elements/Icon/Icon.js @@ -83,7 +83,7 @@ const Icon = React.forwardRef(function (props, ref) { return } - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) }) return ( @@ -135,6 +135,14 @@ Icon.propTypes = { /** Name of the icon. */ name: customPropTypes.suggest(SUI.ALL_ICONS_IN_ALL_CONTEXTS), + /** + * Called on click. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props. + */ + onClick: PropTypes.func, + /** Icon can rotated. */ rotated: PropTypes.oneOf(['clockwise', 'counterclockwise']), diff --git a/src/elements/Input/Input.js b/src/elements/Input/Input.js index 11fa3cc6fe..692d794666 100644 --- a/src/elements/Input/Input.js +++ b/src/elements/Input/Input.js @@ -72,7 +72,7 @@ const Input = React.forwardRef(function (props, ref) { const handleChange = (e) => { const newValue = _.get(e, 'target.value') - _.invoke(props, 'onChange', e, { ...props, value: newValue }) + props.onChange?.(e, { ...props, value: newValue }) } const partitionProps = () => { diff --git a/src/elements/Label/Label.js b/src/elements/Label/Label.js index 736234687a..bd9b64e521 100644 --- a/src/elements/Label/Label.js +++ b/src/elements/Label/Label.js @@ -78,7 +78,7 @@ const Label = React.forwardRef(function (props, ref) { const ElementType = getComponentType(props) const handleClick = useEventCallback((e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) }) if (!childrenUtils.isNil(children)) { @@ -102,8 +102,8 @@ const Label = React.forwardRef(function (props, ref) { autoGenerateKey: false, overrideProps: (predefinedProps) => ({ onClick: (e) => { - _.invoke(predefinedProps, 'onClick', e) - _.invoke(props, 'onRemove', e, props) + predefinedProps.onClick?.(e) + props.onRemove?.(e, props) }, }), })} diff --git a/src/elements/List/List.js b/src/elements/List/List.js index cb134c41ee..05ad126859 100644 --- a/src/elements/List/List.js +++ b/src/elements/List/List.js @@ -88,8 +88,8 @@ const List = React.forwardRef(function (props, ref) { ListItem.create(item, { overrideProps: (predefinedProps) => ({ onClick: (e, itemProps) => { - _.invoke(predefinedProps, 'onClick', e, itemProps) - _.invoke(props, 'onItemClick', e, itemProps) + predefinedProps.onClick?.(e, itemProps) + props.onItemClick?.(e, itemProps) }, }), }), diff --git a/src/elements/List/ListItem.js b/src/elements/List/ListItem.js index 2491728065..097c513c5b 100644 --- a/src/elements/List/ListItem.js +++ b/src/elements/List/ListItem.js @@ -46,7 +46,7 @@ const ListItem = React.forwardRef(function (props, ref) { const handleClick = useEventCallback((e) => { if (!disabled) { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } }) const valueProp = ElementType === 'li' ? { value } : { 'data-value': value } diff --git a/src/elements/Step/Step.js b/src/elements/Step/Step.js index 4cc17ed1c2..5957d5c659 100644 --- a/src/elements/Step/Step.js +++ b/src/elements/Step/Step.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -39,7 +38,7 @@ const Step = React.forwardRef(function (props, ref) { const handleClick = useEventCallback((e) => { if (!disabled) { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } }) diff --git a/src/lib/ModernAutoControlledComponent.js b/src/lib/ModernAutoControlledComponent.js index 9aeec29e2b..c95b417416 100644 --- a/src/lib/ModernAutoControlledComponent.js +++ b/src/lib/ModernAutoControlledComponent.js @@ -72,7 +72,7 @@ export default class ModernAutoControlledComponent extends React.Component { super(...args) const { autoControlledProps, getAutoControlledStateFromProps } = this.constructor - const state = _.invoke(this, 'getInitialAutoControlledState', this.props) || {} + const state = this.getInitialAutoControlledState?.(this.props) || {} if (process.env.NODE_ENV !== 'production') { const { defaultProps, name, propTypes, getDerivedStateFromProps } = this.constructor diff --git a/src/lib/doesNodeContainClick.js b/src/lib/doesNodeContainClick.js index d400098eeb..c44d24b04a 100644 --- a/src/lib/doesNodeContainClick.js +++ b/src/lib/doesNodeContainClick.js @@ -16,10 +16,10 @@ const doesNodeContainClick = (node, e) => { // if there is an e.target and it is in the document, use a simple node.contains() check if (e.target) { - _.invoke(e.target, 'setAttribute', 'data-suir-click-target', true) + e.target.setAttribute?.('data-suir-click-target', true) if (document.querySelector('[data-suir-click-target=true]')) { - _.invoke(e.target, 'removeAttribute', 'data-suir-click-target') + e.target.removeAttribute?.('data-suir-click-target') if (typeof node.contains === 'function') { return node.contains(e.target) diff --git a/src/modules/Accordion/AccordionAccordion.js b/src/modules/Accordion/AccordionAccordion.js index f99285ee5d..1e020fd554 100644 --- a/src/modules/Accordion/AccordionAccordion.js +++ b/src/modules/Accordion/AccordionAccordion.js @@ -60,7 +60,7 @@ const AccordionAccordion = React.forwardRef(function (props, ref) { const { index } = titleProps setActiveIndex(computeNewIndex(exclusive, activeIndex, index)) - _.invoke(props, 'onTitleClick', e, titleProps) + props.onTitleClick?.(e, titleProps) }) if (process.env.NODE_ENV !== 'production') { diff --git a/src/modules/Accordion/AccordionPanel.js b/src/modules/Accordion/AccordionPanel.js index b66acce997..bf2da38f75 100644 --- a/src/modules/Accordion/AccordionPanel.js +++ b/src/modules/Accordion/AccordionPanel.js @@ -1,4 +1,3 @@ -import _ from 'lodash' import PropTypes from 'prop-types' import React, { Component } from 'react' @@ -12,8 +11,8 @@ import AccordionContent from './AccordionContent' class AccordionPanel extends Component { handleTitleOverrides = (predefinedProps) => ({ onClick: (e, titleProps) => { - _.invoke(predefinedProps, 'onClick', e, titleProps) - _.invoke(this.props, 'onTitleClick', e, titleProps) + predefinedProps.onClick?.(e, titleProps) + this.props.onTitleClick?.(e, titleProps) }, }) diff --git a/src/modules/Accordion/AccordionTitle.js b/src/modules/Accordion/AccordionTitle.js index 34405b861e..ced4c1315b 100644 --- a/src/modules/Accordion/AccordionTitle.js +++ b/src/modules/Accordion/AccordionTitle.js @@ -26,7 +26,7 @@ const AccordionTitle = React.forwardRef(function (props, ref) { const iconValue = _.isNil(icon) ? 'dropdown' : icon const handleClick = useEventCallback((e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) }) if (!childrenUtils.isNil(children)) { diff --git a/src/modules/Checkbox/Checkbox.js b/src/modules/Checkbox/Checkbox.js index d183f01feb..502c62df69 100644 --- a/src/modules/Checkbox/Checkbox.js +++ b/src/modules/Checkbox/Checkbox.js @@ -96,7 +96,7 @@ const Checkbox = React.forwardRef(function (props, ref) { debug('handleChange()', _.get(e, 'target.tagName')) - _.invoke(props, 'onChange', e, { + props.onChange?.(e, { ...props, checked: !checked, indeterminate: false, @@ -108,8 +108,8 @@ const Checkbox = React.forwardRef(function (props, ref) { const handleClick = (e) => { debug('handleClick()', _.get(e, 'target.tagName')) - const isInputClick = _.invoke(inputRef.current, 'contains', e.target) - const isLabelClick = _.invoke(labelRef.current, 'contains', e.target) + const isInputClick = inputRef.current.contains?.(e.target) + const isLabelClick = labelRef.current.contains?.(e.target) const isRootClick = !isLabelClick && !isInputClick const hasId = !_.isNil(id) @@ -117,7 +117,7 @@ const Checkbox = React.forwardRef(function (props, ref) { // https://github.com/Semantic-Org/Semantic-UI-React/pull/3351 if (!isLabelClickAndForwardedToInput) { - _.invoke(props, 'onClick', e, { + props.onClick?.(e, { ...props, checked: !checked, indeterminate: !!indeterminate, @@ -147,14 +147,14 @@ const Checkbox = React.forwardRef(function (props, ref) { const handleMouseDown = (e) => { debug('handleMouseDown()') - _.invoke(props, 'onMouseDown', e, { + props.onMouseDown?.(e, { ...props, checked: !!checked, indeterminate: !!indeterminate, }) if (!e.defaultPrevented) { - _.invoke(inputRef.current, 'focus') + inputRef.current?.focus?.() } // Heads up! @@ -166,7 +166,7 @@ const Checkbox = React.forwardRef(function (props, ref) { debug('handleMouseUp()') isClickFromMouse.current = true - _.invoke(props, 'onMouseUp', e, { + props.onMouseUp?.(e, { ...props, checked: !!checked, indeterminate: !!indeterminate, diff --git a/src/modules/Dimmer/DimmerInner.js b/src/modules/Dimmer/DimmerInner.js index 636a0370d6..1f5ced1436 100644 --- a/src/modules/Dimmer/DimmerInner.js +++ b/src/modules/Dimmer/DimmerInner.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -47,13 +46,13 @@ const DimmerInner = React.forwardRef(function (props, ref) { }, [active]) const handleClick = (e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) if (contentRef.current !== e.target && doesNodeContainClick(contentRef.current, e)) { return } - _.invoke(props, 'onClickOutside', e, props) + props.onClickOutside?.(e, props) } const classes = cx( diff --git a/src/modules/Dropdown/Dropdown.js b/src/modules/Dropdown/Dropdown.js index d77720b102..e87f0b8429 100644 --- a/src/modules/Dropdown/Dropdown.js +++ b/src/modules/Dropdown/Dropdown.js @@ -231,7 +231,7 @@ class DropdownInner extends Component { // can't rely on props.value if we are controlled handleChange = (e, value) => { debug('handleChange()', value) - _.invoke(this.props, 'onChange', e, { ...this.props, value }) + this.props.onChange?.(e, { ...this.props, value }) } closeOnChange = (e) => { @@ -342,7 +342,7 @@ class DropdownInner extends Component { // Heads up! This event handler should be called after `onChange` // Notify the onAddItem prop if this is a new value if (item['data-additional']) { - _.invoke(this.props, 'onAddItem', e, { ...this.props, value: selectedValue }) + this.props.onAddItem?.(e, { ...this.props, value: selectedValue }) } } @@ -411,7 +411,7 @@ class DropdownInner extends Component { this.clearSearchQuery() if (search) { - _.invoke(this.searchRef.current, 'focus') + this.searchRef.current?.focus?.() } } @@ -452,7 +452,7 @@ class DropdownInner extends Component { debug('handleMouseDown()') this.isMouseDown = true - _.invoke(this.props, 'onMouseDown', e, this.props) + this.props.onMouseDown?.(e, this.props) document.addEventListener('mouseup', this.handleDocumentMouseUp) } @@ -469,20 +469,20 @@ class DropdownInner extends Component { const { minCharacters, search } = this.props const { open, searchQuery } = this.state - _.invoke(this.props, 'onClick', e, this.props) + this.props.onClick?.(e, this.props) // prevent closeOnDocumentClick() e.stopPropagation() if (!search) return this.toggle(e) if (open) { - _.invoke(this.searchRef.current, 'focus') + this.searchRef.current?.focus?.() return } if (searchQuery.length >= minCharacters || minCharacters === 1) { this.open(e) return } - _.invoke(this.searchRef.current, 'focus') + this.searchRef.current?.focus?.() } handleIconClick = (e) => { @@ -490,7 +490,7 @@ class DropdownInner extends Component { const hasValue = this.hasValue() debug('handleIconClick()', { e, clearable, hasValue }) - _.invoke(this.props, 'onClick', e, this.props) + this.props.onClick?.(e, this.props) // prevent handleClick() e.stopPropagation() @@ -534,9 +534,9 @@ class DropdownInner extends Component { this.clearSearchQuery() if (search) { - _.invoke(this.searchRef.current, 'focus') + this.searchRef.current?.focus?.() } else { - _.invoke(this.ref.current, 'focus') + this.ref.current?.focus?.() } this.closeOnChange(e) @@ -544,7 +544,7 @@ class DropdownInner extends Component { // Heads up! This event handler should be called after `onChange` // Notify the onAddItem prop if this is a new value if (isAdditionItem) { - _.invoke(this.props, 'onAddItem', e, { ...this.props, value }) + this.props.onAddItem?.(e, { ...this.props, value }) } } @@ -554,7 +554,7 @@ class DropdownInner extends Component { if (focus) return - _.invoke(this.props, 'onFocus', e, this.props) + this.props.onFocus?.(e, this.props) this.setState({ focus: true }) } @@ -570,7 +570,7 @@ class DropdownInner extends Component { // do not "blur" when the mouse is down inside of the Dropdown if (this.isMouseDown) return - _.invoke(this.props, 'onBlur', e, this.props) + this.props.onBlur?.(e, this.props) if (selectOnBlur && !multiple) { this.makeSelectedItemActive(e, this.state.selectedIndex) @@ -592,7 +592,7 @@ class DropdownInner extends Component { const { open } = this.state const newQuery = value - _.invoke(this.props, 'onSearchChange', e, { ...this.props, searchQuery: newQuery }) + this.props.onSearchChange?.(e, { ...this.props, searchQuery: newQuery }) this.setState({ searchQuery: newQuery, selectedIndex: 0 }) // open search dropdown on search query @@ -610,7 +610,7 @@ class DropdownInner extends Component { this.openOnSpace(e) this.selectItemOnEnter(e) - _.invoke(this.props, 'onKeyDown', e) + this.props.onKeyDown?.(e) } // ---------------------------------------- @@ -685,7 +685,7 @@ class DropdownInner extends Component { e.stopPropagation() this.setState({ selectedLabel: labelProps.value }) - _.invoke(this.props, 'onLabelClick', e, labelProps) + this.props.onLabelClick?.(e, labelProps) } handleLabelRemove = (e, labelProps) => { @@ -757,7 +757,7 @@ class DropdownInner extends Component { return { className: classes, onClick: (e) => { - _.invoke(predefinedProps, 'onClick', e, predefinedProps) + predefinedProps.onClick?.(e, predefinedProps) this.handleIconClick(e) }, } @@ -808,7 +808,7 @@ class DropdownInner extends Component { handleSearchInputOverrides = (predefinedProps) => ({ onChange: (e, inputProps) => { - _.invoke(predefinedProps, 'onChange', e, inputProps) + predefinedProps.onChange?.(e, inputProps) this.handleSearchChange(e, inputProps) }, ref: this.searchRef, @@ -871,9 +871,9 @@ class DropdownInner extends Component { debug('open()', { disabled, search, open: this.state.open }) if (disabled) return - if (search) _.invoke(this.searchRef.current, 'focus') + if (search) this.searchRef.current?.focus?.() - _.invoke(this.props, 'onOpen', e, this.props) + this.props.onOpen?.(e, this.props) if (triggerSetState) { this.setState({ open: true }) @@ -885,7 +885,7 @@ class DropdownInner extends Component { debug('close()', { open: this.state.open }) if (this.state.open) { - _.invoke(this.props, 'onClose', e, this.props) + this.props.onClose?.(e, this.props) this.setState({ open: false }, callback) } } diff --git a/src/modules/Dropdown/DropdownItem.js b/src/modules/Dropdown/DropdownItem.js index a2e85e900f..42faca6821 100644 --- a/src/modules/Dropdown/DropdownItem.js +++ b/src/modules/Dropdown/DropdownItem.js @@ -37,7 +37,7 @@ const DropdownItem = React.forwardRef(function (props, ref) { } = props const handleClick = (e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } const classes = cx( diff --git a/src/modules/Dropdown/DropdownSearchInput.d.ts b/src/modules/Dropdown/DropdownSearchInput.d.ts index db021d28f2..75e07019de 100644 --- a/src/modules/Dropdown/DropdownSearchInput.d.ts +++ b/src/modules/Dropdown/DropdownSearchInput.d.ts @@ -14,6 +14,14 @@ export interface StrictDropdownSearchInputProps { /** Additional classes. */ className?: string + /** + * Called on change. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onChange?: (event: React.ChangeEvent, data: DropdownSearchInputProps) => void + /** An input can receive focus. */ tabIndex?: number | string diff --git a/src/modules/Dropdown/DropdownSearchInput.js b/src/modules/Dropdown/DropdownSearchInput.js index 9917e81a53..4fd1c74226 100644 --- a/src/modules/Dropdown/DropdownSearchInput.js +++ b/src/modules/Dropdown/DropdownSearchInput.js @@ -14,7 +14,7 @@ const DropdownSearchInput = React.forwardRef(function (props, ref) { const handleChange = (e) => { const newValue = _.get(e, 'target.value') - _.invoke(props, 'onChange', e, { ...props, value: newValue }) + props.onChange?.(e, { ...props, value: newValue }) } const classes = cx('search', className) @@ -47,6 +47,14 @@ DropdownSearchInput.propTypes = { /** Additional classes. */ className: PropTypes.string, + /** + * Called when the user attempts to change the value. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props and proposed value. + */ + onChange: PropTypes.func, + /** An input can receive focus. */ tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), diff --git a/src/modules/Embed/Embed.js b/src/modules/Embed/Embed.js index 2854feebc9..37399dd734 100644 --- a/src/modules/Embed/Embed.js +++ b/src/modules/Embed/Embed.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -70,7 +69,7 @@ const Embed = React.forwardRef(function (props, ref) { } const handleClick = (e) => { - _.invoke(props, 'onClick', e, { ...props, active: true }) + props.onClick?.(e, { ...props, active: true }) if (!active) { setActive(true) } diff --git a/src/modules/Modal/Modal.js b/src/modules/Modal/Modal.js index eb89025712..bc9f024356 100644 --- a/src/modules/Modal/Modal.js +++ b/src/modules/Modal/Modal.js @@ -108,7 +108,7 @@ const Modal = React.forwardRef(function (props, ref) { debug('close()') setOpen(false) - _.invoke(props, 'onClose', e, { ...props, open: false }) + props.onClose?.(e, { ...props, open: false }) } const handleDocumentMouseDown = (e) => { @@ -129,14 +129,14 @@ const Modal = React.forwardRef(function (props, ref) { return setOpen(false) - _.invoke(props, 'onClose', e, { ...props, open: false }) + props.onClose?.(e, { ...props, open: false }) } const handleOpen = (e) => { debug('open()') setOpen(true) - _.invoke(props, 'onOpen', e, { ...props, open: true }) + props.onOpen?.(e, { ...props, open: true }) } const handlePortalMount = (e) => { @@ -153,7 +153,7 @@ const Modal = React.forwardRef(function (props, ref) { pool: eventPool, target: dimmerRef.current, }) - _.invoke(props, 'onMount', e, props) + props.onMount?.(e, props) } const handlePortalUnmount = (e) => { @@ -168,7 +168,7 @@ const Modal = React.forwardRef(function (props, ref) { pool: eventPool, target: dimmerRef.current, }) - _.invoke(props, 'onUnmount', e, props) + props.onUnmount?.(e, props) } // ---------------------------------------- @@ -191,7 +191,7 @@ const Modal = React.forwardRef(function (props, ref) { const closeIconJSX = Icon.create(closeIconName, { overrideProps: (predefinedProps) => ({ onClick: (e) => { - _.invoke(predefinedProps, 'onClick', e) + predefinedProps.onClick?.(e) handleClose(e) }, }), @@ -212,8 +212,8 @@ const Modal = React.forwardRef(function (props, ref) { {ModalActions.create(actions, { overrideProps: (predefinedProps) => ({ onActionClick: (e, actionProps) => { - _.invoke(predefinedProps, 'onActionClick', e, actionProps) - _.invoke(props, 'onActionClick', e, props) + predefinedProps.onActionClick?.(e, actionProps) + props.onActionClick?.(e, props) handleClose(e) }, diff --git a/src/modules/Modal/ModalActions.js b/src/modules/Modal/ModalActions.js index 583bcb7091..eda0aeea81 100644 --- a/src/modules/Modal/ModalActions.js +++ b/src/modules/Modal/ModalActions.js @@ -43,8 +43,8 @@ const ModalActions = React.forwardRef(function (props, ref) { Button.create(action, { overrideProps: (predefinedProps) => ({ onClick: (e, buttonProps) => { - _.invoke(predefinedProps, 'onClick', e, buttonProps) - _.invoke(props, 'onActionClick', e, buttonProps) + predefinedProps.onClick?.(e, buttonProps) + props.onActionClick?.(e, buttonProps) }, }), }), diff --git a/src/modules/Popup/Popup.js b/src/modules/Popup/Popup.js index 2c3c9ba4f2..be797f61e2 100644 --- a/src/modules/Popup/Popup.js +++ b/src/modules/Popup/Popup.js @@ -171,12 +171,12 @@ const Popup = React.forwardRef(function (props, ref) { const handleClose = (e) => { debug('handleClose()') - _.invoke(props, 'onClose', e, { ...props, open: false }) + props.onClose?.(e, { ...props, open: false }) } const handleOpen = (e) => { debug('handleOpen()') - _.invoke(props, 'onOpen', e, { ...props, open: true }) + props.onOpen?.(e, { ...props, open: true }) } const hideOnScroll = (e) => { @@ -199,14 +199,14 @@ const Popup = React.forwardRef(function (props, ref) { const handlePortalMount = (e) => { debug('handlePortalMount()') - _.invoke(props, 'onMount', e, props) + props.onMount?.(e, props) } const handlePortalUnmount = (e) => { debug('handlePortalUnmount()') positionUpdate.current = null - _.invoke(props, 'onUnmount', e, props) + props.onUnmount?.(e, props) } // ---------------------------------------- diff --git a/src/modules/Popup/lib/createReferenceProxy.js b/src/modules/Popup/lib/createReferenceProxy.js index 8da1b109c0..6d701a42b1 100644 --- a/src/modules/Popup/lib/createReferenceProxy.js +++ b/src/modules/Popup/lib/createReferenceProxy.js @@ -7,7 +7,7 @@ class ReferenceProxy { } getBoundingClientRect() { - return _.invoke(this.ref.current, 'getBoundingClientRect') || {} + return this.ref.current?.getBoundingClientRect?.() || {} } get clientWidth() { diff --git a/src/modules/Rating/Rating.d.ts b/src/modules/Rating/Rating.d.ts index 5fccb4c1c3..12a207251f 100644 --- a/src/modules/Rating/Rating.d.ts +++ b/src/modules/Rating/Rating.d.ts @@ -32,6 +32,13 @@ export interface StrictRatingProps { /** The total number of icons. */ maxRating?: number | string + /** + * Called after user moves cursor out of element. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + */ + onMouseLeave?: (event: React.MouseEvent) => void + /** * Called after user selects a new rating. * diff --git a/src/modules/Rating/Rating.js b/src/modules/Rating/Rating.js index 0f1274dbea..80c6c09704 100644 --- a/src/modules/Rating/Rating.js +++ b/src/modules/Rating/Rating.js @@ -58,7 +58,7 @@ const Rating = React.forwardRef(function (props, ref) { setRating(newRating) setIsSelecting(false) - _.invoke(props, 'onRate', e, { ...props, rating: newRating }) + props.onRate?.(e, { ...props, rating: newRating }) } const handleIconMouseEnter = (e, { index }) => { @@ -71,7 +71,7 @@ const Rating = React.forwardRef(function (props, ref) { } const handleMouseLeave = (...args) => { - _.invoke(props, 'onMouseLeave', ...args) + props.onMouseLeave?.(...args) if (disabled) { return @@ -136,6 +136,13 @@ Rating.propTypes = { /** The total number of icons. */ maxRating: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * Called after user moves cursor out of element. + * + * @param {SyntheticEvent} event - React's original SyntheticEvent. + */ + onMouseLeave: PropTypes.func, + /** * Called after user selects a new rating. * diff --git a/src/modules/Rating/RatingIcon.js b/src/modules/Rating/RatingIcon.js index e21cb050fa..3510c2fd4d 100644 --- a/src/modules/Rating/RatingIcon.js +++ b/src/modules/Rating/RatingIcon.js @@ -1,6 +1,5 @@ import cx from 'clsx' import keyboardKey from 'keyboard-key' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -22,24 +21,24 @@ const RatingIcon = React.forwardRef(function (props, ref) { const ElementType = getComponentType(props, { defaultAs: 'i' }) const handleClick = (e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } const handleKeyUp = (e) => { - _.invoke(props, 'onKeyUp', e, props) + props.onKeyUp?.(e, props) switch (keyboardKey.getCode(e)) { case keyboardKey.Enter: case keyboardKey.Spacebar: e.preventDefault() - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) break default: } } const handleMouseEnter = (e) => { - _.invoke(props, 'onMouseEnter', e, props) + props.onMouseEnter?.(e, props) } return ( diff --git a/src/modules/Search/Search.js b/src/modules/Search/Search.js index 5e5e0ed5b8..5d535e5f90 100644 --- a/src/modules/Search/Search.js +++ b/src/modules/Search/Search.js @@ -152,14 +152,14 @@ class SearchInner extends Component { debug('handleResultSelect()') debug(result) - _.invoke(this.props, 'onResultSelect', e, { ...this.props, result }) + this.props.onResultSelect?.(e, { ...this.props, result }) } handleSelectionChange = (e) => { debug('handleSelectionChange()') const result = this.getSelectedResult() - _.invoke(this.props, 'onSelectionChange', e, { ...this.props, result }) + this.props.onSelectionChange?.(e, { ...this.props, result }) } closeOnEscape = (e) => { @@ -217,7 +217,7 @@ class SearchInner extends Component { debug('handleMouseDown()') this.isMouseDown = true - _.invoke(this.props, 'onMouseDown', e, this.props) + this.props.onMouseDown?.(e, this.props) eventStack.sub('mouseup', this.handleDocumentMouseUp) } @@ -262,14 +262,14 @@ class SearchInner extends Component { handleFocus = (e) => { debug('handleFocus()') - _.invoke(this.props, 'onFocus', e, this.props) + this.props.onFocus?.(e, this.props) this.setState({ focus: true }) } handleBlur = (e) => { debug('handleBlur()') - _.invoke(this.props, 'onBlur', e, this.props) + this.props.onBlur?.(e, this.props) this.setState({ focus: false }) } @@ -282,7 +282,7 @@ class SearchInner extends Component { const { open } = this.state const newQuery = e.target.value - _.invoke(this.props, 'onSearchChange', e, { ...this.props, value: newQuery }) + this.props.onSearchChange?.(e, { ...this.props, value: newQuery }) // open search dropdown on search query if (newQuery.length < minCharacters) { diff --git a/src/modules/Search/SearchResult.js b/src/modules/Search/SearchResult.js index fe486c6535..bf375777f6 100644 --- a/src/modules/Search/SearchResult.js +++ b/src/modules/Search/SearchResult.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -35,7 +34,7 @@ const SearchResult = React.forwardRef(function (props, ref) { const { active, className, renderer = defaultRenderer } = props const handleClick = (e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) } const classes = cx(useKeyOnly(active, 'active'), 'result', className) diff --git a/src/modules/Sidebar/Sidebar.js b/src/modules/Sidebar/Sidebar.js index 0489850b56..08ef8751d6 100644 --- a/src/modules/Sidebar/Sidebar.js +++ b/src/modules/Sidebar/Sidebar.js @@ -1,6 +1,5 @@ import { EventListener, documentRef } from '@fluentui/react-component-event-listener' import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -71,7 +70,7 @@ const Sidebar = React.forwardRef((props, ref) => { const callback = visible ? 'onShow' : 'onHidden' resetAnimationTick() - _.invoke(props, callback, null, props) + props[callback]?.(null, props) }) const handleAnimationStart = useEventCallback(() => { @@ -85,13 +84,13 @@ const Sidebar = React.forwardRef((props, ref) => { return } - _.invoke(props, callback, null, props) + props[callback]?.(null, props) }) const handleDocumentClick = (e) => { if (!doesNodeContainClick(elementRef.current, e)) { skipNextCallback.current = true - _.invoke(props, 'onHide', e, { ...props, visible: false }) + props.onHide?.(e, { ...props, visible: false }) } } diff --git a/src/modules/Sticky/Sticky.js b/src/modules/Sticky/Sticky.js index 5fc0709678..af3c7435b9 100644 --- a/src/modules/Sticky/Sticky.js +++ b/src/modules/Sticky/Sticky.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -100,28 +99,28 @@ const Sticky = React.forwardRef(function (props, ref) { setBound(newBound) setSticky(true) - _.invoke(props, 'onStick', e, props) + props.onStick?.(e, props) } const setUnsticked = (e, newBound) => { setBound(newBound) setSticky(false) - _.invoke(props, 'onUnstick', e, props) + props.onUnstick?.(e, props) } const stickToContextBottom = (e) => { setSticked(e, true) togglePushing(true) - _.invoke(props, 'onBottom', e, props) + props.onBottom?.(e, props) } const stickToContextTop = (e) => { setUnsticked(e, false) togglePushing(false) - _.invoke(props, 'onTop', e, props) + props.onTop?.(e, props) } const stickToScreenBottom = (e) => { diff --git a/src/modules/Tab/Tab.js b/src/modules/Tab/Tab.js index cbbac2bf46..0373447dcf 100644 --- a/src/modules/Tab/Tab.js +++ b/src/modules/Tab/Tab.js @@ -34,13 +34,13 @@ const Tab = React.forwardRef(function (props, ref) { }) const handleItemClick = (e, { index }) => { - _.invoke(props, 'onTabChange', e, { ...props, activeIndex: index }) + props.onTabChange?.(e, { ...props, activeIndex: index }) setActiveIndex(index) } const renderItems = () => { if (renderActiveOnly) { - return _.invoke(_.get(panes, `[${activeIndex}]`), 'render', props) + return panes?.[activeIndex]?.render?.(props) } return _.map(panes, ({ pane }, index) => diff --git a/src/modules/Transition/Transition.js b/src/modules/Transition/Transition.js index 3ad3c88018..46b677cc5d 100644 --- a/src/modules/Transition/Transition.js +++ b/src/modules/Transition/Transition.js @@ -97,14 +97,14 @@ export default class Transition extends React.Component { } if (!prevState.animating && this.state.animating) { - _.invoke(this.props, 'onStart', null, { ...this.props, status: this.state.status }) + this.props.onStart?.(null, { ...this.props, status: this.state.status }) } if (prevState.animating && !this.state.animating) { const callback = this.state.status === TRANSITION_STATUS_ENTERED ? 'onShow' : 'onHide' - _.invoke(this.props, 'onComplete', null, { ...this.props, status: this.state.status }) - _.invoke(this.props, callback, null, { ...this.props, status: this.state.status }) + this.props.onComplete?.(null, { ...this.props, status: this.state.status }) + this.props[callback]?.(null, { ...this.props, status: this.state.status }) } } diff --git a/src/views/Card/Card.js b/src/views/Card/Card.js index aa6c54a202..c6d319e71d 100644 --- a/src/views/Card/Card.js +++ b/src/views/Card/Card.js @@ -1,5 +1,4 @@ import cx from 'clsx' -import _ from 'lodash' import PropTypes from 'prop-types' import * as React from 'react' @@ -61,7 +60,7 @@ const Card = React.forwardRef(function (props, ref) { }) const handleClick = useEventCallback((e) => { - _.invoke(props, 'onClick', e, props) + props.onClick?.(e, props) }) if (!childrenUtils.isNil(children)) {