Kaynağa Gözat

ref(toolbar): Implement stateless toolbar

j8
hristoterezov 8 yıl önce
ebeveyn
işleme
b81dc4e59b

+ 65
- 0
react/features/toolbox/components/StatelessToolbar.web.js Dosyayı Görüntüle

@@ -0,0 +1,65 @@
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 Dosyayı Görüntüle

@@ -0,0 +1,148 @@
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 Dosyayı Görüntüle

@@ -4,6 +4,8 @@ import React, { Component } from 'react';
4 4
 import { connect } from 'react-redux';
5 5
 
6 6
 import { setToolbarHovered } from '../actions';
7
+
8
+import StatelessToolbar from './StatelessToolbar';
7 9
 import ToolbarButton from './ToolbarButton';
8 10
 
9 11
 /**
@@ -15,6 +17,8 @@ import ToolbarButton from './ToolbarButton';
15 17
  * @extends Component
16 18
  */
17 19
 class Toolbar extends Component {
20
+    _onMouseOut: Function;
21
+    _onMouseOver: Function;
18 22
     _renderToolbarButton: Function;
19 23
 
20 24
     /**
@@ -56,10 +60,12 @@ class Toolbar extends Component {
56 60
      *
57 61
      * @param {Object} props - Object containing React component properties.
58 62
      */
59
-    constructor(props) {
63
+    constructor(props: Object) {
60 64
         super(props);
61 65
 
62 66
         // Bind callbacks to preverse this.
67
+        this._onMouseOut = this._onMouseOut.bind(this);
68
+        this._onMouseOver = this._onMouseOver.bind(this);
63 69
         this._renderToolbarButton = this._renderToolbarButton.bind(this);
64 70
     }
65 71
 
@@ -70,21 +76,36 @@ class Toolbar extends Component {
70 76
      * @returns {ReactElement}
71 77
      */
72 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 99
         return (
76
-            <div
77
-                className = { `toolbar ${className}` }
78
-                onMouseOut = { this._onMouseOut }
79
-                onMouseOver = { this._onMouseOver }>
100
+            <StatelessToolbar { ...props }>
80 101
                 {
81 102
                     [ ...this.props.toolbarButtons.entries() ]
82
-                    .reduce(this._renderToolbarButton, [])
103
+                    .map(this._renderToolbarButton)
83 104
                 }
84 105
                 {
85 106
                     this.props.children
86 107
                 }
87
-            </div>
108
+            </StatelessToolbar>
88 109
         );
89 110
     }
90 111
 
@@ -109,47 +130,38 @@ class Toolbar extends Component {
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 135
      * @param {Array} keyValuePair - Key value pair containing button and its
116 136
      * key.
117 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 142
         const [ key, button ] = keyValuePair;
123 143
 
124 144
         if (button.component) {
125
-            acc.push(
145
+            return (
126 146
                 <button.component
127 147
                     key = { key }
128 148
                     tooltipPosition = { this.props.tooltipPosition } />
129 149
             );
130
-
131
-            return acc;
132 150
         }
133 151
 
134 152
         const { tooltipPosition } = this.props;
135 153
 
136 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 157
             <ToolbarButton
144 158
                 button = { button }
145 159
                 key = { key }
146
-                onClick = { onClickHandler }
160
+                onClick = { onClick }
147 161
                 onMount = { onMount }
148 162
                 onUnmount = { onUnmount }
149 163
                 tooltipPosition = { tooltipPosition } />
150 164
         );
151
-
152
-        return acc;
153 165
     }
154 166
 }
155 167
 

+ 12
- 37
react/features/toolbox/components/ToolbarButton.web.js Dosyayı Görüntüle

@@ -1,6 +1,6 @@
1 1
 /* @flow */
2 2
 
3
-import React from 'react';
3
+import React, { Component } from 'react';
4 4
 
5 5
 import { translate } from '../../base/i18n';
6 6
 
@@ -9,8 +9,7 @@ import {
9 9
     setTooltipText
10 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 14
 declare var APP: Object;
16 15
 
@@ -20,18 +19,17 @@ declare var APP: Object;
20 19
  * @class ToolbarButton
21 20
  * @extends AbstractToolbarButton
22 21
  */
23
-class ToolbarButton extends AbstractToolbarButton {
22
+class ToolbarButton extends Component {
23
+    button: Object;
24 24
     _createRefToButton: Function;
25 25
 
26
-    _onClick: Function;
27
-
28 26
     /**
29 27
      * Toolbar button component's property types.
30 28
      *
31 29
      * @static
32 30
      */
33 31
     static propTypes = {
34
-        ...AbstractToolbarButton.propTypes,
32
+        ...StatelessToolbarButton.propTypes,
35 33
 
36 34
         /**
37 35
          * Object describing button.
@@ -71,7 +69,6 @@ class ToolbarButton extends AbstractToolbarButton {
71 69
 
72 70
         // Bind methods to save the context
73 71
         this._createRefToButton = this._createRefToButton.bind(this);
74
-        this._onClick = this._onClick.bind(this);
75 72
     }
76 73
 
77 74
     /**
@@ -109,17 +106,17 @@ class ToolbarButton extends AbstractToolbarButton {
109 106
      */
110 107
     render(): ReactElement<*> {
111 108
         const { button } = this.props;
112
-        const attributes = getButtonAttributesByProps(button);
113 109
         const popups = button.popups || [];
114 110
 
111
+        const props = {
112
+            ...this.props,
113
+            createRefToButton: this._createRefToButton
114
+        };
115
+
115 116
         return (
116
-            <a
117
-                { ...attributes }
118
-                onClick = { this._onClick }
119
-                ref = { this._createRefToButton }>
120
-                { this._renderInnerElementsIfRequired() }
117
+            <StatelessToolbarButton { ...props }>
121 118
                 { this._renderPopups(popups) }
122
-            </a>
119
+            </StatelessToolbarButton>
123 120
         );
124 121
     }
125 122
 
@@ -135,28 +132,6 @@ class ToolbarButton extends AbstractToolbarButton {
135 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 136
      * If toolbar button should contain children elements
162 137
      * renders them.

+ 0
- 43
react/features/toolbox/functions.web.js Dosyayı Görüntüle

@@ -3,55 +3,12 @@ import SideContainerToggler
3 3
 
4 4
 import defaultToolbarButtons from './defaultToolbarButtons';
5 5
 
6
-type MapOfAttributes = { [key: string]: * };
7
-
8 6
 declare var $: Function;
9 7
 declare var AJS: Object;
10 8
 declare var interfaceConfig: Object;
11 9
 
12 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 13
  * Returns an object which contains the default buttons for the primary and
57 14
  * secondary toolbars.

+ 1
- 0
react/features/toolbox/index.js Dosyayı Görüntüle

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

Loading…
İptal
Kaydet