浏览代码

ref(toolbar): Implement stateless toolbar

master
hristoterezov 8 年前
父节点
当前提交
b81dc4e59b

+ 65
- 0
react/features/toolbox/components/StatelessToolbar.web.js 查看文件

1
+/* @flow */
2
+
3
+import React, { Component } from 'react';
4
+
5
+/**
6
+ * Implements a toolbar in React/Web. It is a strip that contains a set of
7
+ * toolbar items such as buttons. Toolbar is commonly placed inside of a
8
+ * Toolbox.
9
+ *
10
+ * @class Toolbar
11
+ * @extends Component
12
+ */
13
+export default class StatelessToolbar extends Component {
14
+    /**
15
+     * Base toolbar component's property types.
16
+     *
17
+     * @static
18
+     */
19
+    static propTypes = {
20
+        /**
21
+         * Children of current React component.
22
+         */
23
+        children: React.PropTypes.node,
24
+
25
+        /**
26
+         * Toolbar's class name.
27
+         */
28
+        className: React.PropTypes.string,
29
+
30
+        /**
31
+         *  Handler for mouse out event.
32
+         */
33
+        onMouseOut: React.PropTypes.func,
34
+
35
+        /**
36
+         * Handler for mouse over event.
37
+         */
38
+        onMouseOver: React.PropTypes.func
39
+    };
40
+
41
+    /**
42
+     * Implements React's {@link Component#render()}.
43
+     *
44
+     * @inheritdoc
45
+     * @returns {ReactElement}
46
+     */
47
+    render(): ReactElement<*> {
48
+        const {
49
+            className,
50
+            onMouseOut,
51
+            onMouseOver
52
+        } = this.props;
53
+
54
+        return (
55
+            <div
56
+                className = { `toolbar ${className}` }
57
+                onMouseOut = { onMouseOut }
58
+                onMouseOver = { onMouseOver }>
59
+                {
60
+                    this.props.children
61
+                }
62
+            </div>
63
+        );
64
+    }
65
+}

+ 148
- 0
react/features/toolbox/components/StatelessToolbarButton.js 查看文件

1
+/* @flow */
2
+
3
+import React from 'react';
4
+
5
+import AbstractToolbarButton from './AbstractToolbarButton';
6
+
7
+type MapOfAttributes = { [key: string]: * };
8
+
9
+
10
+/* eslint-disable flowtype/space-before-type-colon */
11
+
12
+/**
13
+ * Takes toolbar button props and maps them to HTML attributes to set.
14
+ *
15
+ * @param {Object} props - Props set to the React component.
16
+ * @returns {MapOfAttributes}
17
+ */
18
+function getButtonAttributesByProps(props: Object = {})
19
+        : MapOfAttributes {
20
+    // XXX Make sure to not modify props.classNames because that'd be bad
21
+    // practice.
22
+    const classNames = (props.classNames && [ ...props.classNames ]) || [];
23
+
24
+    props.toggled && classNames.push('toggled');
25
+    props.unclickable && classNames.push('unclickable');
26
+
27
+    const result: MapOfAttributes = {
28
+        className: classNames.join(' '),
29
+        'data-container': 'body',
30
+        'data-placement': 'bottom',
31
+        id: props.id
32
+    };
33
+
34
+    if (!props.enabled) {
35
+        result.disabled = 'disabled';
36
+    }
37
+
38
+    if (props.hidden) {
39
+        result.style = { display: 'none' };
40
+    }
41
+
42
+    if (props.tooltipText) {
43
+        result.content = props.tooltipText;
44
+    }
45
+
46
+    return result;
47
+}
48
+
49
+/* eslint-enable flowtype/space-before-type-colon */
50
+
51
+/**
52
+ * Represents a button in Toolbar on React.
53
+ *
54
+ * @class ToolbarButton
55
+ * @extends AbstractToolbarButton
56
+ */
57
+export default class StatelessToolbarButton extends AbstractToolbarButton {
58
+    _onClick: Function;
59
+
60
+    /**
61
+     * Toolbar button component's property types.
62
+     *
63
+     * @static
64
+     */
65
+    static propTypes = {
66
+        ...AbstractToolbarButton.propTypes,
67
+
68
+        /**
69
+         * Object describing button.
70
+         */
71
+        button: React.PropTypes.object.isRequired,
72
+
73
+        /**
74
+         * Handler for button's reference.
75
+         */
76
+        createRefToButton: React.PropTypes.func
77
+    };
78
+
79
+    /**
80
+     * Initializes new ToolbarButton instance.
81
+     *
82
+     * @param {Object} props - The read-only properties with which the new
83
+     * instance is to be initialized.
84
+     */
85
+    constructor(props: Object) {
86
+        super(props);
87
+
88
+        // Bind methods to save the context
89
+        this._onClick = this._onClick.bind(this);
90
+    }
91
+
92
+    /**
93
+     * Implements React's {@link Component#render()}.
94
+     *
95
+     * @inheritdoc
96
+     * @returns {ReactElement}
97
+     */
98
+    render(): ReactElement<*> {
99
+        const { button } = this.props;
100
+        const attributes = getButtonAttributesByProps(button);
101
+
102
+        return (
103
+            <a
104
+                { ...attributes }
105
+                onClick = { this._onClick }
106
+                ref = { this.props.createRefToButton }>
107
+                { this._renderInnerElementsIfRequired() }
108
+            </a>
109
+        );
110
+    }
111
+
112
+    /**
113
+     * Wrapper on on click handler props for current button.
114
+     *
115
+     * @param {Event} event - Click event object.
116
+     * @returns {void}
117
+     * @private
118
+     */
119
+    _onClick(event: Event): void {
120
+        const {
121
+            button,
122
+            onClick
123
+        } = this.props;
124
+        const {
125
+            enabled,
126
+            unclickable
127
+        } = button;
128
+
129
+        if (enabled && !unclickable && onClick) {
130
+            onClick(event);
131
+        }
132
+    }
133
+
134
+    /**
135
+     * If toolbar button should contain children elements
136
+     * renders them.
137
+     *
138
+     * @returns {ReactElement|null}
139
+     * @private
140
+     */
141
+    _renderInnerElementsIfRequired(): ReactElement<*> | null {
142
+        if (this.props.button.html) {
143
+            return this.props.button.html;
144
+        }
145
+
146
+        return null;
147
+    }
148
+}

