diff --git a/react/features/toolbox/components/StatelessToolbar.web.js b/react/features/toolbox/components/StatelessToolbar.web.js
new file mode 100644
index 000000000..c5fa79166
--- /dev/null
+++ b/react/features/toolbox/components/StatelessToolbar.web.js
@@ -0,0 +1,65 @@
+/* @flow */
+
+import React, { Component } from 'react';
+
+/**
+ * Implements a toolbar in React/Web. It is a strip that contains a set of
+ * toolbar items such as buttons. Toolbar is commonly placed inside of a
+ * Toolbox.
+ *
+ * @class Toolbar
+ * @extends Component
+ */
+export default class StatelessToolbar extends Component {
+ /**
+ * Base toolbar component's property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * Children of current React component.
+ */
+ children: React.PropTypes.node,
+
+ /**
+ * Toolbar's class name.
+ */
+ className: React.PropTypes.string,
+
+ /**
+ * Handler for mouse out event.
+ */
+ onMouseOut: React.PropTypes.func,
+
+ /**
+ * Handler for mouse over event.
+ */
+ onMouseOver: React.PropTypes.func
+ };
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render(): ReactElement<*> {
+ const {
+ className,
+ onMouseOut,
+ onMouseOver
+ } = this.props;
+
+ return (
+
+ {
+ this.props.children
+ }
+
+ );
+ }
+}
diff --git a/react/features/toolbox/components/StatelessToolbarButton.js b/react/features/toolbox/components/StatelessToolbarButton.js
new file mode 100644
index 000000000..ce410eda3
--- /dev/null
+++ b/react/features/toolbox/components/StatelessToolbarButton.js
@@ -0,0 +1,148 @@
+/* @flow */
+
+import React from 'react';
+
+import AbstractToolbarButton from './AbstractToolbarButton';
+
+type MapOfAttributes = { [key: string]: * };
+
+
+/* eslint-disable flowtype/space-before-type-colon */
+
+/**
+ * Takes toolbar button props and maps them to HTML attributes to set.
+ *
+ * @param {Object} props - Props set to the React component.
+ * @returns {MapOfAttributes}
+ */
+function getButtonAttributesByProps(props: Object = {})
+ : MapOfAttributes {
+ // XXX Make sure to not modify props.classNames because that'd be bad
+ // practice.
+ const classNames = (props.classNames && [ ...props.classNames ]) || [];
+
+ props.toggled && classNames.push('toggled');
+ props.unclickable && classNames.push('unclickable');
+
+ const result: MapOfAttributes = {
+ className: classNames.join(' '),
+ 'data-container': 'body',
+ 'data-placement': 'bottom',
+ id: props.id
+ };
+
+ if (!props.enabled) {
+ result.disabled = 'disabled';
+ }
+
+ if (props.hidden) {
+ result.style = { display: 'none' };
+ }
+
+ if (props.tooltipText) {
+ result.content = props.tooltipText;
+ }
+
+ return result;
+}
+
+/* eslint-enable flowtype/space-before-type-colon */
+
+/**
+ * Represents a button in Toolbar on React.
+ *
+ * @class ToolbarButton
+ * @extends AbstractToolbarButton
+ */
+export default class StatelessToolbarButton extends AbstractToolbarButton {
+ _onClick: Function;
+
+ /**
+ * Toolbar button component's property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ ...AbstractToolbarButton.propTypes,
+
+ /**
+ * Object describing button.
+ */
+ button: React.PropTypes.object.isRequired,
+
+ /**
+ * Handler for button's reference.
+ */
+ createRefToButton: React.PropTypes.func
+ };
+
+ /**
+ * Initializes new ToolbarButton instance.
+ *
+ * @param {Object} props - The read-only properties with which the new
+ * instance is to be initialized.
+ */
+ constructor(props: Object) {
+ super(props);
+
+ // Bind methods to save the context
+ this._onClick = this._onClick.bind(this);
+ }
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render(): ReactElement<*> {
+ const { button } = this.props;
+ const attributes = getButtonAttributesByProps(button);
+
+ return (
+
+ { this._renderInnerElementsIfRequired() }
+
+ );
+ }
+
+ /**
+ * Wrapper on on click handler props for current button.
+ *
+ * @param {Event} event - Click event object.
+ * @returns {void}
+ * @private
+ */
+ _onClick(event: Event): void {
+ const {
+ button,
+ onClick
+ } = this.props;
+ const {
+ enabled,
+ unclickable
+ } = button;
+
+ if (enabled && !unclickable && onClick) {
+ onClick(event);
+ }
+ }
+
+ /**
+ * If toolbar button should contain children elements
+ * renders them.
+ *
+ * @returns {ReactElement|null}
+ * @private
+ */
+ _renderInnerElementsIfRequired(): ReactElement<*> | null {
+ if (this.props.button.html) {
+ return this.props.button.html;
+ }
+
+ return null;
+ }
+}
diff --git a/react/features/toolbox/components/Toolbar.web.js b/react/features/toolbox/components/Toolbar.web.js
index 8faa15606..fa1336c81 100644
--- a/react/features/toolbox/components/Toolbar.web.js
+++ b/react/features/toolbox/components/Toolbar.web.js
@@ -4,6 +4,8 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { setToolbarHovered } from '../actions';
+
+import StatelessToolbar from './StatelessToolbar';
import ToolbarButton from './ToolbarButton';
/**
@@ -15,6 +17,8 @@ import ToolbarButton from './ToolbarButton';
* @extends Component
*/
class Toolbar extends Component {
+ _onMouseOut: Function;
+ _onMouseOver: Function;
_renderToolbarButton: Function;
/**
@@ -56,10 +60,12 @@ class Toolbar extends Component {
*
* @param {Object} props - Object containing React component properties.
*/
- constructor(props) {
+ constructor(props: Object) {
super(props);
// Bind callbacks to preverse this.
+ this._onMouseOut = this._onMouseOut.bind(this);
+ this._onMouseOver = this._onMouseOver.bind(this);
this._renderToolbarButton = this._renderToolbarButton.bind(this);
}
@@ -70,21 +76,36 @@ class Toolbar extends Component {
* @returns {ReactElement}
*/
render(): ReactElement<*> {
- const { className } = this.props;
+ const toolbarButtons = new Map();
+
+ this.props.toolbarButtons
+ .forEach((button, key) => {
+ const { onClick } = button;
+
+ toolbarButtons.set(key, {
+ ...button,
+ onClick: (...args) =>
+ onClick && onClick(this.props.dispatch, ...args)
+ });
+ });
+
+ const props = {
+ ...this.props,
+ onMouseOut: this._onMouseOut,
+ onMouseOver: this._onMouseOver,
+ toolbarButtons
+ };
return (
-
+
{
[ ...this.props.toolbarButtons.entries() ]
- .reduce(this._renderToolbarButton, [])
+ .map(this._renderToolbarButton)
}
{
this.props.children
}
-
+
);
}
@@ -109,47 +130,38 @@ class Toolbar extends Component {
}
/**
- * Renders toolbar button. Method is passed to reduce function.
+ * Renders toolbar button. Method is passed to map function.
*
- * @param {Array} acc - Toolbar buttons array.
* @param {Array} keyValuePair - Key value pair containing button and its
* key.
* @private
- * @returns {Array} Array of toolbar buttons.
+ * @returns {ReactElement} A toolbar button.
*/
- _renderToolbarButton(acc: Array<*>,
- keyValuePair: Array<*>): Array> {
+ _renderToolbarButton(
+ keyValuePair: Array<*>): ReactElement<*> {
const [ key, button ] = keyValuePair;
if (button.component) {
- acc.push(
+ return (
);
-
- return acc;
}
const { tooltipPosition } = this.props;
const { onClick, onMount, onUnmount } = button;
- const onClickHandler
- = (...args) =>
- onClick(this.props.dispatch, ...args);
-
- acc.push(
+ return (
);
-
- return acc;
}
}
diff --git a/react/features/toolbox/components/ToolbarButton.web.js b/react/features/toolbox/components/ToolbarButton.web.js
index 778d445ea..bd902bceb 100644
--- a/react/features/toolbox/components/ToolbarButton.web.js
+++ b/react/features/toolbox/components/ToolbarButton.web.js
@@ -1,6 +1,6 @@
/* @flow */
-import React from 'react';
+import React, { Component } from 'react';
import { translate } from '../../base/i18n';
@@ -9,8 +9,7 @@ import {
setTooltipText
} from '../../../../modules/UI/util/Tooltip';
-import AbstractToolbarButton from './AbstractToolbarButton';
-import { getButtonAttributesByProps } from '../functions';
+import StatelessToolbarButton from './StatelessToolbarButton';
declare var APP: Object;
@@ -20,18 +19,17 @@ declare var APP: Object;
* @class ToolbarButton
* @extends AbstractToolbarButton
*/
-class ToolbarButton extends AbstractToolbarButton {
+class ToolbarButton extends Component {
+ button: Object;
_createRefToButton: Function;
- _onClick: Function;
-
/**
* Toolbar button component's property types.
*
* @static
*/
static propTypes = {
- ...AbstractToolbarButton.propTypes,
+ ...StatelessToolbarButton.propTypes,
/**
* Object describing button.
@@ -71,7 +69,6 @@ class ToolbarButton extends AbstractToolbarButton {
// Bind methods to save the context
this._createRefToButton = this._createRefToButton.bind(this);
- this._onClick = this._onClick.bind(this);
}
/**
@@ -109,17 +106,17 @@ class ToolbarButton extends AbstractToolbarButton {
*/
render(): ReactElement<*> {
const { button } = this.props;
- const attributes = getButtonAttributesByProps(button);
const popups = button.popups || [];
+ const props = {
+ ...this.props,
+ createRefToButton: this._createRefToButton
+ };
+
return (
-
- { this._renderInnerElementsIfRequired() }
+
{ this._renderPopups(popups) }
-
+
);
}
@@ -135,28 +132,6 @@ class ToolbarButton extends AbstractToolbarButton {
this.button = element;
}
- /**
- * Wrapper on on click handler props for current button.
- *
- * @param {Event} event - Click event object.
- * @returns {void}
- * @private
- */
- _onClick(event: Event): void {
- const {
- button,
- onClick
- } = this.props;
- const {
- enabled,
- unclickable
- } = button;
-
- if (enabled && !unclickable && onClick) {
- onClick(event);
- }
- }
-
/**
* If toolbar button should contain children elements
* renders them.
diff --git a/react/features/toolbox/functions.web.js b/react/features/toolbox/functions.web.js
index 354e8a598..7c8aa66f5 100644
--- a/react/features/toolbox/functions.web.js
+++ b/react/features/toolbox/functions.web.js
@@ -3,55 +3,12 @@ import SideContainerToggler
import defaultToolbarButtons from './defaultToolbarButtons';
-type MapOfAttributes = { [key: string]: * };
-
declare var $: Function;
declare var AJS: Object;
declare var interfaceConfig: Object;
export { abstractMapStateToProps } from './functions.native';
-/* eslint-disable flowtype/space-before-type-colon */
-
-/**
- * Takes toolbar button props and maps them to HTML attributes to set.
- *
- * @param {Object} props - Props set to the React component.
- * @returns {MapOfAttributes}
- */
-export function getButtonAttributesByProps(props: Object = {})
- : MapOfAttributes {
- // XXX Make sure to not modify props.classNames because that'd be bad
- // practice.
- const classNames = (props.classNames && [ ...props.classNames ]) || [];
-
- props.toggled && classNames.push('toggled');
- props.unclickable && classNames.push('unclickable');
-
- const result: MapOfAttributes = {
- className: classNames.join(' '),
- 'data-container': 'body',
- 'data-placement': 'bottom',
- id: props.id
- };
-
- if (!props.enabled) {
- result.disabled = 'disabled';
- }
-
- if (props.hidden) {
- result.style = { display: 'none' };
- }
-
- if (props.tooltipText) {
- result.content = props.tooltipText;
- }
-
- return result;
-}
-
-/* eslint-enable flowtype/space-before-type-colon */
-
/**
* Returns an object which contains the default buttons for the primary and
* secondary toolbars.
diff --git a/react/features/toolbox/index.js b/react/features/toolbox/index.js
index a29aa08e0..44000a1a7 100644
--- a/react/features/toolbox/index.js
+++ b/react/features/toolbox/index.js
@@ -1,6 +1,7 @@
export * from './actions';
export * from './actionTypes';
export * from './components';
+export * from './functions';
import './middleware';
import './reducer';