]>
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
,
35 handleDocumentClick
= e
=> {
36 if (this.node
&& !this.node
.contains(e
.target
)) {
42 if (e
.key
=== 'Escape') {
44 } else if (!e
.key
|| e
.key
=== 'Enter') {
45 const value
= e
.currentTarget
.getAttribute('data-index');
50 this.props
.onChange(value
);
54 componentDidMount () {
55 document
.addEventListener('click', this.handleDocumentClick
, false);
56 document
.addEventListener('touchend', this.handleDocumentClick
, listenerOptions
);
59 componentWillUnmount () {
60 document
.removeEventListener('click', this.handleDocumentClick
, false);
61 document
.removeEventListener('touchend', this.handleDocumentClick
, listenerOptions
);
69 const { style
, items
, value
} = this.props
;
72 <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 }) }}>
73 {({ opacity
, scaleX
, scaleY
}) => (
74 <div className
='privacy-dropdown__dropdown' style
={{ ...style
, opacity: opacity
, transform: `scale(${scaleX}, ${scaleY})` }} ref
={this.setRef
}>
76 <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
})}>
77 <div className
='privacy-dropdown__option__icon'>
78 <i className
={`fa fa-fw fa-${item.icon}`} />
81 <div className
='privacy-dropdown__option__content'>
82 <strong
>{item
.text
}</strong
>
96 export default class PrivacyDropdown
extends React
.PureComponent
{
99 isUserTouching: PropTypes
.func
,
100 isModalOpen: PropTypes
.bool
.isRequired
,
101 onModalOpen: PropTypes
.func
,
102 onModalClose: PropTypes
.func
,
103 value: PropTypes
.string
.isRequired
,
104 onChange: PropTypes
.func
.isRequired
,
105 intl: PropTypes
.object
.isRequired
,
112 handleToggle
= () => {
113 if (this.props
.isUserTouching()) {
114 if (this.state
.open
) {
115 this.props
.onModalClose();
117 this.props
.onModalOpen({
118 actions: this.options
.map(option
=> ({ ...option
, active: option
.value
=== this.props
.value
})),
119 onClick: this.handleModalActionClick
,
123 this.setState({ open: !this.state
.open
});
127 handleModalActionClick
= (e
) => {
130 const { value
} = this.options
[e
.currentTarget
.getAttribute('data-index')];
132 this.props
.onModalClose();
133 this.props
.onChange(value
);
136 handleKeyDown
= e
=> {
147 handleClose
= () => {
148 this.setState({ open: false });
151 handleChange
= value
=> {
152 this.props
.onChange(value
);
155 componentWillMount () {
156 const { intl: { formatMessage
} } = this.props
;
159 { icon: 'globe', value: 'public', text: formatMessage(messages
.public_short
), meta: formatMessage(messages
.public_long
) },
160 { icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages
.unlisted_short
), meta: formatMessage(messages
.unlisted_long
) },
161 { icon: 'lock', value: 'private', text: formatMessage(messages
.private_short
), meta: formatMessage(messages
.private_long
) },
162 { icon: 'envelope', value: 'direct', text: formatMessage(messages
.direct_short
), meta: formatMessage(messages
.direct_long
) },
167 const { value
, intl
} = this.props
;
168 const { open
} = this.state
;
170 const valueOption
= this.options
.find(item
=> item
.value
=== value
);
173 <div className
={classNames('privacy-dropdown', { active: open
})} onKeyDown
={this.handleKeyDown
}>
174 <div className
={classNames('privacy-dropdown__value', { active: this.options
.indexOf(valueOption
) === 0 })}>
176 className
='privacy-dropdown__value-icon'
177 icon
={valueOption
.icon
}
178 title
={intl
.formatMessage(messages
.change_privacy
)}
183 onClick
={this.handleToggle
}
184 style
={{ height: null, lineHeight: '27px' }}
188 <Overlay show
={open
} placement
='bottom' target
={this}>
192 onClose
={this.handleClose
}
193 onChange
={this.handleChange
}
This page took 0.100887 seconds and 4 git commands to generate.