UNPKG

@wordpress/block-library

Version:
527 lines (467 loc) 17.4 kB
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