From a6873742e739054b49dec5ca7d0069d89a19ecbe Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 1 Oct 2017 12:20:00 +0200 Subject: [PATCH] Improve privacy dropdown, remove react-simple-dropdown dependency (#5140) * Improve privacy dropdown, remove react-simple-dropdown dependency * Animate privacy warning * Fix react-router-scroll --- .../compose/components/privacy_dropdown.js | 154 +++++++++++++----- .../features/compose/components/warning.js | 11 +- app/javascript/styles/components.scss | 27 +-- app/javascript/styles/rtl.scss | 18 +- 4 files changed, 138 insertions(+), 72 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js index 0474dfb4e3..d5bb58712f 100644 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js @@ -2,7 +2,10 @@ 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'; +import { Motion, spring } from 'react-motion'; import detectPassiveEvents from 'detect-passive-events'; +import classNames from 'classnames'; const messages = defineMessages({ public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, @@ -16,10 +19,77 @@ const messages = defineMessages({ change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' }, }); -const iconStyle = { - height: null, - lineHeight: '27px', -}; +const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; + +class PrivacyDropdownMenu extends React.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(); + } + } + + handleClick = e => { + if (e.key === 'Escape') { + this.props.onClose(); + } else if (!e.key || e.key === 'Enter') { + const value = 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); + } + + componentWillUnmount () { + document.removeEventListener('click', this.handleDocumentClick, false); + document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); + } + + setRef = c => { + this.node = c; + } + + render () { + const { style, items, value } = this.props; + + return ( + + {({ opacity, scaleX, scaleY }) => ( +
+ {items.map(item => +
+
+ +
+ +
+ {item.text} + {item.meta} +
+
+ )} +
+ )} +
+ ); + } + +} @injectIntl export default class PrivacyDropdown extends React.PureComponent { @@ -55,26 +125,30 @@ export default class PrivacyDropdown extends React.PureComponent { handleModalActionClick = (e) => { e.preventDefault(); + const { value } = this.options[e.currentTarget.getAttribute('data-index')]; + this.props.onModalClose(); this.props.onChange(value); } - handleClick = (e) => { - if (e.key === 'Escape') { - this.setState({ open: false }); - } else if (!e.key || e.key === 'Enter') { - const value = e.currentTarget.getAttribute('data-index'); - e.preventDefault(); - this.setState({ open: false }); - this.props.onChange(value); + handleKeyDown = e => { + switch(e.key) { + case 'Enter': + this.handleToggle(); + break; + case 'Escape': + this.handleClose(); + break; } } - onGlobalClick = (e) => { - if (e.target !== this.node && !this.node.contains(e.target) && this.state.open) { - this.setState({ open: false }); - } + handleClose = () => { + this.setState({ open: false }); + } + + handleChange = value => { + this.props.onChange(value); } componentWillMount () { @@ -88,20 +162,6 @@ export default class PrivacyDropdown extends React.PureComponent { ]; } - componentDidMount () { - window.addEventListener('click', this.onGlobalClick); - window.addEventListener('touchstart', this.onGlobalClick, detectPassiveEvents.hasSupport ? { passive: true } : false); - } - - componentWillUnmount () { - window.removeEventListener('click', this.onGlobalClick); - window.removeEventListener('touchstart', this.onGlobalClick, detectPassiveEvents.hasSupport ? { passive: true } : false); - } - - setRef = (c) => { - this.node = c; - } - render () { const { value, intl } = this.props; const { open } = this.state; @@ -109,19 +169,29 @@ export default class PrivacyDropdown extends React.PureComponent { const valueOption = this.options.find(item => item.value === value); return ( -
-
-
- {open && this.options.map(item => -
-
-
- {item.text} - {item.meta} -
-
- )} +
+
+
+ + + +
); } diff --git a/app/javascript/mastodon/features/compose/components/warning.js b/app/javascript/mastodon/features/compose/components/warning.js index 75f36b8404..dc902f33bd 100644 --- a/app/javascript/mastodon/features/compose/components/warning.js +++ b/app/javascript/mastodon/features/compose/components/warning.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Motion, spring } from 'react-motion'; export default class Warning extends React.PureComponent { @@ -11,9 +12,13 @@ export default class Warning extends React.PureComponent { const { message } = this.props; return ( -
- {message} -
+ + {({ opacity, scaleX, scaleY }) => ( +
+ {message} +
+ )} +
); } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 5ea0d134ef..caa7c07877 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1275,7 +1275,7 @@ background: $ui-secondary-color; padding: 4px 0; border-radius: 4px; - box-shadow: 0 0 15px rgba($base-shadow-color, 0.4); + box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); ul { list-style: none; @@ -2805,19 +2805,12 @@ button.icon-button.active i.fa-retweet { filter: none; } -.privacy-dropdown { - position: relative; -} - .privacy-dropdown__dropdown { - display: none; position: absolute; - left: 0; - top: 27px; - width: 230px; background: $simple-background-color; - border-radius: 0 4px 4px; - z-index: 2; + box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); + border-radius: 4px; + margin-left: 40px; overflow: hidden; } @@ -2869,6 +2862,18 @@ button.icon-button.active i.fa-retweet { background: $simple-background-color; border-radius: 4px 4px 0 0; box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1); + + .icon-button { + transition: none; + } + + &.active { + background: $ui-highlight-color; + + .icon-button { + color: $primary-text-color; + } + } } .privacy-dropdown__dropdown { diff --git a/app/javascript/styles/rtl.scss b/app/javascript/styles/rtl.scss index 0fdeccd9c3..67bfa8a385 100644 --- a/app/javascript/styles/rtl.scss +++ b/app/javascript/styles/rtl.scss @@ -128,22 +128,8 @@ body.rtl { } .privacy-dropdown__dropdown { - left: auto; - right: 0; - } - - .dropdown--active .dropdown__content { - text-align: right; - } - - .dropdown--active .dropdown__content::before { - left: auto; - right: 8px; - } - - .dropdown--active .dropdown__content > ul { - left: auto; - right: -10px; + margin-left: 0; + margin-right: 40px; } .privacy-dropdown__option__icon {