@wordpress/components
Version:
UI components for WordPress.
333 lines (301 loc) • 8.99 kB
JavaScript
/**
* 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 );