+ 36
- 24
react/features/toolbox/components/Toolbar.web.js 查看文件

4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
 import { setToolbarHovered } from '../actions';
6
 import { setToolbarHovered } from '../actions';
7
+
8
+import StatelessToolbar from './StatelessToolbar';
7
 import ToolbarButton from './ToolbarButton';
9
 import ToolbarButton from './ToolbarButton';
8
 
10
 
9
 /**
11
 /**
15
  * @extends Component
17
  * @extends Component
16
  */
18
  */
17
 class Toolbar extends Component {
19
 class Toolbar extends Component {
20
+    _onMouseOut: Function;
21
+    _onMouseOver: Function;
18
     _renderToolbarButton: Function;
22
     _renderToolbarButton: Function;
19
 
23
 
20
     /**
24
     /**
56
      *
60
      *
57
      * @param {Object} props - Object containing React component properties.
61
      * @param {Object} props - Object containing React component properties.
58
      */
62
      */
59
-    constructor(props) {
63
+    constructor(props: Object) {
60
         super(props);
64
         super(props);
61
 
65
 
62
         // Bind callbacks to preverse this.
66
         // Bind callbacks to preverse this.
67
+        this._onMouseOut = this._onMouseOut.bind(this);
68
+        this._onMouseOver = this._onMouseOver.bind(this);
63
         this._renderToolbarButton = this._renderToolbarButton.bind(this);
69
         this._renderToolbarButton = this._renderToolbarButton.bind(this);
64
     }
70
     }
65
 
71
 
70
      * @returns {ReactElement}
76
      * @returns {ReactElement}
71
      */
77
      */
72
     render(): ReactElement<*> {
78
     render(): ReactElement<*> {
73
-        const { className } = this.props;
79
+        const toolbarButtons = new Map();
80
+
81
+        this.props.toolbarButtons
82
+            .forEach((button, key) => {
83
+                const { onClick } = button;
84
+
85
+                toolbarButtons.set(key, {
86
+                    ...button,
87
+                    onClick: (...args) =>
88
+                        onClick && onClick(this.props.dispatch, ...args)
89
+                });
90
+            });
91
+
92
+        const props = {
93
+            ...this.props,
94
+            onMouseOut: this._onMouseOut,
95
+            onMouseOver: this._onMouseOver,
96
+            toolbarButtons
97
+        };
74
 
98
 
75
         return (
99
         return (
76
-            <div
77
-                className = { `toolbar ${className}` }
78
-                onMouseOut = { this._onMouseOut }
79
-                onMouseOver = { this._onMouseOver }>
100
+            <StatelessToolbar { ...props }>
80
                 {
101
                 {
81
                     [ ...this.props.toolbarButtons.entries() ]
102
                     [ ...this.props.toolbarButtons.entries() ]
82
-                    .reduce(this._renderToolbarButton, [])
103
+                    .map(this._renderToolbarButton)
83
                 }
104
                 }
84
                 {
105
                 {
85
                     this.props.children
106
                     this.props.children
86
                 }
107
                 }
87
-            </div>
108
+            </StatelessToolbar>
88
         );
109
         );
89
     }
110
     }
90
 
111
 
109
     }
130
     }
