From 14028655df1b94a1722d6e329174947db45db477 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sun, 21 Apr 2019 17:17:10 +0200 Subject: Move composer Dropdown from features/composer to features/compose --- .../composer/options/dropdown/content/index.js | 146 -------- .../options/dropdown/content/item/index.js | 129 ------- .../features/composer/options/dropdown/index.js | 229 ------------- .../glitch/features/composer/options/index.js | 377 --------------------- 4 files changed, 881 deletions(-) delete mode 100644 app/javascript/flavours/glitch/features/composer/options/dropdown/content/index.js delete mode 100644 app/javascript/flavours/glitch/features/composer/options/dropdown/content/item/index.js delete mode 100644 app/javascript/flavours/glitch/features/composer/options/dropdown/index.js delete mode 100644 app/javascript/flavours/glitch/features/composer/options/index.js (limited to 'app/javascript/flavours/glitch/features/composer') diff --git a/app/javascript/flavours/glitch/features/composer/options/dropdown/content/index.js b/app/javascript/flavours/glitch/features/composer/options/dropdown/content/index.js deleted file mode 100644 index b76410561..000000000 --- a/app/javascript/flavours/glitch/features/composer/options/dropdown/content/index.js +++ /dev/null @@ -1,146 +0,0 @@ -// Package imports. -import PropTypes from 'prop-types'; -import React from 'react'; -import spring from 'react-motion/lib/spring'; - -// Components. -import ComposerOptionsDropdownContentItem from './item'; - -// Utils. -import { withPassive } from 'flavours/glitch/util/dom_helpers'; -import Motion from 'flavours/glitch/util/optional_motion'; -import { assignHandlers } from 'flavours/glitch/util/react_helpers'; - -// Handlers. -const handlers = { - // When the document is clicked elsewhere, we close the dropdown. - handleDocumentClick ({ target }) { - const { node } = this; - const { onClose } = this.props; - if (onClose && node && !node.contains(target)) { - onClose(); - } - }, - - // Stores our node in `this.node`. - handleRef (node) { - this.node = node; - }, -}; - -// The spring to use with our motion. -const springMotion = spring(1, { - damping: 35, - stiffness: 400, -}); - -// The component. -export default class ComposerOptionsDropdownContent extends React.PureComponent { - - // Constructor. - constructor (props) { - super(props); - assignHandlers(this, handlers); - - // Instance variables. - this.node = null; - - this.state = { - mounted: false, - }; - } - - // On mounting, we add our listeners. - componentDidMount () { - const { handleDocumentClick } = this.handlers; - document.addEventListener('click', handleDocumentClick, false); - document.addEventListener('touchend', handleDocumentClick, withPassive); - this.setState({ mounted: true }); - } - - // On unmounting, we remove our listeners. - componentWillUnmount () { - const { handleDocumentClick } = this.handlers; - document.removeEventListener('click', handleDocumentClick, false); - document.removeEventListener('touchend', handleDocumentClick, withPassive); - } - - // Rendering. - render () { - const { mounted } = this.state; - const { handleRef } = this.handlers; - const { - items, - onChange, - onClose, - style, - value, - } = this.props; - - // The result. - return ( - - {({ 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 -
- {items ? items.map( - ({ - name, - ...rest - }) => ( - - ) - ) : null} -
- )} -
- ); - } - -} - -// Props. -ComposerOptionsDropdownContent.propTypes = { - items: PropTypes.arrayOf(PropTypes.shape({ - icon: PropTypes.string, - meta: PropTypes.node, - name: PropTypes.string.isRequired, - on: PropTypes.bool, - text: PropTypes.node, - })), - onChange: PropTypes.func, - onClose: PropTypes.func, - style: PropTypes.object, - value: PropTypes.string, -}; - -// Default props. -ComposerOptionsDropdownContent.defaultProps = { style: {} }; diff --git a/app/javascript/flavours/glitch/features/composer/options/dropdown/content/item/index.js b/app/javascript/flavours/glitch/features/composer/options/dropdown/content/item/index.js deleted file mode 100644 index 68a52083f..000000000 --- a/app/javascript/flavours/glitch/features/composer/options/dropdown/content/item/index.js +++ /dev/null @@ -1,129 +0,0 @@ -// Package imports. -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import Toggle from 'react-toggle'; - -// Components. -import Icon from 'flavours/glitch/components/icon'; - -// Utils. -import { assignHandlers } from 'flavours/glitch/util/react_helpers'; - -// Handlers. -const handlers = { - - // This function activates the dropdown item. - handleActivate (e) { - const { - name, - onChange, - onClose, - options: { on }, - } = this.props; - - // If the escape key was pressed, we close the dropdown. - if (e.key === 'Escape' && onClose) { - onClose(); - - // Otherwise, we both close the dropdown and change the value. - } else if (onChange && (!e.key || e.key === 'Enter')) { - e.preventDefault(); // Prevents change in focus on click - if ((on === null || typeof on === 'undefined') && onClose) { - onClose(); - } - onChange(name); - } - }, -}; - -// The component. -export default class ComposerOptionsDropdownContentItem extends React.PureComponent { - - // Constructor. - constructor (props) { - super(props); - assignHandlers(this, handlers); - } - - // Rendering. - render () { - const { handleActivate } = this.handlers; - const { - active, - options: { - icon, - meta, - on, - text, - }, - } = this.props; - const computedClass = classNames('composer--options--dropdown--content--item', { - active, - lengthy: meta, - 'toggled-off': !on && on !== null && typeof on !== 'undefined', - 'toggled-on': on, - 'with-icon': icon, - }); - - // The result. - return ( -
- {function () { - - // We render a `` if we were provided an `on` - // property, and otherwise show an `` if available. - switch (true) { - case on !== null && typeof on !== 'undefined': - return ( - - ); - case !!icon: - return ( - - ); - default: - return null; - } - }()} - {meta ? ( -
- {text} - {meta} -
- ) : -
- {text} -
} -
- ); - } - -}; - -// Props. -ComposerOptionsDropdownContentItem.propTypes = { - active: PropTypes.bool, - name: PropTypes.string, - onChange: PropTypes.func, - onClose: PropTypes.func, - options: PropTypes.shape({ - icon: PropTypes.string, - meta: PropTypes.node, - on: PropTypes.bool, - text: PropTypes.node, - }), -}; diff --git a/app/javascript/flavours/glitch/features/composer/options/dropdown/index.js b/app/javascript/flavours/glitch/features/composer/options/dropdown/index.js deleted file mode 100644 index 7817cc964..000000000 --- a/app/javascript/flavours/glitch/features/composer/options/dropdown/index.js +++ /dev/null @@ -1,229 +0,0 @@ -// Package imports. -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; -import Overlay from 'react-overlays/lib/Overlay'; - -// Components. -import IconButton from 'flavours/glitch/components/icon_button'; -import ComposerOptionsDropdownContent from './content'; - -// Utils. -import { isUserTouching } from 'flavours/glitch/util/is_mobile'; -import { assignHandlers } from 'flavours/glitch/util/react_helpers'; - -// Handlers. -const handlers = { - - // Closes the dropdown. - handleClose () { - this.setState({ open: false }); - }, - - // The enter key toggles the dropdown's open state, and the escape - // key closes it. - handleKeyDown ({ key }) { - const { - handleClose, - handleToggle, - } = this.handlers; - switch (key) { - case 'Enter': - handleToggle(key); - break; - case 'Escape': - handleClose(); - break; - } - }, - - // Creates an action modal object. - handleMakeModal () { - const component = this; - const { - items, - onChange, - onModalOpen, - onModalClose, - value, - } = this.props; - - // Required props. - if (!(onChange && onModalOpen && onModalClose && items)) { - return null; - } - - // The object. - return { - actions: items.map( - ({ - name, - ...rest - }) => ({ - ...rest, - active: value && name === value, - name, - onClick (e) { - e.preventDefault(); // Prevents focus from changing - onModalClose(); - onChange(name); - }, - onPassiveClick (e) { - e.preventDefault(); // Prevents focus from changing - onChange(name); - component.setState({ needsModalUpdate: true }); - }, - }) - ), - }; - }, - - // Toggles opening and closing the dropdown. - handleToggle ({ target }) { - const { handleMakeModal } = this.handlers; - const { onModalOpen } = this.props; - const { open } = this.state; - - // If this is a touch device, we open a modal instead of the - // dropdown. - if (isUserTouching()) { - - // This gets the modal to open. - const modal = handleMakeModal(); - - // If we can, we then open the modal. - if (modal && onModalOpen) { - onModalOpen(modal); - return; - } - } - - const { top } = target.getBoundingClientRect(); - this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); - // Otherwise, we just set our state to open. - this.setState({ open: !open }); - }, - - // If our modal is open and our props update, we need to also update - // the modal. - handleUpdate () { - const { handleMakeModal } = this.handlers; - const { onModalOpen } = this.props; - const { needsModalUpdate } = this.state; - - // Gets our modal object. - const modal = handleMakeModal(); - - // Reopens the modal with the new object. - if (needsModalUpdate && modal && onModalOpen) { - onModalOpen(modal); - } - }, -}; - -// The component. -export default class ComposerOptionsDropdown extends React.PureComponent { - - // Constructor. - constructor (props) { - super(props); - assignHandlers(this, handlers); - this.state = { - needsModalUpdate: false, - open: false, - placement: 'bottom', - }; - } - - // Updates our modal as necessary. - componentDidUpdate (prevProps) { - const { handleUpdate } = this.handlers; - const { items } = this.props; - const { needsModalUpdate } = this.state; - if (needsModalUpdate && items.find( - (item, i) => item.on !== prevProps.items[i].on - )) { - handleUpdate(); - this.setState({ needsModalUpdate: false }); - } - } - - // Rendering. - render () { - const { - handleClose, - handleKeyDown, - handleToggle, - } = this.handlers; - const { - active, - disabled, - title, - icon, - items, - onChange, - value, - } = this.props; - const { open, placement } = this.state; - const computedClass = classNames('composer--options--dropdown', { - active, - open, - top: placement === 'top', - }); - - // The result. - return ( -
- - - - -
- ); - } - -} - -// Props. -ComposerOptionsDropdown.propTypes = { - active: PropTypes.bool, - disabled: PropTypes.bool, - icon: PropTypes.string, - items: PropTypes.arrayOf(PropTypes.shape({ - icon: PropTypes.string, - meta: PropTypes.node, - name: PropTypes.string.isRequired, - on: PropTypes.bool, - text: PropTypes.node, - })).isRequired, - onChange: PropTypes.func, - onModalClose: PropTypes.func, - onModalOpen: PropTypes.func, - title: PropTypes.string, - value: PropTypes.string, -}; diff --git a/app/javascript/flavours/glitch/features/composer/options/index.js b/app/javascript/flavours/glitch/features/composer/options/index.js deleted file mode 100644 index 7c7f01dc2..000000000 --- a/app/javascript/flavours/glitch/features/composer/options/index.js +++ /dev/null @@ -1,377 +0,0 @@ -// Package imports. -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { - FormattedMessage, - defineMessages, -} from 'react-intl'; -import spring from 'react-motion/lib/spring'; - -// Components. -import IconButton from 'flavours/glitch/components/icon_button'; -import TextIconButton from 'flavours/glitch/components/text_icon_button'; -import Dropdown from './dropdown'; - -// Utils. -import Motion from 'flavours/glitch/util/optional_motion'; -import { - assignHandlers, - hiddenComponent, -} from 'flavours/glitch/util/react_helpers'; -import { pollLimits } from 'flavours/glitch/util/initial_state'; - -// Messages. -const messages = defineMessages({ - advanced_options_icon_title: { - defaultMessage: 'Advanced options', - id: 'advanced_options.icon_title', - }, - attach: { - defaultMessage: 'Attach...', - id: 'compose.attach', - }, - change_privacy: { - defaultMessage: 'Adjust status privacy', - id: 'privacy.change', - }, - direct_long: { - defaultMessage: 'Post to mentioned users only', - id: 'privacy.direct.long', - }, - direct_short: { - defaultMessage: 'Direct', - id: 'privacy.direct.short', - }, - doodle: { - defaultMessage: 'Draw something', - id: 'compose.attach.doodle', - }, - local_only_long: { - defaultMessage: 'Do not post to other instances', - id: 'advanced_options.local-only.long', - }, - local_only_short: { - defaultMessage: 'Local-only', - id: 'advanced_options.local-only.short', - }, - private_long: { - defaultMessage: 'Post to followers only', - id: 'privacy.private.long', - }, - private_short: { - defaultMessage: 'Followers-only', - id: 'privacy.private.short', - }, - public_long: { - defaultMessage: 'Post to public timelines', - id: 'privacy.public.long', - }, - public_short: { - defaultMessage: 'Public', - id: 'privacy.public.short', - }, - sensitive: { - defaultMessage: 'Mark media as sensitive', - id: 'compose_form.sensitive', - }, - spoiler: { - defaultMessage: 'Hide text behind warning', - id: 'compose_form.spoiler', - }, - threaded_mode_long: { - defaultMessage: 'Automatically opens a reply on posting', - id: 'advanced_options.threaded_mode.long', - }, - threaded_mode_short: { - defaultMessage: 'Threaded mode', - id: 'advanced_options.threaded_mode.short', - }, - unlisted_long: { - defaultMessage: 'Do not show in public timelines', - id: 'privacy.unlisted.long', - }, - unlisted_short: { - defaultMessage: 'Unlisted', - id: 'privacy.unlisted.short', - }, - upload: { - defaultMessage: 'Upload a file', - id: 'compose.attach.upload', - }, - add_poll: { - defaultMessage: 'Add a poll', - id: 'poll_button.add_poll', - }, - remove_poll: { - defaultMessage: 'Remove poll', - id: 'poll_button.remove_poll', - }, -}); - -// Handlers. -const handlers = { - - // Handles file selection. - handleChangeFiles ({ target: { files } }) { - const { onUpload } = this.props; - if (files.length && onUpload) { - onUpload(files); - } - }, - - // Handles attachment clicks. - handleClickAttach (name) { - const { fileElement } = this; - const { onDoodleOpen } = this.props; - - // We switch over the name of the option. - switch (name) { - case 'upload': - if (fileElement) { - fileElement.click(); - } - return; - case 'doodle': - if (onDoodleOpen) { - onDoodleOpen(); - } - return; - } - }, - - // Handles a ref to the file input. - handleRefFileElement (fileElement) { - this.fileElement = fileElement; - }, -}; - -// The component. -export default class ComposerOptions extends React.PureComponent { - - // Constructor. - constructor (props) { - super(props); - assignHandlers(this, handlers); - - // Instance variables. - this.fileElement = null; - } - - // Rendering. - render () { - const { - handleChangeFiles, - handleClickAttach, - handleRefFileElement, - } = this.handlers; - const { - acceptContentTypes, - advancedOptions, - disabled, - allowMedia, - hasMedia, - allowPoll, - hasPoll, - intl, - onChangeAdvancedOption, - onChangeSensitivity, - onChangeVisibility, - onTogglePoll, - onModalClose, - onModalOpen, - onToggleSpoiler, - privacy, - resetFileKey, - sensitive, - spoiler, - } = this.props; - - // We predefine our privacy items so that we can easily pick the - // dropdown icon later. - const privacyItems = { - direct: { - icon: 'envelope', - meta: , - name: 'direct', - text: , - }, - private: { - icon: 'lock', - meta: , - name: 'private', - text: , - }, - public: { - icon: 'globe', - meta: , - name: 'public', - text: , - }, - unlisted: { - icon: 'unlock', - meta: , - name: 'unlisted', - text: , - }, - }; - - // The result. - return ( -
- - , - }, - { - icon: 'paint-brush', - name: 'doodle', - text: , - }, - ]} - onChange={handleClickAttach} - onModalClose={onModalClose} - onModalOpen={onModalOpen} - title={intl.formatMessage(messages.attach)} - /> - {!!pollLimits && ( - - )} - - {({ scale }) => ( -
- -
- )} -
-
- - {onToggleSpoiler && ( - - )} - !!value)} - disabled={disabled} - icon='ellipsis-h' - items={advancedOptions ? [ - { - meta: , - name: 'do_not_federate', - on: advancedOptions.get('do_not_federate'), - text: , - }, - { - meta: , - name: 'threaded_mode', - on: advancedOptions.get('threaded_mode'), - text: , - }, - ] : null} - onChange={onChangeAdvancedOption} - onModalClose={onModalClose} - onModalOpen={onModalOpen} - title={intl.formatMessage(messages.advanced_options_icon_title)} - /> -
- ); - } - -} - -// Props. -ComposerOptions.propTypes = { - acceptContentTypes: PropTypes.string, - advancedOptions: ImmutablePropTypes.map, - disabled: PropTypes.bool, - allowMedia: PropTypes.bool, - hasMedia: PropTypes.bool, - allowPoll: PropTypes.bool, - hasPoll: PropTypes.bool, - intl: PropTypes.object.isRequired, - onChangeAdvancedOption: PropTypes.func, - onChangeSensitivity: PropTypes.func, - onChangeVisibility: PropTypes.func, - onTogglePoll: PropTypes.func, - onDoodleOpen: PropTypes.func, - onModalClose: PropTypes.func, - onModalOpen: PropTypes.func, - onToggleSpoiler: PropTypes.func, - onUpload: PropTypes.func, - privacy: PropTypes.string, - resetFileKey: PropTypes.number, - sensitive: PropTypes.bool, - spoiler: PropTypes.bool, -}; -- cgit