import * as React from 'react' ; import * as PropTypes from 'prop-types' ; import { Button , Tooltip } from 'antd' ; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' ; import { IconProp } from '@fortawesome/fontawesome-svg-core' ; import _isFunction from 'lodash/isFunction' ; import { AbstractTooltipProps , TooltipPlacement } from 'antd/lib/tooltip' ; import { SimpleButtonProps } from '../SimpleButton/SimpleButton' ; import logger from '@terrestris/base-util/dist/Logger' ; import { CSS_PREFIX } from '../../constants' ; import '../../Util/fontawesome' ; import './ToggleButton.less' ; interface DefaultProps { type : 'default' | 'primary' | 'ghost' | 'dashed' | 'danger' | 'link' ; /**
     * Additional [antd tooltip](https://ant.design/components/tooltip/)
     * properties to pass to the tooltip component. Note: The props `title`
     * and `placement` will override the props `tooltip` and `tooltipPlacement`
     * of this component!
     */ tooltipProps : AbstractTooltipProps ; /**
     * The initial pressed state of the ToggleButton
     * Note: If a ToggleButton is inside a ToggleGroup, the pressed state will be controlled by the selectedName property
     * of the ToggleGroup and this property will be ignored.
     */ pressed : boolean ; /**
     * The toggle handler
     */ onToggle : ( pressed : boolean , lastClickEvt : any ) => void ; } interface BaseProps { className ? : string ; /**
     * The icon to render for the pressed state. See
     * https://ant.design/components/icon/.
     */ pressedIcon ? : React . ReactNode ; /**
     * The name of the font awesome icon. It could be:
     * - an icon object, like `{ faCoffee }`.
     * - a string, like `'coffee'`.
     * - an array of strings, where the first element is a style prefix
     *   and the second element is the icon name: `{ ['fab', 'apple'] }`
     * For a list of possible icons, please see on https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
    */ pressedIconName ? : IconProp ; /**
     * The tooltip to be shown on hover.
     */ tooltip ? : string ; /**
     * The position of the tooltip.
     */ tooltipPlacement ? : TooltipPlacement ; } interface ToggleButtonState { pressed : boolean ; lastClickEvt : any ; overallPressed : boolean ; isClicked : boolean ; } export type ToggleButtonProps = BaseProps & Partial < DefaultProps > & SimpleButtonProps ; class extends React . Component class ToggleButton extends React . Component < ToggleButtonProps , ToggleButtonState > { /**
     * The default properties.
     */ static defaultProps : DefaultProps = { type : 'primary' , pressed : false , tooltipProps : { mouseEnterDelay : 1.5 } , onToggle : ( ) => undefined } ; /**
     * The context types.
     */ static contextTypes = { toggleGroup : PropTypes . object } ; private _className = `${ CSS_PREFIX }togglebutton` ; /**
     * The class to apply for a toggled/pressed button.
     */ pressedClass = 'btn-pressed' ; constructs constructor ( props ) { super ( props ) ; this . state = { pressed : props . pressed , lastClickEvt : null , overallPressed : props . pressed , isClicked : false } ; } param nextProps param prevState static getDerivedStateFromProps ( nextProps , prevState ) { if ( prevState . pressed !== nextProps . pressed ) { return { pressed : nextProps . pressed , overallPressed : nextProps . pressed , isClicked : false , lastClickEvt : null } ; } return null ; } /**
     * We will handle the initial state of the button here.
     * If it is pressed, we will have to call its `onToggle`
     * method, if it exists, in order to reflect the initial
     * state correctly (e.g. activating ol.Controls)
     */ componentDidMount ( ) { if ( this . props . onToggle && this . props . pressed === true ) { this . props . onToggle ( true , null ) ; } } method componentDidUpdate ( prevProps , prevState ) { const { onToggle } = this . props ; const { pressed , lastClickEvt , overallPressed , isClicked } = this . state ; /**
         * the following is performed here as a hack to keep track of the pressed changes.
         *
         * check if the button has been clicked
         * |__ YES: ==> toggle the button
         * |
         * |__ NO: check if the prop has changed
         *        |__ YES: ==> Toggle the button
         *        |__ NO: check if previous update action was a click
         *                |__ YES: ==> run the Toggle function fo the prop value
         */ let shouldToggle ; if ( isClicked || prevState . pressed !== pressed || prevState . isClicked ) { if ( isClicked ) { shouldToggle = true ; } else { if ( pressed !== prevState . overallPressed ) { shouldToggle = true ; } else { if ( prevState . isClicked ) { if ( prevState . overallPressed !== overallPressed ) { shouldToggle = true ; } } } } if ( shouldToggle && onToggle ) { onToggle ( overallPressed , lastClickEvt ) ; } } } param evt method onClick ( evt ) { evt . persist ( ) ; this . setState ( { overallPressed : ! this . state . overallPressed , lastClickEvt : evt , isClicked : true } , ( ) => { if ( this . context . toggleGroup && _isFunction ( this . context . toggleGroup . onChange ) ) { this . context . toggleGroup . onChange ( this . props ) ; this . setState ( { overallPressed : ! this . state . overallPressed } ) ; } } ) ; } /**
     * The render function.
     */ render ( ) { const { overallPressed } = this . state ; const { className , icon , iconName , pressedIcon , pressedIconName , pressed , onToggle , tooltip , tooltipPlacement , tooltipProps , ... antBtnProps } = this . props ; const { onClick , ... filteredAntBtnProps } = antBtnProps ; const finalClassName = className ? `${ className } ${ this . _className }` : this . _className ; let pressedClass = '' ; if ( overallPressed ) { pressedClass = ` ${ this . pressedClass } ` ; } if ( icon && iconName ) { logger . warn ( 'Provide either an icon node or the name of a fa icon. ' + 'If both are provided the fa icon will be rendered.' ) ; } let iconToRender ; if ( icon ) { iconToRender = icon ; } if ( iconName ) { iconToRender = < FontAwesomeIcon icon = { iconName } />; } if ( pressedIcon && pressedIconName ) { logger . warn ( 'Provide either a pressed icon node or the name of a fa ' + 'icon. If both are provided the fa icon will be rendered.' ) ; } let pressedIconToRender ; if ( pressedIcon ) { pressedIconToRender = pressedIcon ; } if ( pressedIconName ) { pressedIconToRender = < FontAwesomeIcon icon = { pressedIconName } />; } return ( < Tooltip title = { tooltip } placement = { tooltipPlacement } { ... tooltipProps } > < Button className = { `${ finalClassName }${ pressedClass }` } aria - pressed = { overallPressed } onClick = { this . onClick . bind ( this ) } icon = { overallPressed ? pressedIconToRender : iconToRender } { ... filteredAntBtnProps } > { antBtnProps . children } < /Button> < /Tooltip>); } } export default ToggleButton ;