import PropTypes from "prop-types"; import { PureComponent } from "react"; import { injectIntl, defineMessages } from "react-intl"; import classNames from "classnames"; import { supportsPassiveEvents } from "detect-passive-events"; import Overlay from "react-overlays/Overlay"; import { Icon } from "mastodon/components/icon"; import { IconButton } from "../../../components/icon_button"; const messages = defineMessages({ public_short: { id: "privacy.public.short", defaultMessage: "Public" }, public_long: { id: "privacy.public.long", defaultMessage: "Visible for all" }, unlisted_short: { id: "privacy.unlisted.short", defaultMessage: "Unlisted" }, unlisted_long: { id: "privacy.unlisted.long", defaultMessage: "Visible for all, but opted-out of discovery features" }, private_short: { id: "privacy.private.short", defaultMessage: "Followers only" }, private_long: { id: "privacy.private.long", defaultMessage: "Visible for followers only" }, direct_short: { id: "privacy.direct.short", defaultMessage: "Mentioned people only" }, direct_long: { id: "privacy.direct.long", defaultMessage: "Visible for mentioned users only" }, change_privacy: { id: "privacy.change", defaultMessage: "Adjust status privacy" }, }); const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true; class PrivacyDropdownMenu extends PureComponent { static propTypes = { style: PropTypes.object, items: PropTypes.array.isRequired, value: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired, }; handleDocumentClick = e => { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); e.stopPropagation(); } }; handleKeyDown = e => { const { items } = this.props; const value = e.currentTarget.getAttribute("data-index"); const index = items.findIndex(item => { return (item.value === value); }); let element = null; switch(e.key) { case "Escape": this.props.onClose(); break; case "Enter": this.handleClick(e); break; case "ArrowDown": element = this.node.childNodes[index + 1] || this.node.firstChild; break; case "ArrowUp": element = this.node.childNodes[index - 1] || this.node.lastChild; break; case "Tab": if (e.shiftKey) { element = this.node.childNodes[index - 1] || this.node.lastChild; } else { element = this.node.childNodes[index + 1] || this.node.firstChild; } break; case "Home": element = this.node.firstChild; break; case "End": element = this.node.lastChild; break; } if (element) { element.focus(); this.props.onChange(element.getAttribute("data-index")); e.preventDefault(); e.stopPropagation(); } }; handleClick = e => { const value = e.currentTarget.getAttribute("data-index"); e.preventDefault(); this.props.onClose(); this.props.onChange(value); }; componentDidMount () { document.addEventListener("click", this.handleDocumentClick, { capture: true }); document.addEventListener("touchend", this.handleDocumentClick, listenerOptions); if (this.focusedItem) { this.focusedItem.focus({ preventScroll: true }); } } componentWillUnmount () { document.removeEventListener("click", this.handleDocumentClick, { capture: true }); document.removeEventListener("touchend", this.handleDocumentClick, listenerOptions); } setRef = c => { this.node = c; }; setFocusRef = c => { this.focusedItem = c; }; render () { const { style, items, value } = this.props; return (