import React from 'react'; import PropTypes from 'prop-types'; import BlockStyleButtons from './BlockStyleButtons'; import InlineStyleButtons from './InlineStyleButtons'; import TableControls from './TableControls'; import StyleButton from './StyleButton'; import {SelectionButton} from './SelectionButton'; import {IconButton} from './IconButton'; import {ToolbarPopup} from './ToolbarPopup'; import {connect} from 'react-redux'; import {LinkToolbar} from '../links'; import classNames from 'classnames'; import * as actions from '../../actions'; import {PopupTypes, changeCase, undo, redo} from '../../actions'; import {getHighlightsConfig} from '../../highlightsConfig'; import {gettext} from 'core/utils'; interface IState { // When true, the toolbar is floating at the top of the item. This // helps the toolbar continue to be visible when it goes out of view // because of scrolling. floating: boolean; width: string | number; } /** * @ngdoc React * @module superdesk.core.editor3 * @name Toolbar * @param {boolean} disabled Disables clicking on the toolbar, if true. * @description Holds the editor's toolbar. */ class ToolbarComponent extends React.Component { static propTypes: any; static defaultProps: any; scrollContainer: any; toolbarNode: any; constructor(props) { super(props); this.scrollContainer = $(props.scrollContainer || window); this.onScroll = this.onScroll.bind(this); this.computeState = this.computeState.bind(this); this.state = this.computeState(); this.toolbarNode = React.createRef(); } computeState(): IState { const defaultState = { floating: false, width: 'auto', }; if (!this.props.editorNode || !this.toolbarNode) { return defaultState; } const editorRect = this.props.editorNode.getBoundingClientRect(); const pageRect = this.scrollContainer[0].getBoundingClientRect(); if (!editorRect || !pageRect) { return defaultState; } const isToolbarOut = editorRect.top < pageRect.top + 80; const isBottomOut = editorRect.bottom < pageRect.top + 70; const isContentLarger = this.props.editorNode.clientHeight < this.toolbarNode.current.clientHeight; const floating = !isContentLarger && isToolbarOut && !isBottomOut; const width = floating ? editorRect.width : 'auto'; return {floating, width}; } shouldComponentUpdate(nextProps) { if (nextProps.popup.type === 'NONE' && this.props.popup.type !== 'NONE') { // restoring editor focus after closing the popup const scrollableElement = document.querySelector('.page-content-container--scrollable'); setTimeout(() => { // wait for render const {editorWrapperElement} = this.props; if (editorWrapperElement instanceof Element !== true) { return; } const draftjsContentEditable = editorWrapperElement.querySelector('.public-DraftEditor-content'); if (draftjsContentEditable instanceof Element !== true) { return; } draftjsContentEditable.focus(); if (scrollableElement != null) { // prevent fousing from changing scroll position // eslint-disable-next-line no-self-assign scrollableElement.scrollTop = scrollableElement.scrollTop; // eslint-disable-next-line no-self-assign scrollableElement.scrollLeft = scrollableElement.scrollLeft; } }); } return true; } /** * @ngdoc method * @name Toolbar#onScroll * @description Triggered when the authoring page is scrolled. It adjusts toolbar * style, based on the location of the editor within the scroll container. */ onScroll(e) { this.setState(this.computeState()); } componentDidMount() { this.scrollContainer.on('scroll', this.onScroll); } componentWillUnmount() { this.scrollContainer.off('scroll', this.onScroll); } /* eslint-disable complexity */ render() { const {floating} = this.state; const { disabled, popup, editorFormat, activeCell, addTable, insertMedia, suggestingMode, toggleSuggestingMode, invisibles, toggleInvisibles, removeFormat, removeAllFormat, dispatch, } = this.props; const has = (opt) => editorFormat.indexOf(opt) > -1; const showPopup = (type) => (data) => this.props.showPopup(type, data); const cx = classNames({ 'Editor3-controls': true, 'floating-toolbar': floating, disabled: disabled && activeCell === null, }); return activeCell !== null ? : (
{/* Styles */} {/* Formatting options */} {has('link') && ( )} {has('embed') && ( )} {has('media') && ( )} {has('table') && ( )} {has('remove format') && ( )} {has('remove all format') && ( )} {has('comments') && ( )} {has('annotation') && ( )} {has('suggestions') && ( )} {has('formatting marks') && ( )} {has('uppercase') && ( dispatch(changeCase('uppercase', selection))} precondition={!suggestingMode} key="uppercase-button" iconName="to-uppercase" tooltip={gettext('Convert text to uppercase')} /> )} {has('lowercase') && ( dispatch(changeCase('lowercase', selection))} precondition={!suggestingMode} key="lowercase-button" iconName="to-lowercase" tooltip={gettext('Convert text to lowercase')} /> )} {has('undo') && ( { this.props.dispatch(undo()); }} tooltip={gettext('Undo') + ' (ctrl + z)'} iconName="undo" /> )} {has('redo') && ( { this.props.dispatch(redo()); }} tooltip={gettext('Redo') + ' (ctrl + y)'} iconName="redo" /> )} {/* LinkToolbar must be the last node. */}
); } } ToolbarComponent.propTypes = { disabled: PropTypes.bool, editorFormat: PropTypes.array, activeCell: PropTypes.any, suggestingMode: PropTypes.bool, invisibles: PropTypes.bool, addTable: PropTypes.func, insertMedia: PropTypes.func, showPopup: PropTypes.func, toggleSuggestingMode: PropTypes.func, toggleInvisibles: PropTypes.func, removeFormat: PropTypes.func, popup: PropTypes.object, editorState: PropTypes.object, editorNode: PropTypes.object, scrollContainer: PropTypes.string.isRequired, highlightsManager: PropTypes.object.isRequired, editorWrapperElement: PropTypes.object, }; const mapStateToProps = ({ editorFormat, activeCell, popup, editorState, suggestingMode, invisibles, }) => ({ editorFormat, activeCell, popup, editorState, suggestingMode, invisibles, }); const mapDispatchToProps = (dispatch) => ({ insertMedia: () => dispatch(actions.insertMedia()), showPopup: (type, data) => dispatch(actions.showPopup(type, data)), addTable: () => dispatch(actions.addTable()), toggleSuggestingMode: () => dispatch(actions.toggleSuggestingMode()), toggleInvisibles: () => dispatch(actions.toggleInvisibles()), removeFormat: () => dispatch(actions.removeFormat()), removeAllFormat: () => dispatch(actions.removeAllFormat()), dispatch: dispatch, }); const Toolbar = connect(mapStateToProps, mapDispatchToProps)(ToolbarComponent); export default Toolbar;