Compose buttons bar redesign + generalize dropdown (#194)
* Generalize compose dropdown for re-use * wip stuffs * new tootbox look and removed old doodle button files * use the house icon for ...master
parent
a7be86e875
commit
d589dd7cd0
@ -0,0 +1,133 @@ |
||||
// Package imports //
|
||||
import React from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
import { connect } from 'react-redux'; |
||||
import { injectIntl, defineMessages } from 'react-intl'; |
||||
|
||||
// Our imports //
|
||||
import ComposeDropdown from '../dropdown/index'; |
||||
import { uploadCompose } from '../../../../mastodon/actions/compose'; |
||||
import ImmutablePropTypes from 'react-immutable-proptypes'; |
||||
import ImmutablePureComponent from 'react-immutable-pure-component'; |
||||
import { openModal } from '../../../../mastodon/actions/modal'; |
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
|
||||
const messages = defineMessages({ |
||||
upload : |
||||
{ id: 'compose.attach.upload', defaultMessage: 'Upload a file' }, |
||||
doodle : |
||||
{ id: 'compose.attach.doodle', defaultMessage: 'Draw something' }, |
||||
attach : |
||||
{ id: 'compose.attach', defaultMessage: 'Attach...' }, |
||||
}); |
||||
|
||||
const mapStateToProps = state => ({ |
||||
// This horrible expression is copied from vanilla upload_button_container
|
||||
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')), |
||||
resetFileKey: state.getIn(['compose', 'resetFileKey']), |
||||
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']), |
||||
}); |
||||
|
||||
const mapDispatchToProps = dispatch => ({ |
||||
onSelectFile (files) { |
||||
dispatch(uploadCompose(files)); |
||||
}, |
||||
onOpenDoodle () { |
||||
dispatch(openModal('DOODLE', { noEsc: true })); |
||||
}, |
||||
}); |
||||
|
||||
@injectIntl |
||||
@connect(mapStateToProps, mapDispatchToProps) |
||||
export default class ComposeAttachOptions extends ImmutablePureComponent { |
||||
|
||||
static propTypes = { |
||||
intl : PropTypes.object.isRequired, |
||||
resetFileKey: PropTypes.number, |
||||
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired, |
||||
disabled: PropTypes.bool, |
||||
onSelectFile: PropTypes.func.isRequired, |
||||
onOpenDoodle: PropTypes.func.isRequired, |
||||
}; |
||||
|
||||
handleItemClick = bt => { |
||||
if (bt === 'upload') { |
||||
this.fileElement.click(); |
||||
} |
||||
|
||||
if (bt === 'doodle') { |
||||
this.props.onOpenDoodle(); |
||||
} |
||||
|
||||
this.dropdown.setState({ open: false }); |
||||
}; |
||||
|
||||
handleFileChange = (e) => { |
||||
if (e.target.files.length > 0) { |
||||
this.props.onSelectFile(e.target.files); |
||||
} |
||||
} |
||||
|
||||
setFileRef = (c) => { |
||||
this.fileElement = c; |
||||
} |
||||
|
||||
setDropdownRef = (c) => { |
||||
this.dropdown = c; |
||||
} |
||||
|
||||
render () { |
||||
const { intl, resetFileKey, disabled, acceptContentTypes } = this.props; |
||||
|
||||
const options = [ |
||||
{ icon: 'cloud-upload', text: messages.upload, name: 'upload' }, |
||||
{ icon: 'paint-brush', text: messages.doodle, name: 'doodle' }, |
||||
]; |
||||
|
||||
const optionElems = options.map((item) => { |
||||
const hdl = () => this.handleItemClick(item.name); |
||||
return ( |
||||
<div |
||||
role='button' |
||||
tabIndex='0' |
||||
key={item.name} |
||||
onClick={hdl} |
||||
className='privacy-dropdown__option' |
||||
> |
||||
<div className='privacy-dropdown__option__icon'> |
||||
<i className={`fa fa-fw fa-${item.icon}`} /> |
||||
</div> |
||||
|
||||
<div className='privacy-dropdown__option__content'> |
||||
<strong>{intl.formatMessage(item.text)}</strong> |
||||
</div> |
||||
</div> |
||||
); |
||||
}); |
||||
|
||||
return ( |
||||
<div> |
||||
<ComposeDropdown |
||||
title={intl.formatMessage(messages.attach)} |
||||
icon='paperclip' |
||||
disabled={disabled} |
||||
ref={this.setDropdownRef} |
||||
> |
||||
{optionElems} |
||||
</ComposeDropdown> |
||||
<input |
||||
key={resetFileKey} |
||||
ref={this.setFileRef} |
||||
type='file' |
||||
multiple={false} |
||||
accept={acceptContentTypes.toArray().join(',')} |
||||
onChange={this.handleFileChange} |
||||
disabled={disabled} |
||||
style={{ display: 'none' }} |
||||
/> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,77 @@ |
||||
// Package imports //
|
||||
import React from 'react'; |
||||
import PropTypes from 'prop-types'; |
||||
|
||||
// Mastodon imports //
|
||||
import IconButton from '../../../../mastodon/components/icon_button'; |
||||
|
||||
const iconStyle = { |
||||
height : null, |
||||
lineHeight : '27px', |
||||
}; |
||||
|
||||
export default class ComposeDropdown extends React.PureComponent { |
||||
|
||||
static propTypes = { |
||||
title: PropTypes.string.isRequired, |
||||
icon: PropTypes.string, |
||||
highlight: PropTypes.bool, |
||||
disabled: PropTypes.bool, |
||||
children: PropTypes.arrayOf(PropTypes.node).isRequired, |
||||
}; |
||||
|
||||
state = { |
||||
open: false, |
||||
}; |
||||
|
||||
onGlobalClick = (e) => { |
||||
if (e.target !== this.node && !this.node.contains(e.target) && this.state.open) { |
||||
this.setState({ open: false }); |
||||
} |
||||
}; |
||||
|
||||
componentDidMount () { |
||||
window.addEventListener('click', this.onGlobalClick); |
||||
window.addEventListener('touchstart', this.onGlobalClick); |
||||
} |
||||
componentWillUnmount () { |
||||
window.removeEventListener('click', this.onGlobalClick); |
||||
window.removeEventListener('touchstart', this.onGlobalClick); |
||||
} |
||||
|
||||
onToggleDropdown = () => { |
||||
if (this.props.disabled) return; |
||||
this.setState({ open: !this.state.open }); |
||||
}; |
||||
|
||||
setRef = (c) => { |
||||
this.node = c; |
||||
}; |
||||
|
||||
render () { |
||||
const { open } = this.state; |
||||
let { highlight, title, icon, disabled } = this.props; |
||||
|
||||
if (!icon) icon = 'ellipsis-h'; |
||||
|
||||
return ( |
||||
<div ref={this.setRef} className={`advanced-options-dropdown ${open ? 'open' : ''} ${highlight ? 'active' : ''} `}> |
||||
<div className='advanced-options-dropdown__value'> |
||||
<IconButton |
||||
className={'inverted'} |
||||
title={title} |
||||
icon={icon} active={open || highlight} |
||||
size={18} |
||||
style={iconStyle} |
||||
disabled={disabled} |
||||
onClick={this.onToggleDropdown} |
||||
/> |
||||
</div> |
||||
<div className='advanced-options-dropdown__dropdown'> |
||||
{this.props.children} |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
@ -1,41 +0,0 @@ |
||||
import React from 'react'; |
||||
import IconButton from '../../../components/icon_button'; |
||||
import PropTypes from 'prop-types'; |
||||
import { defineMessages, injectIntl } from 'react-intl'; |
||||
import ImmutablePureComponent from 'react-immutable-pure-component'; |
||||
|
||||
const messages = defineMessages({ |
||||
doodle: { id: 'doodle_button.label', defaultMessage: 'Add a drawing' }, |
||||
}); |
||||
|
||||
const iconStyle = { |
||||
height: null, |
||||
lineHeight: '27px', |
||||
}; |
||||
|
||||
@injectIntl |
||||
export default class UploadButton extends ImmutablePureComponent { |
||||
|
||||
static propTypes = { |
||||
disabled: PropTypes.bool, |
||||
onOpenCanvas: PropTypes.func.isRequired, |
||||
style: PropTypes.object, |
||||
intl: PropTypes.object.isRequired, |
||||
}; |
||||
|
||||
handleClick = () => { |
||||
this.props.onOpenCanvas(); |
||||
} |
||||
|
||||
render () { |
||||
|
||||
const { intl, disabled } = this.props; |
||||
|
||||
return ( |
||||
<div className='compose-form__upload-button'> |
||||
<IconButton icon='pencil' title={intl.formatMessage(messages.doodle)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} /> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
@ -1,15 +0,0 @@ |
||||
import { connect } from 'react-redux'; |
||||
import DoodleButton from '../components/doodle_button'; |
||||
import { openModal } from '../../../actions/modal'; |
||||
|
||||
const mapStateToProps = state => ({ |
||||
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')), |
||||
}); |
||||
|
||||
const mapDispatchToProps = dispatch => ({ |
||||
onOpenCanvas () { |
||||
dispatch(openModal('DOODLE', { noEsc: true })); |
||||
}, |
||||
}); |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DoodleButton); |
Loading…
Reference in new issue