This commit is contained in:
mgabdev
2020-02-28 10:20:47 -05:00
parent 0bd1eb2c77
commit 3ca4ffcc6b
77 changed files with 6110 additions and 1427 deletions

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class ContentWarningPopover extends PureComponent {
render() {
return (
<div>

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class DatePickerPopover extends PureComponent {
render() {
return (
<div>

View File

@@ -0,0 +1,12 @@
import PopoverLayout from './popover_layout'
import Text from '../text'
export default class UserInfoPopover extends PureComponent {
render() {
return (
<PopoverLayout>
<Text>testing</Text>
</PopoverLayout>
)
}
}

View File

@@ -1,9 +0,0 @@
export default class Popover extends PureComponent {
render() {
return (
<div>
{ /* */ }
</div>
)
}
}

View File

@@ -0,0 +1,146 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import classnames from 'classnames/bind'
import Overlay from 'react-overlays/lib/Overlay'
import spring from 'react-motion/lib/spring'
import Motion from '../../features/ui/util/optional_motion'
import { openPopover, closePopover } from '../../actions/popover'
import { openModal, closeModal } from '../../actions/modal'
import { isUserTouching } from '../../utils/is_mobile'
const cx = classnames.bind(_s)
let id = 0
const mapStateToProps = state => ({
isModalOpen: state.get('modal').modalType === 'ACTIONS',
popoverPlacement: state.getIn(['popover', 'placement']),
openPopoverType: state.getIn(['popover', 'popoverType']),
openedViaKeyboard: state.getIn(['popover', 'keyboard']),
})
const mapDispatchToProps = (dispatch, { status, items }) => ({
onOpen(id, onItemClick, popoverPlacement, keyboard) {
dispatch(isUserTouching() ? openModal('ACTIONS', {
status,
actions: items,
onClick: onItemClick,
}) : openPopover(id, popoverPlacement, keyboard))
},
onClose(id) {
dispatch(closeModal())
dispatch(closePopover(id))
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
class PopoverBase extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
icon: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
size: PropTypes.number.isRequired,
title: PropTypes.string,
disabled: PropTypes.bool,
status: ImmutablePropTypes.map,
isUserTouching: PropTypes.func,
isModalOpen: PropTypes.bool.isRequired,
onOpen: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
popoverPlacement: PropTypes.string,
openPopoverType: PropTypes.number,
openedViaKeyboard: PropTypes.bool,
visible: PropTypes.bool,
}
static defaultProps = {
title: 'Menu',
}
state = {
id: id++,
}
handleClick = ({ target, type }) => {
if (this.state.id === this.props.openPopoverType) {
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')
}
}
handleClose = () => {
this.props.onClose(this.state.id)
}
handleKeyDown = e => {
switch (e.key) {
case ' ':
case 'Enter':
this.handleClick(e)
e.preventDefault()
break
case 'Escape':
this.handleClose()
break
}
}
handleItemClick = e => {
const i = Number(e.currentTarget.getAttribute('data-index'))
const { action, to } = this.props.items[i]
this.handleClose()
if (typeof action === 'function') {
e.preventDefault()
action()
} else if (to) {
e.preventDefault()
this.context.router.history.push(to)
}
}
setTargetRef = c => {
this.target = c
}
findTarget = () => {
return this.target
}
componentWillUnmount = () => {
if (this.state.id === this.props.openPopoverType) {
this.handleClose()
}
}
render() {
const { icon, children, visible, items, size, title, disabled, popoverPlacement, openPopoverType, openedViaKeyboard } = this.props
const open = this.state.id === openPopoverType
const containerClasses = cx({
default: 1,
z4: 1,
displayNone: !visible,
})
return (
<div onKeyDown={this.handleKeyDown} className={containerClasses}>
<div show={open} placement={popoverPlacement} target={this.findTarget}>
{ /* <PopoverMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} /> */}
{children}
</div>
</div>
)
}
}

View File

@@ -1,8 +1,14 @@
export default class Popover extends PureComponent {
import Block from '../block'
export default class PopoverLayout extends PureComponent {
render() {
const { children } = this.props
return (
<div>
{ /* */ }
<Block>
{children}
</Block>
</div>
)
}

View File

@@ -0,0 +1,171 @@
import detectPassiveEvents from 'detect-passive-events'
import { closePopover } from '../../actions/popover'
import Bundle from '../../features/ui/util/bundle'
import BundleModalError from '../bundle_modal_error'
import PopoverBase from './popover_base'
import ContentWarningPopover from './content_warning_popover'
import DatePickerPopover from './date_picker_popover'
import GroupInfoPopover from './group_info_popover'
import ProfileOptionsPopover from './profile_options_popover'
import SearchPopover from './search_popover'
import SidebarMorePopover from './sidebar_more_popover'
import StatusOptionsPopover from './status_options_popover'
import UserInfoPopover from './user_info_popover'
import StatusVisibilityPopover from './status_visibility_popover'
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
const POPOVER_COMPONENTS = {
CONTENT_WARNING: () => Promise.resolve({ default: ContentWarningPopover }),
DATE_PICKER: () => Promise.resolve({ default: DatePickerPopover }),
GROUP_INFO: () => GroupInfoPopover,
PROFILE_OPTIONS: () => ProfileOptionsPopover,
SEARCH: () => Promise.resolve({ default: SearchPopover }),
SIDEBAR_MORE: () => Promise.resolve({ default: SidebarMorePopover }),
STATUS_OPTIONS: () => Promise.resolve({ default: StatusOptionsPopover }),
STATUS_VISIBILITY: () => Promise.resolve({ default: StatusVisibilityPopover }),
USER_INFO: () => UserInfoPopover,
}
const mapStateToProps = state => ({
type: state.get('popover').popoverType,
props: state.get('popover').popoverProps,
})
const mapDispatchToProps = (dispatch) => ({
onClose(optionalType) {
//
dispatch(closePopover())
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
class PopoverRoot extends PureComponent {
static propTypes = {
type: PropTypes.string,
props: PropTypes.object,
onClose: PropTypes.func.isRequired,
}
getSnapshotBeforeUpdate() {
return { visible: !!this.props.type }
}
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
onClose: PropTypes.func.isRequired,
style: PropTypes.object,
placement: PropTypes.string,
openedViaKeyboard: PropTypes.bool,
}
static defaultProps = {
style: {},
placement: 'bottom',
}
state = {
mounted: false,
}
handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) {
this.props.onClose()
}
}
componentDidMount() {
document.addEventListener('click', this.handleDocumentClick, false)
document.addEventListener('keydown', this.handleKeyDown, false)
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions)
if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus()
this.setState({ mounted: true })
}
componentWillUnmount() {
document.removeEventListener('click', this.handleDocumentClick, false)
document.removeEventListener('keydown', this.handleKeyDown, false)
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
}
setRef = c => {
this.node = c
}
setFocusRef = c => {
this.focusedItem = c
}
handleKeyDown = e => {
const items = Array.from(this.node.getElementsByTagName('a'))
const index = items.indexOf(document.activeElement)
let element
switch (e.key) {
case 'ArrowDown':
element = items[index + 1]
if (element) element.focus()
break
case 'ArrowUp':
element = items[index - 1]
if (element) element.focus()
break
case 'Home':
element = items[0]
if (element) element.focus()
break
case 'End':
element = items[items.length - 1]
if (element) element.focus()
break
}
}
handleItemKeyDown = e => {
if (e.key === 'Enter') {
this.handleClick(e)
}
}
handleClick = e => {
const i = Number(e.currentTarget.getAttribute('data-index'))
const { action, to } = this.props.items[i]
this.props.onClose()
if (typeof action === 'function') {
e.preventDefault()
action(e)
} else if (to) {
e.preventDefault()
this.context.router.history.push(to)
}
}
renderError = (props) => {
return <BundleModalError />
}
render() {
const { type, style, placement } = this.props
const { mounted } = this.state
const visible = !!type
console.log("popover root - type, visible:", type, visible)
return (
<PopoverBase className={`popover-menu ${placement}`} visible={visible} ref={this.setRef}>
{
visible &&
<UserInfoPopover />
}
</PopoverBase>
)
}
}

View File

@@ -0,0 +1,12 @@
import PopoverLayout from './popover_layout'
import Text from '../text'
export default class UserInfoPopover extends PureComponent {
render() {
return (
<PopoverLayout>
<Text>testing</Text>
</PopoverLayout>
)
}
}

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class SearchPopover extends PureComponent {
render() {
return (
<div>

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class SidebarMorePopover extends PureComponent {
render() {
return (
<div>

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class StatusOptionsPopover extends PureComponent {
render() {
return (
<div>

View File

@@ -1,4 +1,4 @@
export default class Popover extends PureComponent {
export default class StatusVisibilityPopover extends PureComponent {
render() {
return (
<div>

View File

@@ -0,0 +1,12 @@
import PopoverLayout from './popover_layout'
import Text from '../text'
export default class UserInfoPopover extends PureComponent {
render() {
return (
<PopoverLayout>
<Text>testing</Text>
</PopoverLayout>
)
}
}

View File

@@ -1,9 +0,0 @@
export default class Popover extends PureComponent {
render() {
return (
<div>
{ /* */ }
</div>
)
}
}