@wordpress/block-library
Version:
Block library for the WordPress editor.
502 lines (492 loc) • 16.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _reactNative = require("react-native");
var _element = require("@wordpress/element");
var _data = require("@wordpress/data");
var _i18n = require("@wordpress/i18n");
var _blockEditor = require("@wordpress/block-editor");
var _components = require("@wordpress/components");
var _icons = require("@wordpress/icons");
var _editPost = require("@wordpress/edit-post");
var _richText = _interopRequireDefault(require("./rich-text.scss"));
var _editor = _interopRequireDefault(require("./editor.scss"));
var _colorBackground = _interopRequireDefault(require("./color-background"));
var _jsxRuntime = require("react/jsx-runtime");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
// eslint-disable-next-line no-restricted-imports
/**
* Internal dependencies
*/
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: _editor.default.button75?.marginLeft,
50: _editor.default.button50?.marginLeft,
25: _editor.default.button25?.marginLeft
};
function WidthPanel({
selectedWidth,
setAttributes
}) {
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: (0, _i18n.__)('Auto')
}, {
value: 25,
label: '25%'
}, {
value: 50,
label: '50%'
}, {
value: 75,
label: '75%'
}, {
value: 100,
label: '100%'
}];
if (!selectedWidth) {
selectedWidth = 'auto';
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PanelBody, {
title: (0, _i18n.__)('Width Settings'),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.BottomSheetSelectControl, {
label: (0, _i18n.__)('Button width'),
value: selectedWidth,
onChange: handleChange,
options: options
})
});
}
function ButtonEdit(props) {
const {
isSelected,
parentWidth
} = props;
const initialBorderRadius = props?.attributes?.style?.border?.radius;
const {
valueUnit = 'px'
} = (0, _components.getValueAndUnit)(initialBorderRadius) || {};
const {
editorSidebarOpened,
numOfButtons
} = (0, _data.useSelect)(select => {
const {
isEditorSidebarOpened
} = select(_editPost.store);
const {
getBlockCount,
getBlockRootClientId
} = select(_blockEditor.store);
const parentId = getBlockRootClientId(clientId);
const blockCount = getBlockCount(parentId);
const currentIsEditorSidebarOpened = isEditorSidebarOpened();
return {
editorSidebarOpened: isSelected && currentIsEditorSidebarOpened,
numOfButtons: blockCount
};
}, [clientId, isSelected]);
const {
closeGeneralSidebar
} = (0, _data.useDispatch)(_editPost.store);
const [maxWidth, setMaxWidth] = (0, _element.useState)(INITIAL_MAX_WIDTH);
const [isLinkSheetVisible, setIsLinkSheetVisible] = (0, _element.useState)(false);
const [isButtonFocused, setIsButtonFocused] = (0, _element.useState)(true);
const [placeholderTextWidth, setPlaceholderTextWidth] = (0, _element.useState)(0);
const [borderRadiusUnit, setBorderRadiusUnit] = (0, _element.useState)(valueUnit);
const richTextRef = (0, _element.useRef)();
const colors = (0, _blockEditor.useMobileGlobalStylesColors)();
const gradients = (0, _blockEditor.useMobileGlobalStylesColors)('gradients');
(0, _element.useEffect)(() => {
if (isSelected) {
onToggleButtonFocus(true);
}
}, [isSelected]);
(0, _element.useEffect)(() => {
onSetMaxWidth(null, true);
}, [parentWidth]);
(0, _element.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?.current;
if (_reactNative.Platform.OS === 'android' && richText) {
if (editorSidebarOpened || isLinkSheetVisible) {
richText.blur();
onToggleButtonFocus(false);
} else {
onToggleButtonFocus(true);
}
}
}, [editorSidebarOpened, isLinkSheetVisible]);
(0, _element.useEffect)(() => {
if (richTextRef?.current) {
if (!isSelected && isButtonFocused) {
onToggleButtonFocus(false);
}
if (isSelected && !isButtonFocused) {
_reactNative.AccessibilityInfo.isScreenReaderEnabled().then(enabled => {
if (enabled) {
onToggleButtonFocus(true);
richTextRef?.current.focus();
}
});
}
}
}, [isSelected, isButtonFocused]);
const linkSettingsActions = [{
label: (0, _i18n.__)('Remove link'),
onPress: onClearSettings
}];
const linkSettingsOptions = {
url: {
label: (0, _i18n.__)('Button Link URL'),
placeholder: (0, _i18n.__)('Add URL'),
autoFocus: true,
autoFill: false
},
openInNewTab: {
label: (0, _i18n.__)('Open in new tab')
},
linkRel: {
label: (0, _i18n.__)('Link Rel'),
placeholder: (0, _i18n._x)('None', 'Link rel attribute value placeholder')
}
};
const noFocusLinkSettingOptions = {
...linkSettingsOptions,
url: {
...linkSettingsOptions.url,
autoFocus: false
}
};
function getBackgroundColor() {
const {
attributes,
style
} = props;
const {
backgroundColor,
gradient
} = attributes;
// Return named gradient value if available.
const gradientValue = (0, _blockEditor.getGradientValueBySlug)(gradients, gradient);
if (gradientValue) {
return gradientValue;
}
const colorProps = (0, _blockEditor.__experimentalGetColorClassesAndStyles)(attributes);
// Retrieve named color object to force inline styles for themes that
// do not load their color stylesheets in the editor.
const colorObject = (0, _blockEditor.getColorObjectByAttributeValues)(colors, backgroundColor);
return colorObject?.color || colorProps.style?.backgroundColor || colorProps.style?.background || style?.backgroundColor || _editor.default.defaultButton.backgroundColor;
}
function getTextColor() {
const {
attributes,
style
} = props;
const colorProps = (0, _blockEditor.__experimentalGetColorClassesAndStyles)(attributes);
// Retrieve named color object to force inline styles for themes that
// do not load their color stylesheets in the editor.
const colorObject = (0, _blockEditor.getColorObjectByAttributeValues)(colors, attributes.textColor);
return colorObject?.color || colorProps.style?.color || style?.color || _editor.default.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) {
const {
setAttributes,
attributes
} = props;
const {
style
} = attributes;
const newBorderRadius = getBorderRadiusValue(attributes?.style?.border?.radius);
const newStyle = getNewStyle(style, newBorderRadius, newRadiusUnit);
setAttributes({
style: newStyle
});
setBorderRadiusUnit(newRadiusUnit);
}
function getNewStyle(style, radius, radiusUnit) {
return {
...style,
border: {
...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({
nativeEvent
}) {
const {
width
} = nativeEvent.layout;
onSetMaxWidth(width);
}
const onSetMaxWidth = (0, _element.useCallback)((width, isParentWidthDidChange = false) => {
const {
marginRight: spacing
} = _editor.default.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({
nativeEvent
}) {
const textWidth = nativeEvent.lines[0] && nativeEvent.lines[0].width;
if (textWidth && textWidth !== placeholderTextWidth) {
setPlaceholderTextWidth(Math.min(textWidth, maxWidth));
}
}
const onSetRef = (0, _element.useCallback)(ref => {
richTextRef.current = ref;
}, [richTextRef]);
const onUnstableOnFocus = (0, _element.useCallback)(() => {
onToggleButtonFocus(true);
}, []);
const onBlur = (0, _element.useCallback)(() => {
onSetMaxWidth();
}, []);
function dismissSheet() {
onHideLinkSettings();
closeGeneralSidebar();
}
function getLinkSettings(isCompatibleWithSettings) {
const {
attributes,
setAttributes
} = props;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.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 /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: _editor.default.placeholder,
onTextLayout: onPlaceholderTextWidth,
children: placeholderText
});
}
function getBorderRadiusValue(currentBorderRadius, defaultBorderRadius) {
const valueAndUnit = (0, _components.getValueAndUnit)(currentBorderRadius);
if (Number.isInteger(parseInt(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
} = _editor.default.defaultButton;
if (parentWidth === 0) {
return null;
}
const currentBorderRadius = buttonStyle?.border?.radius;
const borderRadiusValue = getBorderRadiusValue(currentBorderRadius, _editor.default.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 || (0, _i18n.__)('Add text…');
const backgroundColor = getBackgroundColor();
const textColor = getTextColor();
const isFixedWidth = !!width;
const outLineStyles = [_editor.default.outline, {
borderRadius: outlineBorderRadius,
borderColor: backgroundColor
}];
const textStyles = {
..._richText.default.richText,
paddingLeft: isFixedWidth ? 0 : _richText.default.richText.paddingLeft,
paddingRight: isFixedWidth ? 0 : _richText.default.richText.paddingRight,
color: textColor
};
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
onLayout: onLayout,
children: [getPlaceholderWidth(placeholderText), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_colorBackground.default, {
borderRadiusValue: buttonBorderRadiusValue,
backgroundColor: backgroundColor,
isSelected: isSelected,
children: [isSelected && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
pointerEvents: "none",
style: outLineStyles
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.RichText, {
ref: onSetRef,
placeholder: placeholderText,
value: text,
onChange: onChangeText,
style: textStyles,
textAlign: align,
placeholderTextColor: style?.color || _editor.default.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?.fontSize
})]
}), isSelected && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.BlockControls, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToolbarGroup, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToolbarButton, {
title: (0, _i18n.__)('Edit link'),
icon: _icons.link,
onClick: onShowLinkSettings,
isActive: url
})
})
}), getLinkSettings(false), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.InspectorControls, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PanelBody, {
title: (0, _i18n.__)('Border Settings'),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.UnitControl, {
label: (0, _i18n.__)('Border Radius'),
min: MIN_BORDER_RADIUS_VALUE,
max: MAX_BORDER_RADIUS_VALUE,
value: borderRadiusValue,
onChange: onChangeBorderRadius,
onUnitChange: onChangeBorderRadiusUnit,
unit: borderRadiusUnit,
units: (0, _components.filterUnitsWithSettings)(['px', 'em', 'rem'], _components.CSS_UNITS)
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(WidthPanel, {
selectedWidth: width,
setAttributes: setAttributes
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.PanelBody, {
title: (0, _i18n.__)('Link Settings'),
children: getLinkSettings(true)
})]
})]
})]
});
}
var _default = exports.default = ButtonEdit;
//# sourceMappingURL=edit.native.js.map