Fix dropdown menu positions when scrolling (#22916)
* Update react-overlays to latest version * Fix breaking changes in dropdown menus * Use react-overlays built-in arrow positioning feature * Re-implemented `.dropdown-menu__arrow` to have a defined width and height to improve positioning * Moved wrapping div (`.dropdown-menu` from `DropdownMenu` to `Dropdown`) * Wrap button in a span to solve issue with ref * Temporarily remove animations * Fix breaking changes in emoji picker * Wrap EmojiPickerMenu in a div where react-overlays’ ref is added * Fix breaking changes in language dropdown * Fix breaking changes in privacy dropdown * Fix breaking changes in search form * Add animations back using `@keyframes` * Fix arrow color in light theme * Fix linting issue * Remove unused `mounted` state * Remove `placement` state from components and redux And remove the placement state from props of the menu components. * Remove abolution position to fix flip issue * Remove z-index to fix modals and overlay positions * Fix lint issues * Set placement in privacy and language components Copy the placement state into the `PrivacyDropdown` and `LanguageDropdown` components, to apply correct styling to the buttons depending on which placement the Overlay has. * Move `placement` state to correct component
This commit is contained in:
		| @@ -1,8 +1,8 @@ | ||||
| export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN'; | ||||
| export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE'; | ||||
|  | ||||
| export function openDropdownMenu(id, placement, keyboard, scroll_key) { | ||||
|   return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard, scroll_key }; | ||||
| export function openDropdownMenu(id, keyboard, scroll_key) { | ||||
|   return { type: DROPDOWN_MENU_OPEN, id, keyboard, scroll_key }; | ||||
| } | ||||
|  | ||||
| export function closeDropdownMenu(id) { | ||||
|   | ||||
| @@ -2,9 +2,7 @@ import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import IconButton from './icon_button'; | ||||
| import Overlay from 'react-overlays/lib/Overlay'; | ||||
| import Motion from '../features/ui/util/optional_motion'; | ||||
| import spring from 'react-motion/lib/spring'; | ||||
| import Overlay from 'react-overlays/Overlay'; | ||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | ||||
| import classNames from 'classnames'; | ||||
| import { CircularProgress } from 'mastodon/components/loading_indicator'; | ||||
| @@ -24,9 +22,6 @@ class DropdownMenu extends React.PureComponent { | ||||
|     scrollable: PropTypes.bool, | ||||
|     onClose: PropTypes.func.isRequired, | ||||
|     style: PropTypes.object, | ||||
|     placement: PropTypes.string, | ||||
|     arrowOffsetLeft: PropTypes.string, | ||||
|     arrowOffsetTop: PropTypes.string, | ||||
|     openedViaKeyboard: PropTypes.bool, | ||||
|     renderItem: PropTypes.func, | ||||
|     renderHeader: PropTypes.func, | ||||
| @@ -35,11 +30,6 @@ class DropdownMenu extends React.PureComponent { | ||||
|  | ||||
|   static defaultProps = { | ||||
|     style: {}, | ||||
|     placement: 'bottom', | ||||
|   }; | ||||
|  | ||||
|   state = { | ||||
|     mounted: false, | ||||
|   }; | ||||
|  | ||||
|   handleDocumentClick = e => { | ||||
| @@ -56,8 +46,6 @@ class DropdownMenu extends React.PureComponent { | ||||
|     if (this.focusedItem && this.props.openedViaKeyboard) { | ||||
|       this.focusedItem.focus({ preventScroll: true }); | ||||
|     } | ||||
|  | ||||
|     this.setState({ mounted: true }); | ||||
|   } | ||||
|  | ||||
|   componentWillUnmount () { | ||||
| @@ -139,40 +127,28 @@ class DropdownMenu extends React.PureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { items, style, placement, arrowOffsetLeft, arrowOffsetTop, scrollable, renderHeader, loading } = this.props; | ||||
|     const { mounted } = this.state; | ||||
|     const { items, scrollable, renderHeader, loading } = this.props; | ||||
|  | ||||
|     let renderItem = this.props.renderItem || this.renderItem; | ||||
|  | ||||
|     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 }) => ( | ||||
|           // It should not be transformed when mounting because the resulting | ||||
|           // size will be used to determine the coordinate of the menu by | ||||
|           // react-overlays | ||||
|           <div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}> | ||||
|             <div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} /> | ||||
|       <div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })} ref={this.setRef}> | ||||
|         {loading && ( | ||||
|           <CircularProgress size={30} strokeWidth={3.5} /> | ||||
|         )} | ||||
|  | ||||
|             <div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })}> | ||||
|               {loading && ( | ||||
|                 <CircularProgress size={30} strokeWidth={3.5} /> | ||||
|               )} | ||||
|  | ||||
|               {!loading && renderHeader && ( | ||||
|                 <div className='dropdown-menu__container__header'> | ||||
|                   {renderHeader(items)} | ||||
|                 </div> | ||||
|               )} | ||||
|  | ||||
|               {!loading && ( | ||||
|                 <ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}> | ||||
|                   {items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))} | ||||
|                 </ul> | ||||
|               )} | ||||
|             </div> | ||||
|         {!loading && renderHeader && ( | ||||
|           <div className='dropdown-menu__container__header'> | ||||
|             {renderHeader(items)} | ||||
|           </div> | ||||
|         )} | ||||
|       </Motion> | ||||
|  | ||||
|         {!loading && ( | ||||
|           <ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}> | ||||
|             {items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))} | ||||
|           </ul> | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -197,7 +173,6 @@ export default class Dropdown extends React.PureComponent { | ||||
|     isUserTouching: PropTypes.func, | ||||
|     onOpen: PropTypes.func.isRequired, | ||||
|     onClose: PropTypes.func.isRequired, | ||||
|     dropdownPlacement: PropTypes.string, | ||||
|     openDropdownId: PropTypes.number, | ||||
|     openedViaKeyboard: PropTypes.bool, | ||||
|     renderItem: PropTypes.func, | ||||
| @@ -213,13 +188,11 @@ export default class Dropdown extends React.PureComponent { | ||||
|     id: id++, | ||||
|   }; | ||||
|  | ||||
|   handleClick = ({ target, type }) => { | ||||
|   handleClick = ({ type }) => { | ||||
|     if (this.state.id === this.props.openDropdownId) { | ||||
|       this.handleClose(); | ||||
|     } else { | ||||
|       const { top } = target.getBoundingClientRect(); | ||||
|       const placement = top * 2 < innerHeight ? 'bottom' : 'top'; | ||||
|       this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click'); | ||||
|       this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -303,7 +276,6 @@ export default class Dropdown extends React.PureComponent { | ||||
|       disabled, | ||||
|       loading, | ||||
|       scrollable, | ||||
|       dropdownPlacement, | ||||
|       openDropdownId, | ||||
|       openedViaKeyboard, | ||||
|       children, | ||||
| @@ -314,7 +286,6 @@ export default class Dropdown extends React.PureComponent { | ||||
|     const open = this.state.id === openDropdownId; | ||||
|  | ||||
|     const button = children ? React.cloneElement(React.Children.only(children), { | ||||
|       ref: this.setTargetRef, | ||||
|       onClick: this.handleClick, | ||||
|       onMouseDown: this.handleMouseDown, | ||||
|       onKeyDown: this.handleButtonKeyDown, | ||||
| @@ -326,7 +297,6 @@ export default class Dropdown extends React.PureComponent { | ||||
|         active={open} | ||||
|         disabled={disabled} | ||||
|         size={size} | ||||
|         ref={this.setTargetRef} | ||||
|         onClick={this.handleClick} | ||||
|         onMouseDown={this.handleMouseDown} | ||||
|         onKeyDown={this.handleButtonKeyDown} | ||||
| @@ -336,19 +306,27 @@ export default class Dropdown extends React.PureComponent { | ||||
|  | ||||
|     return ( | ||||
|       <React.Fragment> | ||||
|         {button} | ||||
|  | ||||
|         <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}> | ||||
|           <DropdownMenu | ||||
|             items={items} | ||||
|             loading={loading} | ||||
|             scrollable={scrollable} | ||||
|             onClose={this.handleClose} | ||||
|             openedViaKeyboard={openedViaKeyboard} | ||||
|             renderItem={renderItem} | ||||
|             renderHeader={renderHeader} | ||||
|             onItemClick={this.handleItemClick} | ||||
|           /> | ||||
|         <span ref={this.setTargetRef}> | ||||
|           {button} | ||||
|         </span> | ||||
|         <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> | ||||
|           {({ props, arrowProps, placement }) => ( | ||||
|             <div {...props}> | ||||
|               <div className={`dropdown-animation dropdown-menu ${placement}`}> | ||||
|                 <div className={`dropdown-menu__arrow ${placement}`} {...arrowProps} /> | ||||
|                 <DropdownMenu | ||||
|                   items={items} | ||||
|                   loading={loading} | ||||
|                   scrollable={scrollable} | ||||
|                   onClose={this.handleClose} | ||||
|                   openedViaKeyboard={openedViaKeyboard} | ||||
|                   renderItem={renderItem} | ||||
|                   renderHeader={renderHeader} | ||||
|                   onItemClick={this.handleItemClick} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </Overlay> | ||||
|       </React.Fragment> | ||||
|     ); | ||||
|   | ||||
| @@ -4,7 +4,6 @@ import { fetchHistory } from 'mastodon/actions/history'; | ||||
| import DropdownMenu from 'mastodon/components/dropdown_menu'; | ||||
|  | ||||
| const mapStateToProps = (state, { statusId }) => ({ | ||||
|   dropdownPlacement: state.getIn(['dropdown_menu', 'placement']), | ||||
|   openDropdownId: state.getIn(['dropdown_menu', 'openId']), | ||||
|   openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']), | ||||
|   items: state.getIn(['history', statusId, 'items']), | ||||
| @@ -13,9 +12,9 @@ const mapStateToProps = (state, { statusId }) => ({ | ||||
|  | ||||
| const mapDispatchToProps = (dispatch, { statusId }) => ({ | ||||
|  | ||||
|   onOpen (id, onItemClick, dropdownPlacement, keyboard) { | ||||
|   onOpen (id, onItemClick, keyboard) { | ||||
|     dispatch(fetchHistory(statusId)); | ||||
|     dispatch(openDropdownMenu(id, dropdownPlacement, keyboard)); | ||||
|     dispatch(openDropdownMenu(id, keyboard)); | ||||
|   }, | ||||
|  | ||||
|   onClose (id) { | ||||
|   | ||||
| @@ -6,13 +6,12 @@ import DropdownMenu from '../components/dropdown_menu'; | ||||
| import { isUserTouching } from '../is_mobile'; | ||||
|  | ||||
| const mapStateToProps = state => ({ | ||||
|   dropdownPlacement: state.getIn(['dropdown_menu', 'placement']), | ||||
|   openDropdownId: state.getIn(['dropdown_menu', 'openId']), | ||||
|   openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']), | ||||
| }); | ||||
|  | ||||
| const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({ | ||||
|   onOpen(id, onItemClick, dropdownPlacement, keyboard) { | ||||
|   onOpen(id, onItemClick, keyboard) { | ||||
|     if (status) { | ||||
|       dispatch(fetchRelationships([status.getIn(['account', 'id'])])); | ||||
|     } | ||||
| @@ -21,7 +20,7 @@ const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({ | ||||
|       status, | ||||
|       actions: items, | ||||
|       onClick: onItemClick, | ||||
|     }) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey)); | ||||
|     }) : openDropdownMenu(id, keyboard, scrollKey)); | ||||
|   }, | ||||
|  | ||||
|   onClose(id) { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | ||||
| import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; | ||||
| import Overlay from 'react-overlays/lib/Overlay'; | ||||
| import Overlay from 'react-overlays/Overlay'; | ||||
| import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | ||||
| @@ -154,9 +154,6 @@ class EmojiPickerMenu extends React.PureComponent { | ||||
|     onClose: PropTypes.func.isRequired, | ||||
|     onPick: PropTypes.func.isRequired, | ||||
|     style: PropTypes.object, | ||||
|     placement: PropTypes.string, | ||||
|     arrowOffsetLeft: PropTypes.string, | ||||
|     arrowOffsetTop: PropTypes.string, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     skinTone: PropTypes.number.isRequired, | ||||
|     onSkinTone: PropTypes.func.isRequired, | ||||
| @@ -324,14 +321,13 @@ class EmojiPickerDropdown extends React.PureComponent { | ||||
|   state = { | ||||
|     active: false, | ||||
|     loading: false, | ||||
|     placement: null, | ||||
|   }; | ||||
|  | ||||
|   setRef = (c) => { | ||||
|     this.dropdown = c; | ||||
|   } | ||||
|  | ||||
|   onShowDropdown = ({ target }) => { | ||||
|   onShowDropdown = () => { | ||||
|     this.setState({ active: true }); | ||||
|  | ||||
|     if (!EmojiPicker) { | ||||
| @@ -346,9 +342,6 @@ class EmojiPickerDropdown extends React.PureComponent { | ||||
|         this.setState({ loading: false, active: false }); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     const { top } = target.getBoundingClientRect(); | ||||
|     this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); | ||||
|   } | ||||
|  | ||||
|   onHideDropdown = () => { | ||||
| @@ -382,7 +375,7 @@ class EmojiPickerDropdown extends React.PureComponent { | ||||
|   render () { | ||||
|     const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props; | ||||
|     const title = intl.formatMessage(messages.emoji); | ||||
|     const { active, loading, placement } = this.state; | ||||
|     const { active, loading } = this.state; | ||||
|  | ||||
|     return ( | ||||
|       <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}> | ||||
| @@ -394,16 +387,22 @@ class EmojiPickerDropdown extends React.PureComponent { | ||||
|           />} | ||||
|         </div> | ||||
|  | ||||
|         <Overlay show={active} placement={placement} target={this.findTarget}> | ||||
|           <EmojiPickerMenu | ||||
|             custom_emojis={this.props.custom_emojis} | ||||
|             loading={loading} | ||||
|             onClose={this.onHideDropdown} | ||||
|             onPick={onPickEmoji} | ||||
|             onSkinTone={onSkinTone} | ||||
|             skinTone={skinTone} | ||||
|             frequentlyUsedEmojis={frequentlyUsedEmojis} | ||||
|           /> | ||||
|         <Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> | ||||
|           {({ props, placement })=> ( | ||||
|             <div {...props} style={{ ...props.style, width: 299 }}> | ||||
|               <div className={`dropdown-animation ${placement}`}> | ||||
|                 <EmojiPickerMenu | ||||
|                   custom_emojis={this.props.custom_emojis} | ||||
|                   loading={loading} | ||||
|                   onClose={this.onHideDropdown} | ||||
|                   onPick={onPickEmoji} | ||||
|                   onSkinTone={onSkinTone} | ||||
|                   skinTone={skinTone} | ||||
|                   frequentlyUsedEmojis={frequentlyUsedEmojis} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </Overlay> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -2,9 +2,7 @@ import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { injectIntl, defineMessages } from 'react-intl'; | ||||
| import TextIconButton from './text_icon_button'; | ||||
| import Overlay from 'react-overlays/lib/Overlay'; | ||||
| import Motion from 'mastodon/features/ui/util/optional_motion'; | ||||
| import spring from 'react-motion/lib/spring'; | ||||
| import Overlay from 'react-overlays/Overlay'; | ||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | ||||
| import classNames from 'classnames'; | ||||
| import { languages as preloadedLanguages } from 'mastodon/initial_state'; | ||||
| @@ -22,10 +20,8 @@ const listenerOptions = supportsPassiveEvents ? { passive: true } : false; | ||||
| class LanguageDropdownMenu extends React.PureComponent { | ||||
|  | ||||
|   static propTypes = { | ||||
|     style: PropTypes.object, | ||||
|     value: PropTypes.string.isRequired, | ||||
|     frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired, | ||||
|     placement: PropTypes.string.isRequired, | ||||
|     onClose: PropTypes.func.isRequired, | ||||
|     onChange: PropTypes.func.isRequired, | ||||
|     languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)), | ||||
| @@ -37,7 +33,6 @@ class LanguageDropdownMenu extends React.PureComponent { | ||||
|   }; | ||||
|  | ||||
|   state = { | ||||
|     mounted: false, | ||||
|     searchValue: '', | ||||
|   }; | ||||
|  | ||||
| @@ -50,7 +45,6 @@ class LanguageDropdownMenu extends React.PureComponent { | ||||
|   componentDidMount () { | ||||
|     document.addEventListener('click', this.handleDocumentClick, false); | ||||
|     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); | ||||
|     this.setState({ mounted: true }); | ||||
|  | ||||
|     // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need | ||||
|     // to wait for a frame before focusing | ||||
| @@ -222,29 +216,22 @@ class LanguageDropdownMenu extends React.PureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { style, placement, intl } = this.props; | ||||
|     const { mounted, searchValue } = this.state; | ||||
|     const { intl } = this.props; | ||||
|     const { searchValue } = this.state; | ||||
|     const isSearching = searchValue !== ''; | ||||
|     const results = this.search(); | ||||
|  | ||||
|     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 }) => ( | ||||
|           // It should not be transformed when mounting because the resulting | ||||
|           // size will be used to determine the coordinate of the menu by | ||||
|           // react-overlays | ||||
|           <div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}> | ||||
|             <div className='emoji-mart-search'> | ||||
|               <input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} /> | ||||
|               <button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button> | ||||
|             </div> | ||||
|       <div ref={this.setRef}> | ||||
|         <div className='emoji-mart-search'> | ||||
|           <input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} /> | ||||
|           <button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button> | ||||
|         </div> | ||||
|  | ||||
|             <div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}> | ||||
|               {results.map(this.renderItem)} | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|       </Motion> | ||||
|         <div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}> | ||||
|           {results.map(this.renderItem)} | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -266,14 +253,11 @@ class LanguageDropdown extends React.PureComponent { | ||||
|     placement: 'bottom', | ||||
|   }; | ||||
|  | ||||
|   handleToggle = ({ target }) => { | ||||
|     const { top } = target.getBoundingClientRect(); | ||||
|  | ||||
|   handleToggle = () => { | ||||
|     if (this.state.open && this.activeElement) { | ||||
|       this.activeElement.focus({ preventScroll: true }); | ||||
|     } | ||||
|  | ||||
|     this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); | ||||
|     this.setState({ open: !this.state.open }); | ||||
|   } | ||||
|  | ||||
| @@ -293,13 +277,25 @@ class LanguageDropdown extends React.PureComponent { | ||||
|     onChange(value); | ||||
|   } | ||||
|  | ||||
|   setTargetRef = c => { | ||||
|     this.target = c; | ||||
|   } | ||||
|  | ||||
|   findTarget = () => { | ||||
|     return this.target; | ||||
|   } | ||||
|  | ||||
|   handleOverlayEnter = (state) => { | ||||
|     this.setState({ placement: state.placement }); | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { value, intl, frequentlyUsedLanguages } = this.props; | ||||
|     const { open, placement } = this.state; | ||||
|  | ||||
|     return ( | ||||
|       <div className={classNames('privacy-dropdown', { active: open })}> | ||||
|         <div className='privacy-dropdown__value'> | ||||
|       <div className={classNames('privacy-dropdown', placement, { active: open })}> | ||||
|         <div className='privacy-dropdown__value' ref={this.setTargetRef} > | ||||
|           <TextIconButton | ||||
|             className='privacy-dropdown__value-icon' | ||||
|             label={value && value.toUpperCase()} | ||||
| @@ -309,15 +305,20 @@ class LanguageDropdown extends React.PureComponent { | ||||
|           /> | ||||
|         </div> | ||||
|  | ||||
|         <Overlay show={open} placement={placement} target={this}> | ||||
|           <LanguageDropdownMenu | ||||
|             value={value} | ||||
|             frequentlyUsedLanguages={frequentlyUsedLanguages} | ||||
|             onClose={this.handleClose} | ||||
|             onChange={this.handleChange} | ||||
|             placement={placement} | ||||
|             intl={intl} | ||||
|           /> | ||||
|         <Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}> | ||||
|           {({ props, placement }) => ( | ||||
|             <div {...props} style={{ ...props.style, width: 280 }}> | ||||
|               <div className={`dropdown-animation language-dropdown__dropdown ${placement}`} > | ||||
|                 <LanguageDropdownMenu | ||||
|                   value={value} | ||||
|                   frequentlyUsedLanguages={frequentlyUsedLanguages} | ||||
|                   onClose={this.handleClose} | ||||
|                   onChange={this.handleChange} | ||||
|                   intl={intl} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </Overlay> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -2,9 +2,7 @@ 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 Overlay from 'react-overlays/Overlay'; | ||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | ||||
| import classNames from 'classnames'; | ||||
| import Icon from 'mastodon/components/icon'; | ||||
| @@ -29,15 +27,10 @@ class PrivacyDropdownMenu extends React.PureComponent { | ||||
|     style: PropTypes.object, | ||||
|     items: PropTypes.array.isRequired, | ||||
|     value: PropTypes.string.isRequired, | ||||
|     placement: PropTypes.string.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(); | ||||
| @@ -101,7 +94,6 @@ class PrivacyDropdownMenu extends React.PureComponent { | ||||
|     document.addEventListener('click', this.handleDocumentClick, false); | ||||
|     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); | ||||
|     if (this.focusedItem) this.focusedItem.focus({ preventScroll: true }); | ||||
|     this.setState({ mounted: true }); | ||||
|   } | ||||
|  | ||||
|   componentWillUnmount () { | ||||
| @@ -118,31 +110,23 @@ class PrivacyDropdownMenu extends React.PureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { mounted } = this.state; | ||||
|     const { style, items, placement, value } = this.props; | ||||
|     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 }) => ( | ||||
|           // It should not be transformed when mounting because the resulting | ||||
|           // size will be used to determine the coordinate of the menu by | ||||
|           // react-overlays | ||||
|           <div className={`privacy-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}> | ||||
|             {items.map(item => ( | ||||
|               <div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}> | ||||
|                 <div className='privacy-dropdown__option__icon'> | ||||
|                   <Icon id={item.icon} fixedWidth /> | ||||
|                 </div> | ||||
|       <div style={{ ...style }} role='listbox' ref={this.setRef}> | ||||
|         {items.map(item => ( | ||||
|           <div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}> | ||||
|             <div className='privacy-dropdown__option__icon'> | ||||
|               <Icon id={item.icon} fixedWidth /> | ||||
|             </div> | ||||
|  | ||||
|                 <div className='privacy-dropdown__option__content'> | ||||
|                   <strong>{item.text}</strong> | ||||
|                   {item.meta} | ||||
|                 </div> | ||||
|               </div> | ||||
|             ))} | ||||
|             <div className='privacy-dropdown__option__content'> | ||||
|               <strong>{item.text}</strong> | ||||
|               {item.meta} | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|       </Motion> | ||||
|         ))} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -168,7 +152,7 @@ class PrivacyDropdown extends React.PureComponent { | ||||
|     placement: 'bottom', | ||||
|   }; | ||||
|  | ||||
|   handleToggle = ({ target }) => { | ||||
|   handleToggle = () => { | ||||
|     if (this.props.isUserTouching && this.props.isUserTouching()) { | ||||
|       if (this.state.open) { | ||||
|         this.props.onModalClose(); | ||||
| @@ -179,11 +163,9 @@ class PrivacyDropdown extends React.PureComponent { | ||||
|         }); | ||||
|       } | ||||
|     } else { | ||||
|       const { top } = target.getBoundingClientRect(); | ||||
|       if (this.state.open && this.activeElement) { | ||||
|         this.activeElement.focus({ preventScroll: true }); | ||||
|       } | ||||
|       this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); | ||||
|       this.setState({ open: !this.state.open }); | ||||
|     } | ||||
|   } | ||||
| @@ -247,6 +229,18 @@ class PrivacyDropdown extends React.PureComponent { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   setTargetRef = c => { | ||||
|     this.target = c; | ||||
|   } | ||||
|  | ||||
|   findTarget = () => { | ||||
|     return this.target; | ||||
|   } | ||||
|  | ||||
|   handleOverlayEnter = (state) => { | ||||
|     this.setState({ placement: state.placement }); | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { value, container, disabled, intl } = this.props; | ||||
|     const { open, placement } = this.state; | ||||
| @@ -255,7 +249,7 @@ class PrivacyDropdown extends React.PureComponent { | ||||
|  | ||||
|     return ( | ||||
|       <div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}> | ||||
|         <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })}> | ||||
|         <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })} ref={this.setTargetRef}> | ||||
|           <IconButton | ||||
|             className='privacy-dropdown__value-icon' | ||||
|             icon={valueOption.icon} | ||||
| @@ -272,14 +266,19 @@ class PrivacyDropdown extends React.PureComponent { | ||||
|           /> | ||||
|         </div> | ||||
|  | ||||
|         <Overlay show={open} placement={placement} target={this} container={container}> | ||||
|           <PrivacyDropdownMenu | ||||
|             items={this.options} | ||||
|             value={value} | ||||
|             onClose={this.handleClose} | ||||
|             onChange={this.handleChange} | ||||
|             placement={placement} | ||||
|           /> | ||||
|         <Overlay show={open} placement={'bottom'} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}> | ||||
|           {({ props, placement }) => ( | ||||
|             <div {...props} style={{ ...props.style, width: 350, maxWidth: '100vw' }}> | ||||
|               <div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}> | ||||
|                 <PrivacyDropdownMenu | ||||
|                   items={this.options} | ||||
|                   value={value} | ||||
|                   onClose={this.handleClose} | ||||
|                   onChange={this.handleChange} | ||||
|                 /> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </Overlay> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | ||||
| import Overlay from 'react-overlays/lib/Overlay'; | ||||
| import Motion from '../../ui/util/optional_motion'; | ||||
| import spring from 'react-motion/lib/spring'; | ||||
| import Overlay from 'react-overlays/Overlay'; | ||||
| import { searchEnabled } from '../../../initial_state'; | ||||
| import Icon from 'mastodon/components/icon'; | ||||
|  | ||||
| @@ -14,31 +12,20 @@ const messages = defineMessages({ | ||||
|  | ||||
| class SearchPopout extends React.PureComponent { | ||||
|  | ||||
|   static propTypes = { | ||||
|     style: PropTypes.object, | ||||
|   }; | ||||
|  | ||||
|   render () { | ||||
|     const { style } = this.props; | ||||
|     const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />; | ||||
|     return ( | ||||
|       <div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}> | ||||
|         <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='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}> | ||||
|               <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4> | ||||
|       <div className='search-popout'> | ||||
|         <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4> | ||||
|  | ||||
|               <ul> | ||||
|                 <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li> | ||||
|                 <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li> | ||||
|                 <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li> | ||||
|                 <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li> | ||||
|               </ul> | ||||
|         <ul> | ||||
|           <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li> | ||||
|           <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li> | ||||
|           <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li> | ||||
|           <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li> | ||||
|         </ul> | ||||
|  | ||||
|               {extraInformation} | ||||
|             </div> | ||||
|           )} | ||||
|         </Motion> | ||||
|         {extraInformation} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| @@ -115,6 +102,10 @@ class Search extends React.PureComponent { | ||||
|     this.setState({ expanded: false }); | ||||
|   } | ||||
|  | ||||
|   findTarget = () => { | ||||
|     return this.searchForm; | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { intl, value, submitted } = this.props; | ||||
|     const { expanded } = this.state; | ||||
| @@ -140,8 +131,14 @@ class Search extends React.PureComponent { | ||||
|           <Icon id='search' className={hasValue ? '' : 'active'} /> | ||||
|           <Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} /> | ||||
|         </div> | ||||
|         <Overlay show={expanded && !hasValue} placement='bottom' target={this} container={this}> | ||||
|           <SearchPopout /> | ||||
|         <Overlay show={expanded && !hasValue} placement='bottom' target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> | ||||
|           {({ props, placement }) => ( | ||||
|             <div {...props} style={{ ...props.style, width: 285, zIndex: 2 }}> | ||||
|               <div className={`dropdown-animation ${placement}`}> | ||||
|                 <SearchPopout /> | ||||
|               </div> | ||||
|             </div> | ||||
|           )} | ||||
|         </Overlay> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -4,12 +4,12 @@ import { | ||||
|   DROPDOWN_MENU_CLOSE, | ||||
| } from '../actions/dropdown_menu'; | ||||
|  | ||||
| const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false, scroll_key: null }); | ||||
| const initialState = Immutable.Map({ openId: null, keyboard: false, scroll_key: null }); | ||||
|  | ||||
| export default function dropdownMenu(state = initialState, action) { | ||||
|   switch (action.type) { | ||||
|   case DROPDOWN_MENU_OPEN: | ||||
|     return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard, scroll_key: action.scroll_key }); | ||||
|     return state.merge({ openId: action.id, keyboard: action.keyboard, scroll_key: action.scroll_key }); | ||||
|   case DROPDOWN_MENU_CLOSE: | ||||
|     return state.get('openId') === action.id ? state.set('openId', null).set('scroll_key', null) : state; | ||||
|   default: | ||||
|   | ||||
| @@ -285,22 +285,8 @@ html { | ||||
| .dropdown-menu { | ||||
|   background: $white; | ||||
|  | ||||
|   &__arrow { | ||||
|     &.left { | ||||
|       border-left-color: $white; | ||||
|     } | ||||
|  | ||||
|     &.top { | ||||
|       border-top-color: $white; | ||||
|     } | ||||
|  | ||||
|     &.bottom { | ||||
|       border-bottom-color: $white; | ||||
|     } | ||||
|  | ||||
|     &.right { | ||||
|       border-right-color: $white; | ||||
|     } | ||||
|   &__arrow::before { | ||||
|     background-color: $white; | ||||
|   } | ||||
|  | ||||
|   &__item { | ||||
|   | ||||
| @@ -363,8 +363,8 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .dropdown-menu { | ||||
|   position: absolute; | ||||
| body > [data-popper-placement] { | ||||
|   z-index: 3; | ||||
| } | ||||
|  | ||||
| .invisible { | ||||
| @@ -1932,6 +1932,42 @@ a.account__display-name { | ||||
|   text-decoration: none; | ||||
| } | ||||
|  | ||||
| .dropdown-animation { | ||||
|   animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1); | ||||
|  | ||||
|   @keyframes dropdown { | ||||
|     from { | ||||
|       opacity: 0; | ||||
|       transform: scaleX(0.85) scaleY(0.75); | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|       opacity: 1; | ||||
|       transform: scaleX(1) scaleY(1); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.top { | ||||
|     transform-origin: bottom; | ||||
|   } | ||||
|  | ||||
|   &.right { | ||||
|     transform-origin: left; | ||||
|   } | ||||
|  | ||||
|   &.bottom { | ||||
|     transform-origin: top; | ||||
|   } | ||||
|  | ||||
|   &.left { | ||||
|     transform-origin: right; | ||||
|   } | ||||
|  | ||||
|   .reduce-motion & { | ||||
|     animation: none; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .dropdown { | ||||
|   display: inline-block; | ||||
| } | ||||
| @@ -2016,36 +2052,42 @@ a.account__display-name { | ||||
|  | ||||
| .dropdown-menu__arrow { | ||||
|   position: absolute; | ||||
|   width: 0; | ||||
|   height: 0; | ||||
|   border: 0 solid transparent; | ||||
|  | ||||
|   &.left { | ||||
|     right: -5px; | ||||
|     margin-top: -5px; | ||||
|     border-width: 5px 0 5px 5px; | ||||
|     border-left-color: $ui-secondary-color; | ||||
|   &::before { | ||||
|     content: ''; | ||||
|     display: block; | ||||
|     width: 14px; | ||||
|     height: 5px; | ||||
|     background-color: $ui-secondary-color; | ||||
|     mask-image: url("data:image/svg+xml;utf8,<svg width='14' height='5' xmlns='http://www.w3.org/2000/svg'><path d='M7 0L0 5h14L7 0z' fill='white'/></svg>"); | ||||
|   } | ||||
|  | ||||
|   &.top { | ||||
|     bottom: -5px; | ||||
|     margin-left: -7px; | ||||
|     border-width: 5px 7px 0; | ||||
|     border-top-color: $ui-secondary-color; | ||||
|  | ||||
|     &::before { | ||||
|       transform: rotate(180deg); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.right { | ||||
|     left: -9px; | ||||
|  | ||||
|     &::before { | ||||
|       transform: rotate(-90deg); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.bottom { | ||||
|     top: -5px; | ||||
|     margin-left: -7px; | ||||
|     border-width: 0 7px 5px; | ||||
|     border-bottom-color: $ui-secondary-color; | ||||
|   } | ||||
|  | ||||
|   &.right { | ||||
|     left: -5px; | ||||
|     margin-top: -5px; | ||||
|     border-width: 5px 5px 5px 0; | ||||
|     border-right-color: $ui-secondary-color; | ||||
|   &.left { | ||||
|     right: -9px; | ||||
|  | ||||
|     &::before { | ||||
|       transform: rotate(90deg); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -4524,7 +4566,6 @@ a.status-card.compact:hover { | ||||
| } | ||||
|  | ||||
| .privacy-dropdown__dropdown { | ||||
|   position: absolute; | ||||
|   background: $simple-background-color; | ||||
|   box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); | ||||
|   border-radius: 4px; | ||||
| @@ -4630,7 +4671,6 @@ a.status-card.compact:hover { | ||||
|  | ||||
| .language-dropdown { | ||||
|   &__dropdown { | ||||
|     position: absolute; | ||||
|     background: $simple-background-color; | ||||
|     box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); | ||||
|     border-radius: 4px; | ||||
| @@ -4877,7 +4917,6 @@ a.status-card.compact:hover { | ||||
| .modal-root__modal { | ||||
|   pointer-events: auto; | ||||
|   display: flex; | ||||
|   z-index: 9999; | ||||
| } | ||||
|  | ||||
| .video-modal__container { | ||||
|   | ||||
| @@ -98,7 +98,7 @@ | ||||
|     "react-intl": "^2.9.0", | ||||
|     "react-motion": "^0.5.2", | ||||
|     "react-notification": "^6.8.5", | ||||
|     "react-overlays": "^0.9.3", | ||||
|     "react-overlays": "^5.2.1", | ||||
|     "react-redux": "^7.2.9", | ||||
|     "react-redux-loading-bar": "^5.0.4", | ||||
|     "react-router-dom": "^4.1.1", | ||||
|   | ||||
							
								
								
									
										97
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								yarn.lock
									
									
									
									
									
								
							| @@ -1029,7 +1029,7 @@ | ||||
|   dependencies: | ||||
|     regenerator-runtime "^0.12.0" | ||||
|  | ||||
| "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.9", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": | ||||
| "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.9", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": | ||||
|   version "7.20.7" | ||||
|   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" | ||||
|   integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== | ||||
| @@ -1562,11 +1562,23 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" | ||||
|   integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== | ||||
|  | ||||
| "@popperjs/core@^2.11.6": | ||||
|   version "2.11.6" | ||||
|   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" | ||||
|   integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== | ||||
|  | ||||
| "@rails/ujs@^6.1.7": | ||||
|   version "6.1.7" | ||||
|   resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.7.tgz#b09dc5b2105dd267e8374c47e4490240451dc7f6" | ||||
|   integrity sha512-0e7WQ4LE/+LEfW2zfAw9ppsB6A8RmxbdAUPAF++UT80epY+7emuQDkKXmaK0a9lp6An50RvzezI0cIQjp1A58w== | ||||
|  | ||||
| "@restart/hooks@^0.4.7": | ||||
|   version "0.4.7" | ||||
|   resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.7.tgz#d79ca6472c01ce04389fc73d4a79af1b5e33cd39" | ||||
|   integrity sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A== | ||||
|   dependencies: | ||||
|     dequal "^2.0.2" | ||||
|  | ||||
| "@rollup/plugin-babel@^5.2.0": | ||||
|   version "5.3.1" | ||||
|   resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" | ||||
| @@ -1880,6 +1892,15 @@ | ||||
|     "@types/scheduler" "*" | ||||
|     csstype "^3.0.2" | ||||
|  | ||||
| "@types/react@>=16.9.11": | ||||
|   version "18.0.26" | ||||
|   resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" | ||||
|   integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== | ||||
|   dependencies: | ||||
|     "@types/prop-types" "*" | ||||
|     "@types/scheduler" "*" | ||||
|     csstype "^3.0.2" | ||||
|  | ||||
| "@types/resolve@1.17.1": | ||||
|   version "1.17.1" | ||||
|   resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" | ||||
| @@ -1919,6 +1940,11 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" | ||||
|   integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== | ||||
|  | ||||
| "@types/warning@^3.0.0": | ||||
|   version "3.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" | ||||
|   integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA== | ||||
|  | ||||
| "@types/yargs-parser@*": | ||||
|   version "15.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" | ||||
| @@ -3967,6 +3993,11 @@ depd@~1.1.2: | ||||
|   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" | ||||
|   integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= | ||||
|  | ||||
| dequal@^2.0.2: | ||||
|   version "2.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" | ||||
|   integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== | ||||
|  | ||||
| des.js@^1.0.0: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" | ||||
| @@ -4072,7 +4103,7 @@ dom-accessibility-api@^0.5.6: | ||||
|   resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" | ||||
|   integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== | ||||
|  | ||||
| dom-helpers@^3.2.1, dom-helpers@^3.4.0: | ||||
| dom-helpers@^3.4.0: | ||||
|   version "3.4.0" | ||||
|   resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" | ||||
|   integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== | ||||
| @@ -4087,6 +4118,14 @@ dom-helpers@^5.0.1: | ||||
|     "@babel/runtime" "^7.6.3" | ||||
|     csstype "^2.6.7" | ||||
|  | ||||
| dom-helpers@^5.2.0: | ||||
|   version "5.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" | ||||
|   integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== | ||||
|   dependencies: | ||||
|     "@babel/runtime" "^7.8.7" | ||||
|     csstype "^3.0.2" | ||||
|  | ||||
| dom-serializer@0: | ||||
|   version "0.2.2" | ||||
|   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" | ||||
| @@ -8630,14 +8669,6 @@ prompts@^2.0.1: | ||||
|     kleur "^3.0.3" | ||||
|     sisteransi "^1.0.4" | ||||
|  | ||||
| prop-types-extra@^1.0.1: | ||||
|   version "1.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" | ||||
|   integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== | ||||
|   dependencies: | ||||
|     react-is "^16.3.2" | ||||
|     warning "^4.0.0" | ||||
|  | ||||
| prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: | ||||
|   version "15.8.1" | ||||
|   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" | ||||
| @@ -8874,7 +8905,7 @@ react-intl@^2.9.0: | ||||
|     intl-relativeformat "^2.1.0" | ||||
|     invariant "^2.1.1" | ||||
|  | ||||
| react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.6: | ||||
| react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.6: | ||||
|   version "16.13.1" | ||||
|   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" | ||||
|   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== | ||||
| @@ -8910,17 +8941,19 @@ react-notification@^6.8.5: | ||||
|   dependencies: | ||||
|     prop-types "^15.6.2" | ||||
|  | ||||
| react-overlays@^0.9.3: | ||||
|   version "0.9.3" | ||||
|   resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.9.3.tgz#5bac8c1e9e7e057a125181dee2d784864dd62902" | ||||
|   integrity sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ== | ||||
| react-overlays@^5.2.1: | ||||
|   version "5.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b" | ||||
|   integrity sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA== | ||||
|   dependencies: | ||||
|     classnames "^2.2.5" | ||||
|     dom-helpers "^3.2.1" | ||||
|     prop-types "^15.5.10" | ||||
|     prop-types-extra "^1.0.1" | ||||
|     react-transition-group "^2.2.1" | ||||
|     warning "^3.0.0" | ||||
|     "@babel/runtime" "^7.13.8" | ||||
|     "@popperjs/core" "^2.11.6" | ||||
|     "@restart/hooks" "^0.4.7" | ||||
|     "@types/warning" "^3.0.0" | ||||
|     dom-helpers "^5.2.0" | ||||
|     prop-types "^15.7.2" | ||||
|     uncontrollable "^7.2.1" | ||||
|     warning "^4.0.3" | ||||
|  | ||||
| react-redux-loading-bar@^5.0.4: | ||||
|   version "5.0.4" | ||||
| @@ -9059,16 +9092,6 @@ react-toggle@^4.1.3: | ||||
|   dependencies: | ||||
|     classnames "^2.2.5" | ||||
|  | ||||
| react-transition-group@^2.2.1: | ||||
|   version "2.9.0" | ||||
|   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" | ||||
|   integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== | ||||
|   dependencies: | ||||
|     dom-helpers "^3.4.0" | ||||
|     loose-envify "^1.4.0" | ||||
|     prop-types "^15.6.2" | ||||
|     react-lifecycles-compat "^3.0.4" | ||||
|  | ||||
| react-transition-group@^4.3.0: | ||||
|   version "4.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683" | ||||
| @@ -10788,6 +10811,16 @@ unbox-primitive@^1.0.2: | ||||
|     has-symbols "^1.0.3" | ||||
|     which-boxed-primitive "^1.0.2" | ||||
|  | ||||
| uncontrollable@^7.2.1: | ||||
|   version "7.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" | ||||
|   integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== | ||||
|   dependencies: | ||||
|     "@babel/runtime" "^7.6.3" | ||||
|     "@types/react" ">=16.9.11" | ||||
|     invariant "^2.2.4" | ||||
|     react-lifecycles-compat "^3.0.4" | ||||
|  | ||||
| unicode-canonical-property-names-ecmascript@^2.0.0: | ||||
|   version "2.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" | ||||
| @@ -11062,7 +11095,7 @@ warning@^3.0.0: | ||||
|   dependencies: | ||||
|     loose-envify "^1.0.0" | ||||
|  | ||||
| warning@^4.0.0, warning@^4.0.1: | ||||
| warning@^4.0.1, warning@^4.0.3: | ||||
|   version "4.0.3" | ||||
|   resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" | ||||
|   integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== | ||||
|   | ||||
		Reference in New Issue
	
	Block a user