110
 
131
 
111
     /**
132
     /**
112
-     * Renders toolbar button. Method is passed to reduce function.
133
+     * Renders toolbar button. Method is passed to map function.
113
      *
134
      *
114
-     * @param {Array} acc - Toolbar buttons array.
115
      * @param {Array} keyValuePair - Key value pair containing button and its
135
      * @param {Array} keyValuePair - Key value pair containing button and its
116
      * key.
136
      * key.
117
      * @private
137
      * @private
118
-     * @returns {Array} Array of toolbar buttons.
138
+     * @returns {ReactElement} A toolbar button.
119
      */
139
      */
120
-    _renderToolbarButton(acc: Array<*>,
121
-                         keyValuePair: Array<*>): Array<ReactElement<*>> {
140
+    _renderToolbarButton(
141
+                         keyValuePair: Array<*>): ReactElement<*> {
122
         const [ key, button ] = keyValuePair;
142
         const [ key, button ] = keyValuePair;
123
 
143
 
124
         if (button.component) {
144
         if (button.component) {
125
-            acc.push(
145
+            return (
126
                 <button.component
146
                 <button.component
127
                     key = { key }
147
                     key = { key }
128
                     tooltipPosition = { this.props.tooltipPosition } />
148
                     tooltipPosition = { this.props.tooltipPosition } />
129
             );
149
             );
130
-
131
-            return acc;
132
         }
150
         }
133
 
151
 
134
         const { tooltipPosition } = this.props;
152
         const { tooltipPosition } = this.props;
135
 
153
 
136
         const { onClick, onMount, onUnmount } = button;
154
         const { onClick, onMount, onUnmount } = button;
137
 
155
 
138
-        const onClickHandler
139
-            = (...args) =>
140
-                onClick(this.props.dispatch, ...args);
141
-
142
-        acc.push(
156
+        return (
143
             <ToolbarButton
157
             <ToolbarButton
144
                 button = { button }
158
                 button = { button }
145
                 key = { key }
159
                 key = { key }
146
-                onClick = { onClickHandler }
160
+                onClick = { onClick }
147
                 onMount = { onMount }
161
                 onMount = { onMount }
148
                 onUnmount = { onUnmount }
162
                 onUnmount = { onUnmount }
149
                 tooltipPosition = { tooltipPosition } />
163
                 tooltipPosition = { tooltipPosition } />
150
         );
164
         );
151
-
152
-        return acc;
153
     }
165
     }
154
 }
166
 }
155
 
167
 

+ 12
- 37
react/features/toolbox/components/ToolbarButton.web.js 查看文件

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
-import React from 'react';
3
+import React, { Component } from 'react';
4
 
4
 
5
 import { translate } from '../../base/i18n';
5
 import { translate } from '../../base/i18n';
6
 
6
 
9
     setTooltipText
9
     setTooltipText
10
 } from '../../../../modules/UI/util/Tooltip';
10
 } from '../../../../modules/UI/util/Tooltip';
11
 
11
 
12
-import AbstractToolbarButton from './AbstractToolbarButton';
13
-import { getButtonAttributesByProps } from '../functions';
12
+import StatelessToolbarButton from './StatelessToolbarButton';
14
 
13
 
15
 declare var APP: Object;
14
 declare var APP: Object;
16
 
15
 
20
  * @class ToolbarButton
19
  * @class ToolbarButton
21
  * @extends AbstractToolbarButton
20
  * @extends AbstractToolbarButton
22
  */
21
  */
