UNPKG

3.19 kBJavaScriptView Raw
1import React from 'react';
2import PropTypes from 'prop-types';
3import classNames from 'classnames';
4import { Reference } from 'react-popper';
5import { DropdownContext } from './DropdownContext';
6import { mapToCssModules, tagPropType } from './utils';
7import Button from './Button';
8
9const propTypes = {
10 caret: PropTypes.bool,
11 color: PropTypes.string,
12 children: PropTypes.node,
13 className: PropTypes.string,
14 cssModule: PropTypes.object,
15 disabled: PropTypes.bool,
16 onClick: PropTypes.func,
17 'aria-haspopup': PropTypes.bool,
18 split: PropTypes.bool,
19 tag: tagPropType,
20 nav: PropTypes.bool,
21 innerRef: PropTypes.oneOfType([
22 PropTypes.object,
23 PropTypes.string,
24 PropTypes.func,
25 ]),
26};
27
28const defaultProps = {
29 color: 'secondary',
30 'aria-haspopup': true,
31};
32
33class DropdownToggle extends React.Component {
34 constructor(props) {
35 super(props);
36
37 this.onClick = this.onClick.bind(this);
38 }
39
40 onClick(e) {
41 if (this.props.disabled || this.context.disabled) {
42 e.preventDefault();
43 return;
44 }
45
46 if (this.props.nav && !this.props.tag) {
47 e.preventDefault();
48 }
49
50 if (this.props.onClick) {
51 this.props.onClick(e);
52 }
53
54 this.context.toggle(e);
55 }
56
57 getRole() {
58 return this.context.menuRole || this.props['aria-haspopup'];
59 }
60
61 render() {
62 const {
63 className,
64 color,
65 cssModule,
66 caret,
67 split,
68 nav,
69 tag,
70 innerRef,
71 ...props
72 } = this.props;
73 const ariaLabel = props['aria-label'] || 'Toggle Dropdown';
74 const classes = mapToCssModules(
75 classNames(className, {
76 'dropdown-toggle': caret || split,
77 'dropdown-toggle-split': split,
78 'nav-link': nav,
79 }),
80 cssModule,
81 );
82 const children =
83 typeof props.children !== 'undefined' ? (
84 props.children
85 ) : (
86 <span className="visually-hidden">{ariaLabel}</span>
87 );
88
89 let Tag;
90
91 if (nav && !tag) {
92 Tag = 'a';
93 props.href = '#';
94 } else if (!tag) {
95 Tag = Button;
96 props.color = color;
97 props.cssModule = cssModule;
98 } else {
99 Tag = tag;
100 }
101
102 // extracted the rendering of the Tag component
103 const returnFunction = ({ ref }) => {
104 const handleRef = (tagRef) => {
105 ref(tagRef);
106 const { onToggleRef } = this.context;
107 if (onToggleRef) onToggleRef(tagRef);
108 };
109
110 return (
111 <Tag
112 {...props}
113 {...{ [typeof Tag === 'string' ? 'ref' : 'innerRef']: handleRef }}
114 className={classes}
115 onClick={this.onClick}
116 aria-expanded={this.context.isOpen}
117 aria-haspopup={this.getRole()}
118 children={children}
119 />
120 );
121 };
122
123 // No Reference component if the component is in Navbar
124 if (this.context.inNavbar) {
125 return <>{returnFunction({ ref: this.context.onToggleRef })}</>;
126 }
127
128 // Normal rendering if component not in NavBar
129 return <Reference innerRef={innerRef}>{returnFunction}</Reference>;
130 }
131}
132
133DropdownToggle.propTypes = propTypes;
134DropdownToggle.defaultProps = defaultProps;
135DropdownToggle.contextType = DropdownContext;
136
137export default DropdownToggle;
138
\No newline at end of file