import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, defineMessages } from 'react-intl'; import IconButton from '../../../components/icon_button'; import Overlay from 'react-overlays/lib/Overlay'; import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import detectPassiveEvents from 'detect-passive-events'; import classNames from 'classnames'; const messages = defineMessages({ federate_short: { id: 'federation.federated.short', defaultMessage: 'Federated' }, federate_long: { id: 'federation.federated.long', defaultMessage: 'Allow toot to reach other instances' }, local_only_short: { id: 'federation.local_only.short', defaultMessage: 'Local-only' }, local_only_long: { id: 'federation.local_only.long', defaultMessage: 'Restrict this toot only to my instance' }, change_federation: { id: 'federation.change', defaultMessage: 'Adjust status federation' }, }); const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; class FederationDropdownMenu extends React.PureComponent { static propTypes = { style: PropTypes.object, items: PropTypes.array.isRequired, value: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired, }; state = { mounted: false, }; handleDocumentClick = e => { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); } } handleKeyDown = e => { const { items } = this.props; const value = Boolean(e.currentTarget.getAttribute('data-index')); const index = items.findIndex(item => { return (item.value === value); }); let element; switch(e.key) { case 'Escape': this.props.onClose(); break; case 'Enter': this.handleClick(e); break; case 'ArrowDown': element = this.node.childNodes[index + 1]; if (element) { element.focus(); this.props.onChange(Boolean(element.getAttribute('data-index'))); } break; case 'ArrowUp': element = this.node.childNodes[index - 1]; if (element) { element.focus(); this.props.onChange(Boolean(element.getAttribute('data-index'))); } break; case 'Home': element = this.node.firstChild; if (element) { element.focus(); this.props.onChange(Boolean(element.getAttribute('data-index'))); } break; case 'End': element = this.node.lastChild; if (element) { element.focus(); this.props.onChange(Boolean(element.getAttribute('data-index'))); } break; } } handleClick = e => { const value = Boolean(e.currentTarget.getAttribute('data-index')); e.preventDefault(); this.props.onClose(); this.props.onChange(value); } componentDidMount () { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); if (this.focusedItem) this.focusedItem.focus(); this.setState({ mounted: true }); } componentWillUnmount () { document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); } setRef = c => { this.node = c; } setFocusRef = c => { this.focusedItem = c; } render () { const { mounted } = this.state; const { style, items, value } = this.props; return ( {({ opacity, scaleX, scaleY }) => ( // It should not be transformed when mounting because the resulting // size will be used to determine the coordinate of the menu by // react-overlays
{items.map(item => (
{item.text} {item.meta}
))}
)}
); } } @injectIntl export default class FederationDropdown extends React.PureComponent { static propTypes = { isUserTouching: PropTypes.func, isModalOpen: PropTypes.bool.isRequired, onModalOpen: PropTypes.func, onModalClose: PropTypes.func, value: PropTypes.bool.isRequired, onChange: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; state = { open: false, placement: null, }; handleToggle = ({ target }) => { if (this.props.isUserTouching()) { if (this.state.open) { this.props.onModalClose(); } else { this.props.onModalOpen({ actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })), onClick: this.handleModalActionClick, }); } } else { const { top } = target.getBoundingClientRect(); this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); this.setState({ open: !this.state.open }); } } handleModalActionClick = (e) => { e.preventDefault(); const { value } = this.options[Boolean(e.currentTarget.getAttribute('data-index'))]; this.props.onModalClose(); this.props.onChange(value); } handleKeyDown = e => { switch(e.key) { case 'Escape': this.handleClose(); break; } } handleClose = () => { this.setState({ open: false }); } handleChange = value => { this.props.onChange(value); } componentWillMount () { const { intl: { formatMessage } } = this.props; this.options = [ { icon: 'link', value: true, text: formatMessage(messages.federate_short), meta: formatMessage(messages.federate_long) }, { icon: 'chain-broken', value: false, text: formatMessage(messages.local_only_short), meta: formatMessage(messages.local_only_long) }, ]; } render () { const { value, intl } = this.props; const { open, placement } = this.state; const valueOption = this.options.find(item => item.value === value); return (
); } }