Improve privacy dropdown, remove react-simple-dropdown dependency (#5140)
* Improve privacy dropdown, remove react-simple-dropdown dependency * Animate privacy warning * Fix react-router-scroll
This commit is contained in:
		
							parent
							
								
									0b3f1ec62a
								
							
						
					
					
						commit
						cdad7977fc
					
				| @ -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 ( | ||||
|       <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 }) }}> | ||||
|         {({ opacity, scaleX, scaleY }) => ( | ||||
|           <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}> | ||||
|             {items.map(item => | ||||
|               <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 })}> | ||||
|                 <div className='privacy-dropdown__option__icon'> | ||||
|                   <i className={`fa fa-fw fa-${item.icon}`} /> | ||||
|                 </div> | ||||
| 
 | ||||
|                 <div className='privacy-dropdown__option__content'> | ||||
|                   <strong>{item.text}</strong> | ||||
|                   {item.meta} | ||||
|                 </div> | ||||
|               </div> | ||||
|             )} | ||||
|           </div> | ||||
|         )} | ||||
|       </Motion> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @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 ( | ||||
|       <div ref={this.setRef} className={`privacy-dropdown ${open ? 'active' : ''}`}> | ||||
|         <div className='privacy-dropdown__value'><IconButton className='privacy-dropdown__value-icon' icon={valueOption.icon} title={intl.formatMessage(messages.change_privacy)} size={18} expanded={open} active={open} inverted onClick={this.handleToggle} style={iconStyle} /></div> | ||||
|         <div className='privacy-dropdown__dropdown'> | ||||
|           {open && this.options.map(item => | ||||
|             <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={`privacy-dropdown__option ${item.value === value ? 'active' : ''}`}> | ||||
|               <div className='privacy-dropdown__option__icon'><i className={`fa fa-fw fa-${item.icon}`} /></div> | ||||
|               <div className='privacy-dropdown__option__content'> | ||||
|                 <strong>{item.text}</strong> | ||||
|                 {item.meta} | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|       <div className={classNames('privacy-dropdown', { active: open })} onKeyDown={this.handleKeyDown}> | ||||
|         <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}> | ||||
|           <IconButton | ||||
|             className='privacy-dropdown__value-icon' | ||||
|             icon={valueOption.icon} | ||||
|             title={intl.formatMessage(messages.change_privacy)} | ||||
|             size={18} | ||||
|             expanded={open} | ||||
|             active={open} | ||||
|             inverted | ||||
|             onClick={this.handleToggle} | ||||
|             style={{ height: null, lineHeight: '27px' }} | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
|         <Overlay show={open} placement='bottom' target={this}> | ||||
|           <PrivacyDropdownMenu | ||||
|             items={this.options} | ||||
|             value={value} | ||||
|             onClose={this.handleClose} | ||||
|             onChange={this.handleChange} | ||||
|           /> | ||||
|         </Overlay> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -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 ( | ||||
|       <div className='compose-form__warning'> | ||||
|         {message} | ||||
|       </div> | ||||
|       <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 }) }}> | ||||
|         {({ opacity, scaleX, scaleY }) => ( | ||||
|           <div className='compose-form__warning' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}> | ||||
|             {message} | ||||
|           </div> | ||||
|         )} | ||||
|       </Motion> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user