@wordpress/block-library
Version:
Block library for the WordPress editor.
527 lines (467 loc) • 17.4 kB
JavaScript
var _styles$button, _styles$button2, _styles$button3;
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import { View, AccessibilityInfo, Platform, Text } from 'react-native';
/**
* WordPress dependencies
*/
import { useCallback, useEffect, useState, useRef } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
import { RichText, InspectorControls, BlockControls, store as blockEditorStore, getColorObjectByAttributeValues, getGradientValueBySlug, __experimentalGetColorClassesAndStyles as getColorClassesAndStyles } from '@wordpress/block-editor';
import { PanelBody, ToolbarGroup, ToolbarButton, LinkSettingsNavigation, UnitControl, getValueAndUnit, BottomSheetSelectControl, CSS_UNITS, filterUnitsWithSettings, useMobileGlobalStylesColors } from '@wordpress/components';
import { link } from '@wordpress/icons';
import { store as editPostStore } from '@wordpress/edit-post';
/**
* Internal dependencies
*/
import richTextStyle from './rich-text.scss';
import styles from './editor.scss';
import ColorBackground from './color-background';
const MIN_BORDER_RADIUS_VALUE = 0;
const MAX_BORDER_RADIUS_VALUE = 50;
const INITIAL_MAX_WIDTH = 108;
const MIN_WIDTH = 40; // Map of the percentage width to pixel subtraction that make the buttons fit nicely into columns.
const MIN_WIDTH_MARGINS = {
100: 0,
75: (_styles$button = styles.button75) === null || _styles$button === void 0 ? void 0 : _styles$button.marginLeft,
50: (_styles$button2 = styles.button50) === null || _styles$button2 === void 0 ? void 0 : _styles$button2.marginLeft,
25: (_styles$button3 = styles.button25) === null || _styles$button3 === void 0 ? void 0 : _styles$button3.marginLeft
};
function WidthPanel(_ref) {
let {
selectedWidth,
setAttributes
} = _ref;
function handleChange(newWidth) {
// Check if we are toggling the width off
let width = selectedWidth === newWidth ? undefined : newWidth;
if (newWidth === 'auto') {
width = undefined;
} // Update attributes.
setAttributes({
width
});
}
const options = [{
value: 'auto',
label: __('Auto')
}, {
value: 25,
label: '25%'
}, {
value: 50,
label: '50%'
}, {
value: 75,
label: '75%'
}, {
value: 100,
label: '100%'
}];
if (!selectedWidth) {
selectedWidth = 'auto';
}
return createElement(PanelBody, {
title: __('Width Settings')
}, createElement(BottomSheetSelectControl, {
label: __('Button width'),
value: selectedWidth,
onChange: handleChange,
options: options
}));
}
function ButtonEdit(props) {
var _props$attributes, _props$attributes$sty, _props$attributes$sty2, _buttonStyle$border;
const {
isSelected,
parentWidth
} = props;
const initialBorderRadius = props === null || props === void 0 ? void 0 : (_props$attributes = props.attributes) === null || _props$attributes === void 0 ? void 0 : (_props$attributes$sty = _props$attributes.style) === null || _props$attributes$sty === void 0 ? void 0 : (_props$attributes$sty2 = _props$attributes$sty.border) === null || _props$attributes$sty2 === void 0 ? void 0 : _props$attributes$sty2.radius;
const {
valueUnit = 'px'
} = getValueAndUnit(initialBorderRadius) || {};
const {
editorSidebarOpened,
numOfButtons
} = useSelect(select => {
const {
isEditorSidebarOpened
} = select(editPostStore);
const {
getBlockCount,
getBlockRootClientId
} = select(blockEditorStore);
const parentId = getBlockRootClientId(clientId);
const blockCount = getBlockCount(parentId);
const currentIsEditorSidebarOpened = isEditorSidebarOpened();
return {
editorSidebarOpened: isSelected && currentIsEditorSidebarOpened,
numOfButtons: blockCount
};
}, [clientId, isSelected]);
const {
closeGeneralSidebar
} = useDispatch(editPostStore);
const [maxWidth, setMaxWidth] = useState(INITIAL_MAX_WIDTH);
const [isLinkSheetVisible, setIsLinkSheetVisible] = useState(false);
const [isButtonFocused, setIsButtonFocused] = useState(true);
const [placeholderTextWidth, setPlaceholderTextWidth] = useState(0);
const [borderRadiusUnit, setBorderRadiusUnit] = useState(valueUnit);
const richTextRef = useRef();
const colors = useMobileGlobalStylesColors();
const gradients = useMobileGlobalStylesColors('gradients');
useEffect(() => {
if (isSelected) {
onToggleButtonFocus(true);
}
}, [isSelected]);
useEffect(() => {
onSetMaxWidth(null, true);
}, [parentWidth]);
useEffect(() => {
// Blur `RichText` on Android when link settings sheet or button settings sheet is opened,
// to avoid flashing caret after closing one of them
const richText = richTextRef === null || richTextRef === void 0 ? void 0 : richTextRef.current;
if (Platform.OS === 'android' && richText) {
if (editorSidebarOpened || isLinkSheetVisible) {
richText.blur();
onToggleButtonFocus(false);
} else {
onToggleButtonFocus(true);
}
}
}, [editorSidebarOpened, isLinkSheetVisible]);
useEffect(() => {
if (richTextRef !== null && richTextRef !== void 0 && richTextRef.current) {
if (!isSelected && isButtonFocused) {
onToggleButtonFocus(false);
}
if (isSelected && !isButtonFocused) {
AccessibilityInfo.isScreenReaderEnabled().then(enabled => {
if (enabled) {
onToggleButtonFocus(true);
richTextRef === null || richTextRef === void 0 ? void 0 : richTextRef.current.focus();
}
});
}
}
}, [isSelected, isButtonFocused]);
const linkSettingsActions = [{
label: __('Remove link'),
onPress: onClearSettings
}];
const linkSettingsOptions = {
url: {
label: __('Button Link URL'),
placeholder: __('Add URL'),
autoFocus: true,
autoFill: false
},
openInNewTab: {
label: __('Open in new tab')
},
linkRel: {
label: __('Link Rel'),
placeholder: _x('None', 'Link rel attribute value placeholder')
}
};
const noFocusLinkSettingOptions = { ...linkSettingsOptions,
url: { ...linkSettingsOptions.url,
autoFocus: false
}
};
function getBackgroundColor() {
var _colorProps$style, _colorProps$style2;
const {
attributes,
style
} = props;
const {
backgroundColor,
gradient
} = attributes; // Return named gradient value if available.
const gradientValue = getGradientValueBySlug(gradients, gradient);
if (gradientValue) {
return gradientValue;
}
const colorProps = getColorClassesAndStyles(attributes); // Retrieve named color object to force inline styles for themes that
// do not load their color stylesheets in the editor.
const colorObject = getColorObjectByAttributeValues(colors, backgroundColor);
return (colorObject === null || colorObject === void 0 ? void 0 : colorObject.color) || ((_colorProps$style = colorProps.style) === null || _colorProps$style === void 0 ? void 0 : _colorProps$style.backgroundColor) || ((_colorProps$style2 = colorProps.style) === null || _colorProps$style2 === void 0 ? void 0 : _colorProps$style2.background) || (style === null || style === void 0 ? void 0 : style.backgroundColor) || styles.defaultButton.backgroundColor;
}
function getTextColor() {
var _colorProps$style3;
const {
attributes,
style
} = props;
const colorProps = getColorClassesAndStyles(attributes); // Retrieve named color object to force inline styles for themes that
// do not load their color stylesheets in the editor.
const colorObject = getColorObjectByAttributeValues(colors, attributes.textColor);
return (colorObject === null || colorObject === void 0 ? void 0 : colorObject.color) || ((_colorProps$style3 = colorProps.style) === null || _colorProps$style3 === void 0 ? void 0 : _colorProps$style3.color) || (style === null || style === void 0 ? void 0 : style.color) || styles.defaultButton.color;
}
function onChangeText(value) {
const {
setAttributes
} = props;
setAttributes({
text: value
});
}
function onChangeBorderRadius(newRadius) {
const {
setAttributes,
attributes
} = props;
const {
style
} = attributes;
const newStyle = getNewStyle(style, newRadius, borderRadiusUnit);
setAttributes({
style: newStyle
});
}
function onChangeBorderRadiusUnit(newRadiusUnit) {
var _attributes$style, _attributes$style$bor;
const {
setAttributes,
attributes
} = props;
const {
style
} = attributes;
const newBorderRadius = getBorderRadiusValue(attributes === null || attributes === void 0 ? void 0 : (_attributes$style = attributes.style) === null || _attributes$style === void 0 ? void 0 : (_attributes$style$bor = _attributes$style.border) === null || _attributes$style$bor === void 0 ? void 0 : _attributes$style$bor.radius);
const newStyle = getNewStyle(style, newBorderRadius, newRadiusUnit);
setAttributes({
style: newStyle
});
setBorderRadiusUnit(newRadiusUnit);
}
function getNewStyle(style, radius, radiusUnit) {
return { ...style,
border: { ...(style === null || style === void 0 ? void 0 : style.border),
radius: `${radius}${radiusUnit}` // Store the value with the unit so that it works as expected.
}
};
}
function onShowLinkSettings() {
setIsLinkSheetVisible(true);
}
function onHideLinkSettings() {
setIsLinkSheetVisible(false);
}
function onToggleButtonFocus(value) {
if (value !== isButtonFocused) {
setIsButtonFocused(value);
}
}
function onClearSettings() {
const {
setAttributes
} = props;
setAttributes({
url: '',
rel: '',
linkTarget: ''
});
onHideLinkSettings();
}
function onLayout(_ref2) {
let {
nativeEvent
} = _ref2;
const {
width
} = nativeEvent.layout;
onSetMaxWidth(width);
}
const onSetMaxWidth = useCallback(function (width) {
let isParentWidthDidChange = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
const {
marginRight: spacing
} = styles.defaultButton;
const isParentWidthChanged = isParentWidthDidChange ? isParentWidthDidChange : maxWidth !== parentWidth;
const isWidthChanged = maxWidth !== width;
if (parentWidth && !width && isParentWidthChanged) {
setMaxWidth(parentWidth - spacing);
} else if (!parentWidth && width && isWidthChanged) {
setMaxWidth(width - spacing);
}
}, [maxWidth, parentWidth]);
function onRemove() {
const {
onDeleteBlock,
onReplace
} = props;
if (numOfButtons === 1) {
onDeleteBlock();
} else {
onReplace([]);
}
}
function onPlaceholderTextWidth(_ref3) {
let {
nativeEvent
} = _ref3;
const textWidth = nativeEvent.lines[0] && nativeEvent.lines[0].width;
if (textWidth && textWidth !== placeholderTextWidth) {
setPlaceholderTextWidth(Math.min(textWidth, maxWidth));
}
}
const onSetRef = useCallback(ref => {
richTextRef.current = ref;
}, [richTextRef]);
const onUnstableOnFocus = useCallback(() => {
onToggleButtonFocus(true);
}, []);
const onBlur = useCallback(() => {
onSetMaxWidth();
}, []);
function dismissSheet() {
onHideLinkSettings();
closeGeneralSidebar();
}
function getLinkSettings(isCompatibleWithSettings) {
const {
attributes,
setAttributes
} = props;
return createElement(LinkSettingsNavigation, {
isVisible: isLinkSheetVisible,
url: attributes.url,
rel: attributes.rel,
linkTarget: attributes.linkTarget,
onClose: dismissSheet,
setAttributes: setAttributes,
withBottomSheet: !isCompatibleWithSettings,
hasPicker: true,
actions: linkSettingsActions,
options: isCompatibleWithSettings ? linkSettingsOptions : noFocusLinkSettingOptions,
showIcon: !isCompatibleWithSettings
});
} // Render `Text` with `placeholderText` styled as a placeholder
// to calculate its width which then is set as a `minWidth`
function getPlaceholderWidth(placeholderText) {
return createElement(Text, {
style: styles.placeholder,
onTextLayout: onPlaceholderTextWidth
}, placeholderText);
}
function getBorderRadiusValue(currentBorderRadius, defaultBorderRadius) {
const valueAndUnit = getValueAndUnit(currentBorderRadius);
if (Number.isInteger(parseInt(valueAndUnit === null || valueAndUnit === void 0 ? void 0 : valueAndUnit.valueToConvert))) {
return parseFloat(valueAndUnit.valueToConvert);
}
return defaultBorderRadius;
}
const {
attributes,
clientId,
onReplace,
mergeBlocks,
setAttributes,
style
} = props;
const {
placeholder,
text,
style: buttonStyle,
url,
align = 'center',
width
} = attributes;
const {
paddingTop: spacing,
borderWidth
} = styles.defaultButton;
if (parentWidth === 0) {
return null;
}
const currentBorderRadius = buttonStyle === null || buttonStyle === void 0 ? void 0 : (_buttonStyle$border = buttonStyle.border) === null || _buttonStyle$border === void 0 ? void 0 : _buttonStyle$border.radius;
const borderRadiusValue = getBorderRadiusValue(currentBorderRadius, styles.defaultButton.borderRadius);
const buttonBorderRadiusValue = borderRadiusUnit === 'px' || borderRadiusUnit === '%' ? borderRadiusValue : Math.floor(14 * borderRadiusValue); // Lets assume that the font size is set to 14px; TO get a nicer preview.
const outlineBorderRadius = buttonBorderRadiusValue > 0 ? buttonBorderRadiusValue + spacing + borderWidth : 0; // To achieve proper expanding and shrinking `RichText` on iOS, there is a need to set a `minWidth`
// value at least on 1 when `RichText` is focused or when is not focused, but `RichText` value is
// different than empty string.
let minWidth = isButtonFocused || !isButtonFocused && text && text !== '' ? MIN_WIDTH : placeholderTextWidth;
if (width) {
// Set the width of the button.
minWidth = Math.floor(maxWidth * (width / 100) - MIN_WIDTH_MARGINS[width]);
} // To achieve proper expanding and shrinking `RichText` on Android, there is a need to set
// a `placeholder` as an empty string when `RichText` is focused,
// because `AztecView` is calculating a `minWidth` based on placeholder text.
const placeholderText = isButtonFocused || !isButtonFocused && text && text !== '' ? '' : placeholder || __('Add text…');
const backgroundColor = getBackgroundColor();
const textColor = getTextColor();
const isFixedWidth = !!width;
const outLineStyles = [styles.outline, {
borderRadius: outlineBorderRadius,
borderColor: backgroundColor
}];
const textStyles = { ...richTextStyle.richText,
paddingLeft: isFixedWidth ? 0 : richTextStyle.richText.paddingLeft,
paddingRight: isFixedWidth ? 0 : richTextStyle.richText.paddingRight,
color: textColor
};
return createElement(View, {
onLayout: onLayout
}, getPlaceholderWidth(placeholderText), createElement(ColorBackground, {
borderRadiusValue: buttonBorderRadiusValue,
backgroundColor: backgroundColor,
isSelected: isSelected
}, isSelected && createElement(View, {
pointerEvents: "none",
style: outLineStyles
}), createElement(RichText, {
setRef: onSetRef,
placeholder: placeholderText,
value: text,
onChange: onChangeText,
style: textStyles,
textAlign: align,
placeholderTextColor: (style === null || style === void 0 ? void 0 : style.color) || styles.placeholderTextColor.color,
identifier: "text",
tagName: "p",
minWidth: minWidth // The minimum Button size.
,
maxWidth: isFixedWidth ? minWidth : maxWidth // The width of the screen.
,
id: clientId,
isSelected: isButtonFocused,
withoutInteractiveFormatting: true,
unstableOnFocus: onUnstableOnFocus,
__unstableMobileNoFocusOnMount: !isSelected,
selectionColor: textColor,
onBlur: onBlur,
onReplace: onReplace,
onRemove: onRemove,
onMerge: mergeBlocks,
fontSize: style === null || style === void 0 ? void 0 : style.fontSize
})), isSelected && createElement(Fragment, null, createElement(BlockControls, null, createElement(ToolbarGroup, null, createElement(ToolbarButton, {
title: __('Edit link'),
icon: link,
onClick: onShowLinkSettings,
isActive: url
}))), getLinkSettings(false), createElement(InspectorControls, null, createElement(PanelBody, {
title: __('Border Settings')
}, createElement(UnitControl, {
label: __('Border Radius'),
min: MIN_BORDER_RADIUS_VALUE,
max: MAX_BORDER_RADIUS_VALUE,
value: borderRadiusValue,
onChange: onChangeBorderRadius,
onUnitChange: onChangeBorderRadiusUnit,
unit: borderRadiusUnit,
units: filterUnitsWithSettings(['px', 'em', 'rem'], CSS_UNITS)
})), createElement(WidthPanel, {
selectedWidth: width,
setAttributes: setAttributes
}), createElement(PanelBody, {
title: __('Link Settings')
}, getLinkSettings(true)))));
}
export default ButtonEdit;
//# sourceMappingURL=edit.native.js.map