UNPKG

@wordpress/components

Version:
333 lines (301 loc) 8.99 kB
/** * External dependencies */ import { Platform } from 'react-native'; import Clipboard from '@react-native-clipboard/clipboard'; /** * WordPress dependencies */ import { compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { isURL, prependHTTP } from '@wordpress/url'; import { useEffect, useState, useRef, useContext, useCallback, } from '@wordpress/element'; import { link, external } from '@wordpress/icons'; /** * Internal dependencies */ import BottomSheet from '../bottom-sheet'; import { BottomSheetContext } from '../bottom-sheet/bottom-sheet-context'; import PanelBody from '../../panel/body'; import TextControl from '../../text-control'; import ToggleControl from '../../toggle-control'; import FooterMessageControl from '../../footer-message-control'; import PanelActions from '../../panel/actions'; import LinkRelIcon from './link-rel'; import styles from './style.scss'; const NEW_TAB_REL = 'noreferrer noopener'; function LinkSettings( { // Control link settings `BottomSheet` visibility isVisible, // Callback that is called on closing bottom sheet onClose, // Function called to set attributes setAttributes, // Callback that is called when url input field is empty onEmptyURL, // Object of available options along with specific, customizable properties. // Available options keys: // * url - uses `TextControl` component to set `attributes.url` // * linkLabel - uses `TextControl` component to set `attributes.label` // * openInNewTab - uses `ToggleControl` component to set `attributes.linkTarget` and `attributes.rel` // * linkRel - uses `TextControl` component to set `attributes.rel` // * footer - uses `FooterMessageControl` component to display message, e.g. about missing functionality // Available properties: // * label - control component label, e.g. `Button Link URL` // * placeholder - control component placeholder, e.g. `Add URL` // * autoFocus (url only) - whether url input should be focused on sheet opening // * autoFill (url only) - whether url input should be filled with url from clipboard // Example: // const options = { // url: { // label: __( 'Button Link URL' ), // placeholder: __( 'Add URL' ), // autoFocus: true, // autoFill: true, // } // } options, // Specifies whether settings should be wrapped into `BottomSheet` withBottomSheet, // Defines buttons which will be displayed below the all options. // It's an array of objects with following properties: // * label - button title // * onPress - callback that is called on pressing button // Example: // const actions = [ // { // label: __( 'Remove link' ), // onPress: () => setAttributes({ url: '' }), // }, // ]; actions, // Specifies whether general `BottomSheet` is opened editorSidebarOpened, // Specifies whether icon should be displayed next to the label showIcon, onLinkCellPressed, urlValue, // Attributes properties url, label = '', linkTarget, rel = '', } ) { const [ urlInputValue, setUrlInputValue ] = useState( '' ); const [ labelInputValue, setLabelInputValue ] = useState( '' ); const [ linkRelInputValue, setLinkRelInputValue ] = useState( '' ); const onCloseSettingsSheetConsumed = useRef( false ); const prevEditorSidebarOpenedRef = useRef(); const { onHandleClosingBottomSheet } = useContext( BottomSheetContext ); useEffect( () => { if ( onHandleClosingBottomSheet ) { onHandleClosingBottomSheet( onCloseSettingsSheet ); } // See https://github.com/WordPress/gutenberg/pull/41166 }, [ urlInputValue, labelInputValue, linkRelInputValue ] ); useEffect( () => { prevEditorSidebarOpenedRef.current = editorSidebarOpened; } ); const prevEditorSidebarOpened = prevEditorSidebarOpenedRef.current; useEffect( () => { if ( url !== urlInputValue ) { setUrlInputValue( url || '' ); } // See https://github.com/WordPress/gutenberg/pull/41166 }, [ url ] ); useEffect( () => { setLabelInputValue( label || '' ); }, [ label ] ); useEffect( () => { setLinkRelInputValue( rel || '' ); }, [ rel ] ); useEffect( () => { const isSettingSheetOpen = isVisible || editorSidebarOpened; if ( isSettingSheetOpen ) { onCloseSettingsSheetConsumed.current = false; } if ( options.url.autoFill && isSettingSheetOpen && ! url ) { getURLFromClipboard(); } if ( prevEditorSidebarOpened && ! editorSidebarOpened ) { onSetAttributes(); } // See https://github.com/WordPress/gutenberg/pull/41166 }, [ editorSidebarOpened, isVisible ] ); useEffect( () => { if ( ! urlValue && onEmptyURL ) { onEmptyURL(); } if ( prependHTTP( urlValue ) !== url ) { setAttributes( { url: prependHTTP( urlValue ), } ); } // See https://github.com/WordPress/gutenberg/pull/41166 }, [ urlValue ] ); const onChangeURL = useCallback( ( value ) => { if ( ! value && onEmptyURL ) { onEmptyURL(); } setUrlInputValue( value ); }, [ onEmptyURL ] ); const onChangeLabel = useCallback( ( value ) => { setLabelInputValue( value ); }, [] ); const onSetAttributes = useCallback( () => { const newURL = prependHTTP( urlInputValue ); if ( url !== newURL || labelInputValue !== label || linkRelInputValue !== rel ) { setAttributes( { url: newURL, label: labelInputValue, rel: linkRelInputValue, } ); } // See https://github.com/WordPress/gutenberg/pull/41166 }, [ urlInputValue, labelInputValue, linkRelInputValue, setAttributes ] ); const onCloseSettingsSheet = useCallback( () => { if ( onCloseSettingsSheetConsumed.current ) { return; } onCloseSettingsSheetConsumed.current = true; onSetAttributes(); if ( onClose ) { onClose(); } }, [ onClose, onSetAttributes ] ); const onChangeOpenInNewTab = useCallback( ( value ) => { const newLinkTarget = value ? '_blank' : undefined; let updatedRel = linkRelInputValue; if ( newLinkTarget && ! linkRelInputValue ) { updatedRel = NEW_TAB_REL; } else if ( ! newLinkTarget && linkRelInputValue === NEW_TAB_REL ) { updatedRel = undefined; } setAttributes( { linkTarget: newLinkTarget, rel: updatedRel, } ); }, // See https://github.com/WordPress/gutenberg/pull/41166 [ linkRelInputValue ] ); const onChangeLinkRel = useCallback( ( value ) => { setLinkRelInputValue( value ); }, [] ); async function getURLFromClipboard() { const clipboardText = await Clipboard.getString(); if ( ! clipboardText ) { return; } // Check if pasted text is URL. if ( ! isURL( clipboardText ) ) { return; } setAttributes( { url: clipboardText } ); } function getSettings() { return ( <> { options.url && ( onLinkCellPressed ? ( <BottomSheet.LinkCell showIcon={ showIcon } value={ url } valueMask={ options.url.valueMask } onPress={ onLinkCellPressed } /> ) : ( <TextControl icon={ showIcon && link } label={ options.url.label } value={ urlInputValue } valuePlaceholder={ options.url.placeholder } onChange={ onChangeURL } onSubmit={ onCloseSettingsSheet } autoCapitalize="none" autoCorrect={ false } // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={ Platform.OS === 'ios' && options.url.autoFocus } keyboardType="url" /> ) ) } { options.linkLabel && ( <TextControl label={ options.linkLabel.label } value={ labelInputValue } valuePlaceholder={ options.linkLabel.placeholder } onChange={ onChangeLabel } /> ) } { !! urlInputValue && ( <> { options.openInNewTab && ( <ToggleControl icon={ showIcon && external } label={ options.openInNewTab.label } checked={ linkTarget === '_blank' } onChange={ onChangeOpenInNewTab } /> ) } { options.linkRel && ( <TextControl icon={ showIcon && LinkRelIcon } label={ options.linkRel.label } value={ linkRelInputValue } valuePlaceholder={ options.linkRel.placeholder } onChange={ onChangeLinkRel } onSubmit={ onCloseSettingsSheet } autoCapitalize="none" autoCorrect={ false } keyboardType="default" /> ) } </> ) } </> ); } if ( ! withBottomSheet ) { return getSettings(); } return ( <> <PanelBody style={ styles.linkSettingsPanel }> { getSettings() } </PanelBody> { options.footer && ( <PanelBody style={ styles.linkSettingsPanel }> <FooterMessageControl label={ options.footer.label } separatorType={ options.footer.separatorType } /> </PanelBody> ) } { actions && <PanelActions actions={ actions } /> } </> ); } export default compose( [ withSelect( ( select ) => { const { isEditorSidebarOpened } = select( 'core/edit-post' ); return { editorSidebarOpened: isEditorSidebarOpened(), }; } ), ] )( LinkSettings );