]>
cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/features/compose/components/privacy_dropdown.js
1 import React
from 'react';
2 import PropTypes
from 'prop-types';
3 import { injectIntl
, defineMessages
} from 'react-intl';
4 import IconButton
from '../../../components/icon_button';
5 import Overlay
from 'react-overlays/lib/Overlay';
6 import Motion
from '../../ui/util/optional_motion';
7 import spring
from 'react-motion/lib/spring';
8 import detectPassiveEvents
from 'detect-passive-events';
9 import classNames
from 'classnames';
11 const messages
= defineMessages({
12 public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
13 public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
14 unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
15 unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
16 private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
17 private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
18 direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
19 direct_long: { id: 'privacy.direct.long', defaultMessage: 'Post to mentioned users only' },
20 change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
23 const listenerOptions
= detectPassiveEvents
.hasSupport
? { passive: true } : false;
25 class PrivacyDropdownMenu
extends React
.PureComponent
{
28 style: PropTypes
.object
,
29 items: PropTypes
.array
.isRequired
,
30 value: PropTypes
.string
.isRequired
,
31 onClose: PropTypes
.func
.isRequired
,
32 onChange: PropTypes
.func
.isRequired
,
39 handleDocumentClick
= e
=> {
40 if (this.node
&& !this.node
.contains(e
.target
)) {
46 if (e
.key
=== 'Escape') {
48 } else if (!e
.key
|| e
.key
=== 'Enter') {
49 const value
= e
.currentTarget
.getAttribute('data-index');
54 this.props
.onChange(value
);
58 componentDidMount () {
59 document
.addEventListener('click', this.handleDocumentClick
, false);
60 document
.addEventListener('touchend', this.handleDocumentClick
, listenerOptions
);
61 this.setState({ mounted: true });
64 componentWillUnmount () {
65 document
.removeEventListener('click', this.handleDocumentClick
, false);
66 document
.removeEventListener('touchend', this.handleDocumentClick
, listenerOptions
);
74 const { mounted
} = this.state
;
75 const { style
, items
, value
} = this.props
;
78 <Motion defaultStyle
={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style
={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
79 {({ opacity
, scaleX
, scaleY
}) => (
80 // It should not be transformed when mounting because the resulting
81 // size will be used to determine the coordinate of the menu by
83 <div className
='privacy-dropdown__dropdown' style
={{ ...style
, opacity: opacity
, transform: mounted
? `scale(${scaleX}, ${scaleY})` : null }} ref
={this.setRef
}>
85 <div role
='button' tabIndex
='0' key
={item
.value
} data
-index
={item
.value
} onKeyDown
={this.handleClick
} onClick
={this.handleClick
} className
={classNames('privacy-dropdown__option', { active: item
.value
=== value
})}>
86 <div className
='privacy-dropdown__option__icon'>
87 <i className
={`fa fa-fw fa-${item.icon}`} />
90 <div className
='privacy-dropdown__option__content'>
91 <strong
>{item
.text
}</strong
>
105 export default class PrivacyDropdown
extends React
.PureComponent
{
108 isUserTouching: PropTypes
.func
,
109 isModalOpen: PropTypes
.bool
.isRequired
,
110 onModalOpen: PropTypes
.func
,
111 onModalClose: PropTypes
.func
,
112 value: PropTypes
.string
.isRequired
,
113 onChange: PropTypes
.func
.isRequired
,
114 intl: PropTypes
.object
.isRequired
,
122 handleToggle
= ({ target
}) => {
123 if (this.props
.isUserTouching()) {
124 if (this.state
.open
) {
125 this.props
.onModalClose();
127 this.props
.onModalOpen({
128 actions: this.options
.map(option
=> ({ ...option
, active: option
.value
=== this.props
.value
})),
129 onClick: this.handleModalActionClick
,
133 const { top
} = target
.getBoundingClientRect();
134 this.setState({ placement: top
* 2 < innerHeight
? 'bottom' : 'top' });
135 this.setState({ open: !this.state
.open
});
139 handleModalActionClick
= (e
) => {
142 const { value
} = this.options
[e
.currentTarget
.getAttribute('data-index')];
144 this.props
.onModalClose();
145 this.props
.onChange(value
);
148 handleKeyDown
= e
=> {
151 this.handleToggle(e
);
159 handleClose
= () => {
160 this.setState({ open: false });
163 handleChange
= value
=> {
164 this.props
.onChange(value
);
167 componentWillMount () {
168 const { intl: { formatMessage
} } = this.props
;
171 { icon: 'globe', value: 'public', text: formatMessage(messages
.public_short
), meta: formatMessage(messages
.public_long
) },
172 { icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages
.unlisted_short
), meta: formatMessage(messages
.unlisted_long
) },
173 { icon: 'lock', value: 'private', text: formatMessage(messages
.private_short
), meta: formatMessage(messages
.private_long
) },
174 { icon: 'envelope', value: 'direct', text: formatMessage(messages
.direct_short
), meta: formatMessage(messages
.direct_long
) },
179 const { value
, intl
} = this.props
;
180 const { open
, placement
} = this.state
;
182 const valueOption
= this.options
.find(item
=> item
.value
=== value
);
185 <div className
={classNames('privacy-dropdown', { active: open
})} onKeyDown
={this.handleKeyDown
}>
186 <div className
={classNames('privacy-dropdown__value', { active: this.options
.indexOf(valueOption
) === 0 })}>
188 className
='privacy-dropdown__value-icon'
189 icon
={valueOption
.icon
}
190 title
={intl
.formatMessage(messages
.change_privacy
)}
195 onClick
={this.handleToggle
}
196 style
={{ height: null, lineHeight: '27px' }}
200 <Overlay show
={open
} placement
={placement
} target
={this}>
204 onClose
={this.handleClose
}
205 onChange
={this.handleChange
}
This page took 0.118264 seconds and 5 git commands to generate.