@wordpress/block-library
Version:
Block library for the WordPress editor.
523 lines (478 loc) • 15.3 kB
JavaScript
import { createElement } from "@wordpress/element";
/**
* External dependencies
*/
import { View, TouchableWithoutFeedback, Text } from 'react-native';
import Clipboard from '@react-native-clipboard/clipboard';
/**
* WordPress dependencies
*/
import { requestImageFailedRetryDialog, requestImageUploadCancelDialog, mediaUploadSync } from '@wordpress/react-native-bridge';
import { BlockIcon, MediaPlaceholder, MediaUploadProgress, RichText, PlainText, BlockControls, MediaUpload, InspectorControls, MEDIA_TYPE_ANY, store as blockEditorStore } from '@wordpress/block-editor';
import { ToolbarButton, ToolbarGroup, PanelBody, ToggleControl, TextControl, SelectControl, Icon } from '@wordpress/components';
import { file as icon, replace, button, external, link, warning } from '@wordpress/icons';
import { Component } from '@wordpress/element';
import { __, _x } from '@wordpress/i18n';
import { compose, withPreferredColorScheme } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import { getProtocol } from '@wordpress/url';
import { store as coreStore } from '@wordpress/core-data';
/**
* Internal dependencies
*/
import styles from './style.scss';
const URL_COPIED_NOTIFICATION_DURATION_MS = 1500;
const MIN_WIDTH = 40;
export class FileEdit extends Component {
constructor(props) {
super(props);
this.state = {
isUploadInProgress: false,
isSidebarLinkSettings: false,
placeholderTextWidth: 0,
maxWidth: 0
};
this.timerRef = null;
this.onLayout = this.onLayout.bind(this);
this.onSelectFile = this.onSelectFile.bind(this);
this.onChangeFileName = this.onChangeFileName.bind(this);
this.onChangeDownloadButtonText = this.onChangeDownloadButtonText.bind(this);
this.updateMediaProgress = this.updateMediaProgress.bind(this);
this.finishMediaUploadWithSuccess = this.finishMediaUploadWithSuccess.bind(this);
this.finishMediaUploadWithFailure = this.finishMediaUploadWithFailure.bind(this);
this.getFileComponent = this.getFileComponent.bind(this);
this.onChangeDownloadButtonVisibility = this.onChangeDownloadButtonVisibility.bind(this);
this.onCopyURL = this.onCopyURL.bind(this);
this.onChangeOpenInNewWindow = this.onChangeOpenInNewWindow.bind(this);
this.onChangeLinkDestinationOption = this.onChangeLinkDestinationOption.bind(this);
this.onShowLinkSettings = this.onShowLinkSettings.bind(this);
this.onFilePressed = this.onFilePressed.bind(this);
this.mediaUploadStateReset = this.mediaUploadStateReset.bind(this);
}
componentDidMount() {
const {
attributes,
setAttributes
} = this.props;
const {
downloadButtonText
} = attributes;
if (downloadButtonText === undefined || downloadButtonText === '') {
setAttributes({
downloadButtonText: _x('Download', 'button label')
});
}
if (attributes.id && attributes.url && getProtocol(attributes.url) === 'file:') {
mediaUploadSync();
}
}
componentWillUnmount() {
clearTimeout(this.timerRef);
}
componentDidUpdate(prevProps) {
if (prevProps.isSidebarOpened && !this.props.isSidebarOpened && this.state.isSidebarLinkSettings) {
this.setState({
isSidebarLinkSettings: false
});
}
}
onSelectFile(media) {
this.props.setAttributes({
href: media.url,
fileName: media.title,
textLinkHref: media.url,
id: media.id
});
}
onChangeFileName(fileName) {
this.props.setAttributes({
fileName
});
}
onChangeDownloadButtonText(downloadButtonText) {
this.props.setAttributes({
downloadButtonText
});
}
onChangeDownloadButtonVisibility(showDownloadButton) {
this.props.setAttributes({
showDownloadButton
});
}
onChangeLinkDestinationOption(newHref) {
// Choose Media File or Attachment Page (when file is in Media Library)
this.props.setAttributes({
textLinkHref: newHref
});
}
onCopyURL() {
if (this.state.isUrlCopied) {
return;
}
const {
href
} = this.props.attributes;
Clipboard.setString(href);
this.setState({
isUrlCopied: true
});
this.timerRef = setTimeout(() => {
this.setState({
isUrlCopied: false
});
}, URL_COPIED_NOTIFICATION_DURATION_MS);
}
onChangeOpenInNewWindow(newValue) {
this.props.setAttributes({
textLinkTarget: newValue ? '_blank' : false
});
}
updateMediaProgress(payload) {
const {
setAttributes
} = this.props;
if (payload.mediaUrl) {
setAttributes({
url: payload.mediaUrl
});
}
if (!this.state.isUploadInProgress) {
this.setState({
isUploadInProgress: true
});
}
}
finishMediaUploadWithSuccess(payload) {
const {
setAttributes
} = this.props;
setAttributes({
href: payload.mediaUrl,
id: payload.mediaServerId,
textLinkHref: payload.mediaUrl
});
this.setState({
isUploadInProgress: false
});
}
finishMediaUploadWithFailure(payload) {
this.props.setAttributes({
id: payload.mediaId
});
this.setState({
isUploadInProgress: false
});
}
mediaUploadStateReset() {
const {
setAttributes
} = this.props;
setAttributes({
id: null,
href: null,
textLinkHref: null,
fileName: null
});
this.setState({
isUploadInProgress: false
});
}
onShowLinkSettings() {
this.setState({
isSidebarLinkSettings: true
}, this.props.openSidebar);
}
getToolbarEditButton(open) {
return createElement(BlockControls, null, createElement(ToolbarGroup, null, createElement(ToolbarButton, {
title: __('Edit file'),
icon: replace,
onClick: open
}), createElement(ToolbarButton, {
title: __('Link To'),
icon: link,
onClick: this.onShowLinkSettings
})));
}
getInspectorControls(_ref, media, isUploadInProgress, isUploadFailed) {
let {
showDownloadButton,
textLinkTarget,
href,
textLinkHref
} = _ref;
let linkDestinationOptions = [{
value: href,
label: __('URL')
}];
const attachmentPage = media && media.link;
const {
isSidebarLinkSettings
} = this.state;
if (attachmentPage) {
linkDestinationOptions = [{
value: href,
label: __('Media file')
}, {
value: attachmentPage,
label: __('Attachment page')
}];
}
const actionButtonStyle = this.props.getStylesFromColorScheme(styles.actionButton, styles.actionButtonDark);
const isCopyUrlDisabled = isUploadFailed || isUploadInProgress;
const dimmedActionButtonStyle = this.props.getStylesFromColorScheme(styles.dimmedActionButton, styles.dimmedActionButtonDark);
const finalButtonStyle = isCopyUrlDisabled ? dimmedActionButtonStyle : actionButtonStyle;
return createElement(InspectorControls, null, isSidebarLinkSettings || createElement(PanelBody, {
title: __('File block settings')
}), createElement(PanelBody, null, createElement(SelectControl, {
icon: link,
label: __('Link to'),
value: textLinkHref,
onChange: this.onChangeLinkDestinationOption,
options: linkDestinationOptions,
hideCancelButton: true
}), createElement(ToggleControl, {
icon: external,
label: __('Open in new tab'),
checked: textLinkTarget === '_blank',
onChange: this.onChangeOpenInNewWindow
}), !isSidebarLinkSettings && createElement(ToggleControl, {
icon: button,
label: __('Show download button'),
checked: showDownloadButton,
onChange: this.onChangeDownloadButtonVisibility
}), createElement(TextControl, {
disabled: isCopyUrlDisabled,
label: this.state.isUrlCopied ? __('Copied!') : __('Copy file URL'),
labelStyle: this.state.isUrlCopied || finalButtonStyle,
onPress: this.onCopyURL
})));
}
getStyleForAlignment(align) {
const getFlexAlign = alignment => {
switch (alignment) {
case 'right':
return 'flex-end';
case 'center':
return 'center';
default:
return 'flex-start';
}
};
return {
alignSelf: getFlexAlign(align)
};
}
getTextAlignmentForAlignment(align) {
switch (align) {
case 'right':
return 'right';
case 'center':
return 'center';
default:
return 'left';
}
}
onFilePressed() {
const {
attributes
} = this.props;
if (this.state.isUploadInProgress) {
requestImageUploadCancelDialog(attributes.id);
} else if (attributes.id && getProtocol(attributes.href) === 'file:') {
requestImageFailedRetryDialog(attributes.id);
}
}
onLayout(_ref2) {
let {
nativeEvent
} = _ref2;
const {
width
} = nativeEvent.layout;
const {
paddingLeft,
paddingRight
} = styles.defaultButton;
this.setState({
maxWidth: width - (paddingLeft + paddingRight)
});
} // Render `Text` with `placeholderText` styled as a placeholder
// to calculate its width which then is set as a `minWidth`
// This should be fixed on RNAztec level. In the mean time,
// We use the same strategy implemented in Button block.
getPlaceholderWidth(placeholderText) {
const {
maxWidth,
placeholderTextWidth
} = this.state;
return createElement(Text, {
style: styles.placeholder,
onTextLayout: _ref3 => {
let {
nativeEvent
} = _ref3;
const textWidth = nativeEvent.lines[0] && nativeEvent.lines[0].width;
if (textWidth && textWidth !== placeholderTextWidth) {
this.setState({
placeholderTextWidth: Math.min(textWidth, maxWidth)
});
}
}
}, placeholderText);
}
getFileComponent(openMediaOptions, getMediaOptions) {
const {
attributes,
media,
isSelected
} = this.props;
const {
isButtonFocused,
placeholderTextWidth
} = this.state;
const {
fileName,
downloadButtonText,
id,
showDownloadButton,
align
} = attributes;
const minWidth = isButtonFocused || !isButtonFocused && downloadButtonText && downloadButtonText !== '' ? MIN_WIDTH : placeholderTextWidth;
const placeholderText = isButtonFocused || !isButtonFocused && downloadButtonText && downloadButtonText !== '' ? '' : __('Add text…');
return createElement(MediaUploadProgress, {
mediaId: id,
onUpdateMediaProgress: this.updateMediaProgress,
onFinishMediaUploadWithSuccess: this.finishMediaUploadWithSuccess,
onFinishMediaUploadWithFailure: this.finishMediaUploadWithFailure,
onMediaUploadStateReset: this.mediaUploadStateReset,
renderContent: _ref4 => {
let {
isUploadInProgress,
isUploadFailed
} = _ref4;
const dimmedStyle = (this.state.isUploadInProgress || isUploadFailed) && styles.disabledButton;
const finalButtonStyle = [styles.defaultButton, dimmedStyle];
const errorIconStyle = Object.assign({}, styles.errorIcon, styles.uploadFailed);
return createElement(TouchableWithoutFeedback, {
accessible: !isSelected,
onPress: this.onFilePressed,
disabled: !isSelected
}, createElement(View, {
onLayout: this.onLayout,
testID: "file-edit-container"
}, this.getPlaceholderWidth(placeholderText), isUploadInProgress || this.getToolbarEditButton(openMediaOptions), getMediaOptions(), isSelected && this.getInspectorControls(attributes, media, isUploadInProgress, isUploadFailed), createElement(View, {
style: styles.container
}, createElement(RichText, {
withoutInteractiveFormatting: true,
__unstableMobileNoFocusOnMount: true,
onChange: this.onChangeFileName,
placeholder: __('File name'),
rootTagsToEliminate: ['p'],
tagName: "p",
underlineColorAndroid: "transparent",
value: fileName,
deleteEnter: true,
textAlign: this.getTextAlignmentForAlignment(align)
}), isUploadFailed && createElement(View, {
style: styles.errorContainer
}, createElement(Icon, {
icon: warning,
style: errorIconStyle
}), createElement(PlainText, {
editable: false,
value: __('Error'),
style: styles.uploadFailed
}))), showDownloadButton && this.state.maxWidth > 0 && createElement(View, {
style: [finalButtonStyle, this.getStyleForAlignment(align)]
}, createElement(RichText, {
withoutInteractiveFormatting: true,
__unstableMobileNoFocusOnMount: true,
rootTagsToEliminate: ['p'],
tagName: "p",
textAlign: "center",
minWidth: minWidth,
maxWidth: this.state.maxWidth,
deleteEnter: true,
style: styles.buttonText,
value: downloadButtonText,
placeholder: placeholderText,
unstableOnFocus: () => this.setState({
isButtonFocused: true
}),
onBlur: () => this.setState({
isButtonFocused: false
}),
selectionColor: styles.buttonText.color,
placeholderTextColor: styles.placeholderTextColor.color,
underlineColorAndroid: "transparent",
onChange: this.onChangeDownloadButtonText
}))));
}
});
}
render() {
const {
attributes,
wasBlockJustInserted,
isSelected
} = this.props;
const {
href
} = attributes;
if (!href) {
return createElement(MediaPlaceholder, {
icon: createElement(BlockIcon, {
icon: icon
}),
labels: {
title: __('File'),
instructions: __('CHOOSE A FILE')
},
onSelect: this.onSelectFile,
onFocus: this.props.onFocus,
allowedTypes: [MEDIA_TYPE_ANY],
autoOpenMediaUpload: isSelected && wasBlockJustInserted
});
}
return createElement(MediaUpload, {
allowedTypes: [MEDIA_TYPE_ANY],
isReplacingMedia: true,
onSelect: this.onSelectFile,
render: _ref5 => {
let {
open,
getMediaOptions
} = _ref5;
return this.getFileComponent(open, getMediaOptions);
}
});
}
}
export default compose([withSelect((select, props) => {
const {
attributes,
isSelected,
clientId
} = props;
const {
id,
href
} = attributes;
const {
isEditorSidebarOpened
} = select('core/edit-post');
const isNotFileHref = id && getProtocol(href) !== 'file:';
return {
media: isNotFileHref ? select(coreStore).getMedia(id) : undefined,
isSidebarOpened: isSelected && isEditorSidebarOpened(),
wasBlockJustInserted: select(blockEditorStore).wasBlockJustInserted(clientId, 'inserter_menu')
};
}), withDispatch(dispatch => {
const {
openGeneralSidebar
} = dispatch('core/edit-post');
return {
openSidebar: () => openGeneralSidebar('edit-post/block')
};
}), withPreferredColorScheme])(FileEdit);
//# sourceMappingURL=edit.native.js.map