@@ -45,6 +45,7 @@ const linkPropKeys = keys(linkProps)
45
45
46
46
export const props = assign ( linkProps , btnProps )
47
47
48
+ // Focus handler for toggle buttons. Needs class of 'focus' when focused.
48
49
function handleFocus ( evt ) {
49
50
if ( evt . type === 'focusin' ) {
50
51
addClass ( evt . target , 'focus' )
@@ -53,19 +54,99 @@ function handleFocus (evt) {
53
54
}
54
55
}
55
56
57
+ // Helper functons to minimize runtime memory footprint when lots of buttons on page
58
+
59
+ // Is the requested button a link?
60
+ function isLink ( props ) {
61
+ // If tag prop is set to `a`, we use a b-link to get proper disabled handling
62
+ return Boolean ( props . href || props . to || ( props . tag && String ( props . tag ) . toLowerCase ( ) === 'a' ) )
63
+ }
64
+
65
+ // Is the button to be a toggle button?
66
+ function isToggle ( props ) {
67
+ return typeof props . pressed === 'boolean'
68
+ }
69
+
70
+ // Is the button "really" a button?
71
+ function isButton ( props ) {
72
+ if ( isLink ( props ) ) {
73
+ return false
74
+ } else if ( props . tag && String ( props . tag ) . toLowerCase ( ) !== 'button' ) {
75
+ return false
76
+ }
77
+ return true
78
+ }
79
+
80
+ // Is the requested tag not a button or link?
81
+ function isNonStandardTag ( props ) {
82
+ return ! isLink ( props ) && ! isButton ( props )
83
+ }
84
+
85
+ // Compute required classes (non static classes)
86
+ function computeClass ( props ) {
87
+ return [
88
+ props . variant ? `btn-${ props . variant } ` : `btn-secondary` ,
89
+ {
90
+ [ `btn-${ props . size } ` ] : Boolean ( props . size ) ,
91
+ 'btn-block' : props . block ,
92
+ disabled : props . disabled ,
93
+ active : props . pressed
94
+ }
95
+ ]
96
+ }
97
+
98
+ // Compute the link props to pass to b-link (if required)
99
+ function computeLinkProps ( props ) {
100
+ return isLink ( props ) ? pluckProps ( linkPropKeys , props ) : null
101
+ }
102
+
103
+ // Compute the attributes for a button
104
+ function computeAttrs ( props , data ) {
105
+ const button = isButton ( props )
106
+ const link = isLink ( props )
107
+ const toggle = isToggle ( props )
108
+ const nonStdTag = isNonStandardTag ( props )
109
+ const role = data . attrs && data . attrs [ 'role' ] ? data . attrs [ 'role' ] : null
110
+ let tabindex = data . attrs ? data . attrs [ 'tabindex' ] : null
111
+ if ( nonStdTag ) {
112
+ tabindex = '0'
113
+ }
114
+ return {
115
+ // Type only used for "real" buttons
116
+ type : ( button && ! link ) ? props . type : null ,
117
+ // Disabled only set on "real" buttons
118
+ disabled : button ? props . disabled : null ,
119
+ // We add a role of button when the tag is not a link or button for ARIA.
120
+ // Don't bork any role provided in data.attrs when isLink or isButton
121
+ role : nonStdTag ? 'button' : role ,
122
+ // We set the aria-disabled state for non-standard tags
123
+ 'aria-disabled' : nonStdTag ? String ( props . disabled ) : null ,
124
+ // For toggles, we need to set the pressed state for ARIA
125
+ 'aria-pressed' : toggle ? String ( props . pressed ) : null ,
126
+ // autocomplete off is needed in toggle mode to prevent some browsers from
127
+ // remembering the previous setting when using the back button.
128
+ autocomplete : toggle ? 'off' : null ,
129
+ // Tab index is used when the component is not a button.
130
+ // Links are tabable, but don't allow disabled, while non buttons or links
131
+ // are not tabable, so we mimic that functionality by disabling tabbing
132
+ // when disabled, and adding a tabindex of '0' to non buttons or non links.
133
+ tabindex : props . disabled && ! button ? '-1' : tabindex
134
+ }
135
+ }
136
+
56
137
export default {
57
138
functional : true ,
58
139
props,
59
140
render ( h , { props, data, listeners, children } ) {
60
- const isLink = Boolean ( props . href || props . to )
61
- const isToggle = typeof props . pressed === 'boolean'
62
- const isButtonTag = props . tag === 'button'
141
+ const toggle = isToggle ( props )
142
+ const link = isLink ( props )
63
143
const on = {
64
144
click ( e ) {
65
145
if ( props . disabled && e instanceof Event ) {
66
146
e . stopPropagation ( )
67
147
e . preventDefault ( )
68
- } else if ( isToggle ) {
148
+ } else if ( toggle && listeners && listeners [ 'update:pressed' ] ) {
149
+ // Send .sync updates to any "pressed" prop (if .sync listeners)
69
150
// Concat will normalize the value to an array
70
151
// without double wrapping an array value in an array.
71
152
concat ( listeners [ 'update:pressed' ] ) . forEach ( fn => {
@@ -77,41 +158,19 @@ export default {
77
158
}
78
159
}
79
160
80
- if ( isToggle ) {
161
+ if ( toggle ) {
81
162
on . focusin = handleFocus
82
163
on . focusout = handleFocus
83
164
}
84
165
85
166
const componentData = {
86
167
staticClass : 'btn' ,
87
- class : [
88
- props . variant ? `btn-${ props . variant } ` : `btn-secondary` ,
89
- {
90
- [ `btn-${ props . size } ` ] : Boolean ( props . size ) ,
91
- 'btn-block' : props . block ,
92
- disabled : props . disabled ,
93
- active : props . pressed
94
- }
95
- ] ,
96
- props : isLink ? pluckProps ( linkPropKeys , props ) : null ,
97
- attrs : {
98
- type : isButtonTag && ! isLink ? props . type : null ,
99
- disabled : isButtonTag && ! isLink ? props . disabled : null ,
100
- // Data attribute not used for js logic,
101
- // but only for BS4 style selectors.
102
- 'data-toggle' : isToggle ? 'button' : null ,
103
- 'aria-pressed' : isToggle ? String ( props . pressed ) : null ,
104
- // Tab index is used when the component becomes a link.
105
- // Links are tabable, but don't allow disabled,
106
- // so we mimic that functionality by disabling tabbing.
107
- tabindex :
108
- props . disabled && isLink
109
- ? '-1'
110
- : data . attrs ? data . attrs [ 'tabindex' ] : null
111
- } ,
168
+ class : computeClass ( props ) ,
169
+ props : computeLinkProps ( props ) ,
170
+ attrs : computeAttrs ( props , data ) ,
112
171
on
113
172
}
114
173
115
- return h ( isLink ? Link : props . tag , mergeData ( data , componentData ) , children )
174
+ return h ( link ? Link : props . tag , mergeData ( data , componentData ) , children )
116
175
}
117
176
}
0 commit comments