23
-class ToolbarButton extends AbstractToolbarButton {
22
+class ToolbarButton extends Component {
23
+    button: Object;
24
     _createRefToButton: Function;
24
     _createRefToButton: Function;
25
 
25
 
26
-    _onClick: Function;
27
-
28
     /**
26
     /**
29
      * Toolbar button component's property types.
27
      * Toolbar button component's property types.
30
      *
28
      *
31
      * @static
29
      * @static
32
      */
30
      */
33
     static propTypes = {
31
     static propTypes = {
34
-        ...AbstractToolbarButton.propTypes,
32
+        ...StatelessToolbarButton.propTypes,
35
 
33
 
36
         /**
34
         /**
37
          * Object describing button.
35
          * Object describing button.
71
 
69
 
72
         // Bind methods to save the context
70
         // Bind methods to save the context
73
         this._createRefToButton = this._createRefToButton.bind(this);
71
         this._createRefToButton = this._createRefToButton.bind(this);
74
-        this._onClick = this._onClick.bind(this);
75
     }
72
     }
76
 
73
 
77
     /**
74
     /**
109
      */
106
      */
110
     render(): ReactElement<*> {
107
     render(): ReactElement<*> {
111
         const { button } = this.props;
108
         const { button } = this.props;
112
-        const attributes = getButtonAttributesByProps(button);
113
         const popups = button.popups || [];
109
         const popups = button.popups || [];
114
 
110
 
111
+        const props = {
112
+            ...this.props,
113
+            createRefToButton: this._createRefToButton
114
+        };
115
+
115
         return (
116
         return (
116
-            <a
117
-                { ...attributes }
118
-                onClick = { this._onClick }
119
-                ref = { this._createRefToButton }>
120
-                { this._renderInnerElementsIfRequired() }
117
+            <StatelessToolbarButton { ...props }>
121
                 { this._renderPopups(popups) }
118
                 { this._renderPopups(popups) }
122
-            </a>
119
+            </StatelessToolbarButton>
123
         );
120
         );
124
     }
121
     }
125
 
122
 
135
         this.button = element;
132
         this.button = element;
136
     }
133
     }
137
 
134
 
138
-    /**
139
-     * Wrapper on on click handler props for current button.
140
-     *
141
-     * @param {Event} event - Click event object.
142
-     * @returns {void}
143
-     * @private
144
-     */
145
-    _onClick(event: Event): void {
146
-        const {
147
-            button,
148
-            onClick
149
-        } = this.props;
150
-        const {
151
-            enabled,
152
-            unclickable
153
-        } = button;
154
-
155
-        if (enabled && !unclickable && onClick) {
156
-            onClick(event);
157
-        }
158
-    }
159
-
160
     /**
135
     /**
161
      * If toolbar button should contain children elements
136
      * If toolbar button should contain children elements
162
      * renders them.
137
      * renders them.

+ 0
- 43
react/features/toolbox/functions.web.js 查看文件

3
 
3
 
4
 import defaultToolbarButtons from './defaultToolbarButtons';
4
 import defaultToolbarButtons from './defaultToolbarButtons';
5
 
5
 
6
-type MapOfAttributes = { [key: string]: * };
7
-
8
 declare var $: Function;
6
 declare var $: Function;
9
 declare var AJS: Object;
7
 declare var AJS: Object;
10
 declare var interfaceConfig: Object;
8
 declare var interfaceConfig: Object;
11
 
9
 
12
 export { abstractMapStateToProps } from './functions.native';
10
 export { abstractMapStateToProps } from './functions.native';
13
 
11
 
14
-/* eslint-disable flowtype/space-before-type-colon */
15
-
16
-/**
17
- * Takes toolbar button props and maps them to HTML attributes to set.
18
- *
19
- * @param {Object} props - Props set to the React component.
20
- * @returns {MapOfAttributes}
21
- */
22
-export function getButtonAttributesByProps(props: Object = {})
23
-        : MapOfAttributes {
24
-    // XXX Make sure to not modify props.classNames because that'd be bad
25
-    // practice.
26
-    const classNames = (props.classNames && [ ...props.classNames ]) || [];
27
-
28
-    props.toggled && classNames.push('toggled');
29
-    props.unclickable && classNames.push('unclickable');
30
-
31
-    const result: MapOfAttributes = {
32
-        className: classNames.join(' '),
33
-        'data-container': 'body',
34
-        'data-placement': 'bottom',
35
-        id: props.id
36
-    };
37
-
38
-    if (!props.enabled) {
39
-        result.disabled = 'disabled';
40
-    }
41
-
42
-    if (props.hidden) {
43
-        result.style = { display: 'none' };
44
-    }
45
-
46
-    if (props.tooltipText) {
47
-        result.content = props.tooltipText;
48
-    }
49
-
50
-    return result;
51
-}
52
-
53
-/* eslint-enable flowtype/space-before-type-colon */
54
-
55
 /**
12
 /**
56
  * Returns an object which contains the default buttons for the primary and
13
  * Returns an object which contains the default buttons for the primary and
57
  * secondary toolbars.
14
  * secondary toolbars.

+ 1
- 0
react/features/toolbox/index.js 查看文件

1
 export * from './actions';
1
 export * from './actions';
2
 export * from './actionTypes';
2
 export * from './actionTypes';
3
 export * from './components';
3
 export * from './components';
4
+export * from './functions';
4
 
5
 
5
 import './middleware';
6
 import './middleware';
6
 import './reducer';
7
 import './reducer';

正在加载...
取消
保存