- 1 :
/**
- 2 :
* @file clickable-component.js
- 3 :
*/
- 4 :
import Component from './component';
- 5 :
import * as Dom from './utils/dom.js';
- 6 :
import log from './utils/log.js';
- 7 :
import keycode from 'keycode';
- 8 :
- 9 :
/**
- 10 :
* Component which is clickable or keyboard actionable, but is not a
- 11 :
* native HTML button.
- 12 :
*
- 13 :
* @extends Component
- 14 :
*/
- 15 :
class ClickableComponent extends Component {
- 16 :
- 17 :
/**
- 18 :
* Creates an instance of this class.
- 19 :
*
- 20 :
* @param { import('./player').default } player
- 21 :
* The `Player` that this class should be attached to.
- 22 :
*
- 23 :
* @param {Object} [options]
- 24 :
* The key/value store of component options.
- 25 :
*
- 26 :
* @param {function} [options.clickHandler]
- 27 :
* The function to call when the button is clicked / activated
- 28 :
*
- 29 :
* @param {string} [options.controlText]
- 30 :
* The text to set on the button
- 31 :
*
- 32 :
* @param {string} [options.className]
- 33 :
* A class or space separated list of classes to add the component
- 34 :
*
- 35 :
*/
- 36 :
constructor(player, options) {
- 37 :
- 38 :
super(player, options);
- 39 :
- 40 :
if (this.options_.controlText) {
- 41 :
this.controlText(this.options_.controlText);
- 42 :
}
- 43 :
- 44 :
this.handleMouseOver_ = (e) => this.handleMouseOver(e);
- 45 :
this.handleMouseOut_ = (e) => this.handleMouseOut(e);
- 46 :
this.handleClick_ = (e) => this.handleClick(e);
- 47 :
this.handleKeyDown_ = (e) => this.handleKeyDown(e);
- 48 :
- 49 :
this.emitTapEvents();
- 50 :
- 51 :
this.enable();
- 52 :
}
- 53 :
- 54 :
/**
- 55 :
* Create the `ClickableComponent`s DOM element.
- 56 :
*
- 57 :
* @param {string} [tag=div]
- 58 :
* The element's node type.
- 59 :
*
- 60 :
* @param {Object} [props={}]
- 61 :
* An object of properties that should be set on the element.
- 62 :
*
- 63 :
* @param {Object} [attributes={}]
- 64 :
* An object of attributes that should be set on the element.
- 65 :
*
- 66 :
* @return {Element}
- 67 :
* The element that gets created.
- 68 :
*/
- 69 :
createEl(tag = 'div', props = {}, attributes = {}) {
- 70 :
props = Object.assign({
- 71 :
className: this.buildCSSClass(),
- 72 :
tabIndex: 0
- 73 :
}, props);
- 74 :
- 75 :
if (tag === 'button') {
- 76 :
log.error(`Creating a ClickableComponent with an HTML element of ${tag} is not supported; use a Button instead.`);
- 77 :
}
- 78 :
- 79 :
// Add ARIA attributes for clickable element which is not a native HTML button
- 80 :
attributes = Object.assign({
- 81 :
role: 'button'
- 82 :
}, attributes);
- 83 :
- 84 :
this.tabIndex_ = props.tabIndex;
- 85 :
- 86 :
const el = Dom.createEl(tag, props, attributes);
- 87 :
- 88 :
el.appendChild(Dom.createEl('span', {
- 89 :
className: 'vjs-icon-placeholder'
- 90 :
}, {
- 91 :
'aria-hidden': true
- 92 :
}));
- 93 :
- 94 :
this.createControlTextEl(el);
- 95 :
- 96 :
return el;
- 97 :
}
- 98 :
- 99 :
dispose() {
- 100 :
// remove controlTextEl_ on dispose
- 101 :
this.controlTextEl_ = null;
- 102 :
- 103 :
super.dispose();
- 104 :
}
- 105 :
- 106 :
/**
- 107 :
* Create a control text element on this `ClickableComponent`
- 108 :
*
- 109 :
* @param {Element} [el]
- 110 :
* Parent element for the control text.
- 111 :
*
- 112 :
* @return {Element}
- 113 :
* The control text element that gets created.
- 114 :
*/
- 115 :
createControlTextEl(el) {
- 116 :
this.controlTextEl_ = Dom.createEl('span', {
- 117 :
className: 'vjs-control-text'
- 118 :
}, {
- 119 :
// let the screen reader user know that the text of the element may change
- 120 :
'aria-live': 'polite'
- 121 :
});
- 122 :
- 123 :
if (el) {
- 124 :
el.appendChild(this.controlTextEl_);
- 125 :
}
- 126 :
- 127 :
this.controlText(this.controlText_, el);
- 128 :
- 129 :
return this.controlTextEl_;
- 130 :
}
- 131 :
- 132 :
/**
- 133 :
* Get or set the localize text to use for the controls on the `ClickableComponent`.
- 134 :
*
- 135 :
* @param {string} [text]
- 136 :
* Control text for element.
- 137 :
*
- 138 :
* @param {Element} [el=this.el()]
- 139 :
* Element to set the title on.
- 140 :
*
- 141 :
* @return {string}
- 142 :
* - The control text when getting
- 143 :
*/
- 144 :
controlText(text, el = this.el()) {
- 145 :
if (text === undefined) {
- 146 :
return this.controlText_ || 'Need Text';
- 147 :
}
- 148 :
- 149 :
const localizedText = this.localize(text);
- 150 :
- 151 :
/** @protected */
- 152 :
this.controlText_ = text;
- 153 :
Dom.textContent(this.controlTextEl_, localizedText);
- 154 :
if (!this.nonIconControl && !this.player_.options_.noUITitleAttributes) {
- 155 :
// Set title attribute if only an icon is shown
- 156 :
el.setAttribute('title', localizedText);
- 157 :
}
- 158 :
}
- 159 :
- 160 :
/**
- 161 :
* Builds the default DOM `className`.
- 162 :
*
- 163 :
* @return {string}
- 164 :
* The DOM `className` for this object.
- 165 :
*/
- 166 :
buildCSSClass() {
- 167 :
return `vjs-control vjs-button ${super.buildCSSClass()}`;
- 168 :
}
- 169 :
- 170 :
/**
- 171 :
* Enable this `ClickableComponent`
- 172 :
*/
- 173 :
enable() {
- 174 :
if (!this.enabled_) {
- 175 :
this.enabled_ = true;
- 176 :
this.removeClass('vjs-disabled');
- 177 :
this.el_.setAttribute('aria-disabled', 'false');
- 178 :
if (typeof this.tabIndex_ !== 'undefined') {
- 179 :
this.el_.setAttribute('tabIndex', this.tabIndex_);
- 180 :
}
- 181 :
this.on(['tap', 'click'], this.handleClick_);
- 182 :
this.on('keydown', this.handleKeyDown_);
- 183 :
}
- 184 :
}
- 185 :
- 186 :
/**
- 187 :
* Disable this `ClickableComponent`
- 188 :
*/
- 189 :
disable() {
- 190 :
this.enabled_ = false;
- 191 :
this.addClass('vjs-disabled');
- 192 :
this.el_.setAttribute('aria-disabled', 'true');
- 193 :
if (typeof this.tabIndex_ !== 'undefined') {
- 194 :
this.el_.removeAttribute('tabIndex');
- 195 :
}
- 196 :
this.off('mouseover', this.handleMouseOver_);
- 197 :
this.off('mouseout', this.handleMouseOut_);
- 198 :
this.off(['tap', 'click'], this.handleClick_);
- 199 :
this.off('keydown', this.handleKeyDown_);
- 200 :
}
- 201 :
- 202 :
/**
- 203 :
* Handles language change in ClickableComponent for the player in components
- 204 :
*
- 205 :
*
- 206 :
*/
- 207 :
handleLanguagechange() {
- 208 :
this.controlText(this.controlText_);
- 209 :
}
- 210 :
- 211 :
/**
- 212 :
* Event handler that is called when a `ClickableComponent` receives a
- 213 :
* `click` or `tap` event.
- 214 :
*
- 215 :
* @param {Event} event
- 216 :
* The `tap` or `click` event that caused this function to be called.
- 217 :
*
- 218 :
* @listens tap
- 219 :
* @listens click
- 220 :
* @abstract
- 221 :
*/
- 222 :
handleClick(event) {
- 223 :
if (this.options_.clickHandler) {
- 224 :
this.options_.clickHandler.call(this, arguments);
- 225 :
}
- 226 :
}
- 227 :
- 228 :
/**
- 229 :
* Event handler that is called when a `ClickableComponent` receives a
- 230 :
* `keydown` event.
- 231 :
*
- 232 :
* By default, if the key is Space or Enter, it will trigger a `click` event.
- 233 :
*
- 234 :
* @param {Event} event
- 235 :
* The `keydown` event that caused this function to be called.
- 236 :
*
- 237 :
* @listens keydown
- 238 :
*/
- 239 :
handleKeyDown(event) {
- 240 :
- 241 :
// Support Space or Enter key operation to fire a click event. Also,
- 242 :
// prevent the event from propagating through the DOM and triggering
- 243 :
// Player hotkeys.
- 244 :
if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
- 245 :
event.preventDefault();
- 246 :
event.stopPropagation();
- 247 :
this.trigger('click');
- 248 :
} else {
- 249 :
- 250 :
// Pass keypress handling up for unsupported keys
- 251 :
super.handleKeyDown(event);
- 252 :
}
- 253 :
}
- 254 :
}
- 255 :
- 256 :
Component.registerComponent('ClickableComponent', ClickableComponent);
- 257 :
export default ClickableComponent;