]> cat aescling's git repositories - mastodon.git/blob - app/javascript/flavours/glitch/features/composer/options/dropdown/content/index.js
Merge branch 'master' into glitch-soc/merge-upstream
[mastodon.git] / app / javascript / flavours / glitch / features / composer / options / dropdown / content / index.js
1 // Package imports.
2 import PropTypes from 'prop-types';
3 import React from 'react';
4 import spring from 'react-motion/lib/spring';
5
6 // Components.
7 import ComposerOptionsDropdownContentItem from './item';
8
9 // Utils.
10 import { withPassive } from 'flavours/glitch/util/dom_helpers';
11 import Motion from 'flavours/glitch/util/optional_motion';
12 import { assignHandlers } from 'flavours/glitch/util/react_helpers';
13
14 // Handlers.
15 const handlers = {
16 // When the document is clicked elsewhere, we close the dropdown.
17 handleDocumentClick ({ target }) {
18 const { node } = this;
19 const { onClose } = this.props;
20 if (onClose && node && !node.contains(target)) {
21 onClose();
22 }
23 },
24
25 // Stores our node in `this.node`.
26 handleRef (node) {
27 this.node = node;
28 },
29 };
30
31 // The spring to use with our motion.
32 const springMotion = spring(1, {
33 damping: 35,
34 stiffness: 400,
35 });
36
37 // The component.
38 export default class ComposerOptionsDropdownContent extends React.PureComponent {
39
40 // Constructor.
41 constructor (props) {
42 super(props);
43 assignHandlers(this, handlers);
44
45 // Instance variables.
46 this.node = null;
47
48 this.state = {
49 mounted: false,
50 };
51 }
52
53 // On mounting, we add our listeners.
54 componentDidMount () {
55 const { handleDocumentClick } = this.handlers;
56 document.addEventListener('click', handleDocumentClick, false);
57 document.addEventListener('touchend', handleDocumentClick, withPassive);
58 this.setState({ mounted: true });
59 }
60
61 // On unmounting, we remove our listeners.
62 componentWillUnmount () {
63 const { handleDocumentClick } = this.handlers;
64 document.removeEventListener('click', handleDocumentClick, false);
65 document.removeEventListener('touchend', handleDocumentClick, withPassive);
66 }
67
68 // Rendering.
69 render () {
70 const { mounted } = this.state;
71 const { handleRef } = this.handlers;
72 const {
73 items,
74 onChange,
75 onClose,
76 style,
77 value,
78 } = this.props;
79
80 // The result.
81 return (
82 <Motion
83 defaultStyle={{
84 opacity: 0,
85 scaleX: 0.85,
86 scaleY: 0.75,
87 }}
88 style={{
89 opacity: springMotion,
90 scaleX: springMotion,
91 scaleY: springMotion,
92 }}
93 >
94 {({ opacity, scaleX, scaleY }) => (
95 // It should not be transformed when mounting because the resulting
96 // size will be used to determine the coordinate of the menu by
97 // react-overlays
98 <div
99 className='composer--options--dropdown--content'
100 ref={handleRef}
101 style={{
102 ...style,
103 opacity: opacity,
104 transform: mounted ? `scale(${scaleX}, ${scaleY})` : null,
105 }}
106 >
107 {items ? items.map(
108 ({
109 name,
110 ...rest
111 }) => (
112 <ComposerOptionsDropdownContentItem
113 active={name === value}
114 key={name}
115 name={name}
116 onChange={onChange}
117 onClose={onClose}
118 options={rest}
119 />
120 )
121 ) : null}
122 </div>
123 )}
124 </Motion>
125 );
126 }
127
128 }
129
130 // Props.
131 ComposerOptionsDropdownContent.propTypes = {
132 items: PropTypes.arrayOf(PropTypes.shape({
133 icon: PropTypes.string,
134 meta: PropTypes.node,
135 name: PropTypes.string.isRequired,
136 on: PropTypes.bool,
137 text: PropTypes.node,
138 })),
139 onChange: PropTypes.func,
140 onClose: PropTypes.func,
141 style: PropTypes.object,
142 value: PropTypes.string,
143 };
144
145 // Default props.
146 ComposerOptionsDropdownContent.defaultProps = { style: {} };
This page took 0.086374 seconds and 4 git commands to generate.