@wordpress/block-library
Version:
Block library for the WordPress editor.
366 lines (337 loc) • 13.8 kB
JavaScript
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { useBlockProps, BlockControls, InspectorControls, RichText, __experimentalUseBorderProps as useBorderProps, __experimentalUseColorProps as useColorProps, getTypographyClassesAndStyles as useTypographyProps, store as blockEditorStore, __experimentalGetElementClassName } from '@wordpress/block-editor';
import { useDispatch, useSelect } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { ToolbarDropdownMenu, ToolbarGroup, Button, ButtonGroup, ToolbarButton, ResizableBox, PanelBody, BaseControl, __experimentalUseCustomUnits as useCustomUnits, __experimentalUnitControl as UnitControl } from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { Icon, search } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { __unstableStripHTML as stripHTML } from '@wordpress/dom';
/**
* Internal dependencies
*/
import { buttonOnly, buttonOutside, buttonInside, noButton, buttonWithIcon, toggleLabel } from './icons';
import { PC_WIDTH_DEFAULT, PX_WIDTH_DEFAULT, MIN_WIDTH, MIN_WIDTH_UNIT } from './utils.js'; // Used to calculate border radius adjustment to avoid "fat" corners when
// button is placed inside wrapper.
const DEFAULT_INNER_PADDING = '4px';
export default function SearchEdit(_ref) {
var _style$border;
let {
className,
attributes,
setAttributes,
toggleSelection,
isSelected,
clientId
} = _ref;
const {
label,
showLabel,
placeholder,
width,
widthUnit,
align,
buttonText,
buttonPosition,
buttonUseIcon,
style
} = attributes;
const insertedInNavigationBlock = useSelect(select => {
var _getBlockParentsByBlo;
const {
getBlockParentsByBlockName,
wasBlockJustInserted
} = select(blockEditorStore);
return !!((_getBlockParentsByBlo = getBlockParentsByBlockName(clientId, 'core/navigation')) !== null && _getBlockParentsByBlo !== void 0 && _getBlockParentsByBlo.length) && wasBlockJustInserted(clientId);
}, [clientId]);
const {
__unstableMarkNextChangeAsNotPersistent
} = useDispatch(blockEditorStore);
useEffect(() => {
if (!insertedInNavigationBlock) return; // This side-effect should not create an undo level.
__unstableMarkNextChangeAsNotPersistent();
setAttributes({
showLabel: false,
buttonUseIcon: true,
buttonPosition: 'button-inside'
});
}, [insertedInNavigationBlock]);
const borderRadius = style === null || style === void 0 ? void 0 : (_style$border = style.border) === null || _style$border === void 0 ? void 0 : _style$border.radius;
const borderProps = useBorderProps(attributes); // Check for old deprecated numerical border radius. Done as a separate
// check so that a borderRadius style won't overwrite the longhand
// per-corner styles.
if (typeof borderRadius === 'number') {
borderProps.style.borderRadius = `${borderRadius}px`;
}
const colorProps = useColorProps(attributes);
const typographyProps = useTypographyProps(attributes);
const unitControlInstanceId = useInstanceId(UnitControl);
const unitControlInputId = `wp-block-search__width-${unitControlInstanceId}`;
const isButtonPositionInside = 'button-inside' === buttonPosition;
const isButtonPositionOutside = 'button-outside' === buttonPosition;
const hasNoButton = 'no-button' === buttonPosition;
const hasOnlyButton = 'button-only' === buttonPosition;
const units = useCustomUnits({
availableUnits: ['%', 'px'],
defaultValues: {
'%': PC_WIDTH_DEFAULT,
px: PX_WIDTH_DEFAULT
}
});
const getBlockClassNames = () => {
return classnames(className, isButtonPositionInside ? 'wp-block-search__button-inside' : undefined, isButtonPositionOutside ? 'wp-block-search__button-outside' : undefined, hasNoButton ? 'wp-block-search__no-button' : undefined, hasOnlyButton ? 'wp-block-search__button-only' : undefined, !buttonUseIcon && !hasNoButton ? 'wp-block-search__text-button' : undefined, buttonUseIcon && !hasNoButton ? 'wp-block-search__icon-button' : undefined);
};
const buttonPositionControls = [{
role: 'menuitemradio',
title: __('Button outside'),
isActive: buttonPosition === 'button-outside',
icon: buttonOutside,
onClick: () => {
setAttributes({
buttonPosition: 'button-outside'
});
}
}, {
role: 'menuitemradio',
title: __('Button inside'),
isActive: buttonPosition === 'button-inside',
icon: buttonInside,
onClick: () => {
setAttributes({
buttonPosition: 'button-inside'
});
}
}, {
role: 'menuitemradio',
title: __('No button'),
isActive: buttonPosition === 'no-button',
icon: noButton,
onClick: () => {
setAttributes({
buttonPosition: 'no-button'
});
}
}];
const getButtonPositionIcon = () => {
switch (buttonPosition) {
case 'button-inside':
return buttonInside;
case 'button-outside':
return buttonOutside;
case 'no-button':
return noButton;
case 'button-only':
return buttonOnly;
}
};
const getResizableSides = () => {
if (hasOnlyButton) {
return {};
}
return {
right: align !== 'right',
left: align === 'right'
};
};
const renderTextField = () => {
// If the input is inside the wrapper, the wrapper gets the border color styles/classes, not the input control.
const textFieldClasses = classnames('wp-block-search__input', isButtonPositionInside ? undefined : borderProps.className, typographyProps.className);
const textFieldStyles = { ...(isButtonPositionInside ? {
borderRadius
} : borderProps.style),
...typographyProps.style,
textDecoration: undefined
};
return createElement("input", {
type: "search",
className: textFieldClasses,
style: textFieldStyles,
"aria-label": __('Optional placeholder text') // We hide the placeholder field's placeholder when there is a value. This
// stops screen readers from reading the placeholder field's placeholder
// which is confusing.
,
placeholder: placeholder ? undefined : __('Optional placeholder…'),
value: placeholder,
onChange: event => setAttributes({
placeholder: event.target.value
})
});
};
const renderButton = () => {
// If the button is inside the wrapper, the wrapper gets the border color styles/classes, not the button.
const buttonClasses = classnames('wp-block-search__button', colorProps.className, typographyProps.className, isButtonPositionInside ? undefined : borderProps.className, buttonUseIcon ? 'has-icon' : undefined, __experimentalGetElementClassName('button'));
const buttonStyles = { ...colorProps.style,
...typographyProps.style,
...(isButtonPositionInside ? {
borderRadius
} : borderProps.style)
};
return createElement(Fragment, null, buttonUseIcon && createElement("button", {
type: "button",
className: buttonClasses,
style: buttonStyles,
"aria-label": buttonText ? stripHTML(buttonText) : __('Search')
}, createElement(Icon, {
icon: search
})), !buttonUseIcon && createElement(RichText, {
className: buttonClasses,
style: buttonStyles,
"aria-label": __('Button text'),
placeholder: __('Add button text…'),
withoutInteractiveFormatting: true,
value: buttonText,
onChange: html => setAttributes({
buttonText: html
})
}));
};
const controls = createElement(Fragment, null, createElement(BlockControls, null, createElement(ToolbarGroup, null, createElement(ToolbarButton, {
title: __('Toggle search label'),
icon: toggleLabel,
onClick: () => {
setAttributes({
showLabel: !showLabel
});
},
className: showLabel ? 'is-pressed' : undefined
}), createElement(ToolbarDropdownMenu, {
icon: getButtonPositionIcon(),
label: __('Change button position'),
controls: buttonPositionControls
}), !hasNoButton && createElement(ToolbarButton, {
title: __('Use button with icon'),
icon: buttonWithIcon,
onClick: () => {
setAttributes({
buttonUseIcon: !buttonUseIcon
});
},
className: buttonUseIcon ? 'is-pressed' : undefined
}))), createElement(InspectorControls, null, createElement(PanelBody, {
title: __('Display Settings')
}, createElement(BaseControl, {
label: __('Width'),
id: unitControlInputId
}, createElement(UnitControl, {
id: unitControlInputId,
min: `${MIN_WIDTH}${MIN_WIDTH_UNIT}`,
onChange: newWidth => {
const filteredWidth = widthUnit === '%' && parseInt(newWidth, 10) > 100 ? 100 : newWidth;
setAttributes({
width: parseInt(filteredWidth, 10)
});
},
onUnitChange: newUnit => {
setAttributes({
width: '%' === newUnit ? PC_WIDTH_DEFAULT : PX_WIDTH_DEFAULT,
widthUnit: newUnit
});
},
style: {
maxWidth: 80
},
value: `${width}${widthUnit}`,
units: units
}), createElement(ButtonGroup, {
className: "wp-block-search__components-button-group",
"aria-label": __('Percentage Width')
}, [25, 50, 75, 100].map(widthValue => {
return createElement(Button, {
key: widthValue,
isSmall: true,
variant: `${widthValue}%` === `${width}${widthUnit}` ? 'primary' : undefined,
onClick: () => setAttributes({
width: widthValue,
widthUnit: '%'
})
}, widthValue, "%");
}))))));
const padBorderRadius = radius => radius ? `calc(${radius} + ${DEFAULT_INNER_PADDING})` : undefined;
const getWrapperStyles = () => {
var _borderProps$style, _borderProps$style2, _borderProps$style3, _borderProps$style4, _borderProps$style5;
const styles = isButtonPositionInside ? borderProps.style : {
borderRadius: (_borderProps$style = borderProps.style) === null || _borderProps$style === void 0 ? void 0 : _borderProps$style.borderRadius,
borderTopLeftRadius: (_borderProps$style2 = borderProps.style) === null || _borderProps$style2 === void 0 ? void 0 : _borderProps$style2.borderTopLeftRadius,
borderTopRightRadius: (_borderProps$style3 = borderProps.style) === null || _borderProps$style3 === void 0 ? void 0 : _borderProps$style3.borderTopRightRadius,
borderBottomLeftRadius: (_borderProps$style4 = borderProps.style) === null || _borderProps$style4 === void 0 ? void 0 : _borderProps$style4.borderBottomLeftRadius,
borderBottomRightRadius: (_borderProps$style5 = borderProps.style) === null || _borderProps$style5 === void 0 ? void 0 : _borderProps$style5.borderBottomRightRadius
};
const isNonZeroBorderRadius = borderRadius !== undefined && parseInt(borderRadius, 10) !== 0;
if (isButtonPositionInside && isNonZeroBorderRadius) {
// We have button inside wrapper and a border radius value to apply.
// Add default padding so we don't get "fat" corners.
//
// CSS calc() is used here to support non-pixel units. The inline
// style using calc() will only apply if both values have units.
if (typeof borderRadius === 'object') {
// Individual corner border radii present.
const {
topLeft,
topRight,
bottomLeft,
bottomRight
} = borderRadius;
return { ...styles,
borderTopLeftRadius: padBorderRadius(topLeft),
borderTopRightRadius: padBorderRadius(topRight),
borderBottomLeftRadius: padBorderRadius(bottomLeft),
borderBottomRightRadius: padBorderRadius(bottomRight)
};
} // The inline style using calc() will only apply if both values
// supplied to calc() have units. Deprecated block's may have
// unitless integer.
const radius = Number.isInteger(borderRadius) ? `${borderRadius}px` : borderRadius;
styles.borderRadius = `calc(${radius} + ${DEFAULT_INNER_PADDING})`;
}
return styles;
};
const blockProps = useBlockProps({
className: getBlockClassNames(),
style: { ...typographyProps.style,
// Input opts out of text decoration.
textDecoration: undefined
}
});
const labelClassnames = classnames('wp-block-search__label', typographyProps.className);
return createElement("div", blockProps, controls, showLabel && createElement(RichText, {
className: labelClassnames,
"aria-label": __('Label text'),
placeholder: __('Add label…'),
withoutInteractiveFormatting: true,
value: label,
onChange: html => setAttributes({
label: html
}),
style: typographyProps.style
}), createElement(ResizableBox, {
size: {
width: `${width}${widthUnit}`
},
className: classnames('wp-block-search__inside-wrapper', isButtonPositionInside ? borderProps.className : undefined),
style: getWrapperStyles(),
minWidth: MIN_WIDTH,
enable: getResizableSides(),
onResizeStart: (event, direction, elt) => {
setAttributes({
width: parseInt(elt.offsetWidth, 10),
widthUnit: 'px'
});
toggleSelection(false);
},
onResizeStop: (event, direction, elt, delta) => {
setAttributes({
width: parseInt(width + delta.width, 10)
});
toggleSelection(true);
},
showHandle: isSelected
}, (isButtonPositionInside || isButtonPositionOutside) && createElement(Fragment, null, renderTextField(), renderButton()), hasOnlyButton && renderButton(), hasNoButton && renderTextField()));
}
//# sourceMappingURL=edit.js.map