@wordpress/block-library
Version:
Block library for the WordPress editor.
377 lines (371 loc) • 13.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _clsx = _interopRequireDefault(require("clsx"));
var _constants = require("./constants");
var _getUpdatedLinkAttributes = require("./get-updated-link-attributes");
var _removeAnchorTag = _interopRequireDefault(require("../utils/remove-anchor-tag"));
var _hooks = require("../utils/hooks");
var _lockUnlock = require("../lock-unlock");
var _i18n = require("@wordpress/i18n");
var _element = require("@wordpress/element");
var _components = require("@wordpress/components");
var _blockEditor = require("@wordpress/block-editor");
var _keycodes = require("@wordpress/keycodes");
var _icons = require("@wordpress/icons");
var _blocks = require("@wordpress/blocks");
var _compose = require("@wordpress/compose");
var _data = require("@wordpress/data");
var _jsxRuntime = require("react/jsx-runtime");
/**
* External dependencies
*/
/**
* Internal dependencies
*/
/**
* WordPress dependencies
*/
const {
HTMLElementControl
} = (0, _lockUnlock.unlock)(_blockEditor.privateApis);
const LINK_SETTINGS = [..._blockEditor.LinkControl.DEFAULT_LINK_SETTINGS, {
id: 'nofollow',
title: (0, _i18n.__)('Mark as nofollow')
}];
function useEnter(props) {
const {
replaceBlocks,
selectionChange
} = (0, _data.useDispatch)(_blockEditor.store);
const {
getBlock,
getBlockRootClientId,
getBlockIndex
} = (0, _data.useSelect)(_blockEditor.store);
const propsRef = (0, _element.useRef)(props);
propsRef.current = props;
return (0, _compose.useRefEffect)(element => {
function onKeyDown(event) {
if (event.defaultPrevented || event.keyCode !== _keycodes.ENTER) {
return;
}
const {
content,
clientId
} = propsRef.current;
if (content.length) {
return;
}
event.preventDefault();
const topParentListBlock = getBlock(getBlockRootClientId(clientId));
const blockIndex = getBlockIndex(clientId);
const head = (0, _blocks.cloneBlock)({
...topParentListBlock,
innerBlocks: topParentListBlock.innerBlocks.slice(0, blockIndex)
});
const middle = (0, _blocks.createBlock)((0, _blocks.getDefaultBlockName)());
const after = topParentListBlock.innerBlocks.slice(blockIndex + 1);
const tail = after.length ? [(0, _blocks.cloneBlock)({
...topParentListBlock,
innerBlocks: after
})] : [];
replaceBlocks(topParentListBlock.clientId, [head, middle, ...tail], 1);
// We manually change the selection here because we are replacing
// a different block than the selected one.
selectionChange(middle.clientId);
}
element.addEventListener('keydown', onKeyDown);
return () => {
element.removeEventListener('keydown', onKeyDown);
};
}, []);
}
function WidthPanel({
selectedWidth,
setAttributes
}) {
const dropdownMenuProps = (0, _hooks.useToolsPanelDropdownMenuProps)();
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToolsPanel, {
label: (0, _i18n.__)('Settings'),
resetAll: () => setAttributes({
width: undefined
}),
dropdownMenuProps: dropdownMenuProps,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToolsPanelItem, {
label: (0, _i18n.__)('Width'),
isShownByDefault: true,
hasValue: () => !!selectedWidth,
onDeselect: () => setAttributes({
width: undefined
}),
__nextHasNoMarginBottom: true,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToggleGroupControl, {
label: (0, _i18n.__)('Width'),
value: selectedWidth,
onChange: newWidth => setAttributes({
width: newWidth
}),
isBlock: true,
__next40pxDefaultSize: true,
__nextHasNoMarginBottom: true,
children: [25, 50, 75, 100].map(widthValue => {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToggleGroupControlOption, {
value: widthValue,
label: (0, _i18n.sprintf)(/* translators: Percentage value. */
(0, _i18n.__)('%d%%'), widthValue)
}, widthValue);
})
})
})
});
}
function ButtonEdit(props) {
const {
attributes,
setAttributes,
className,
isSelected,
onReplace,
mergeBlocks,
clientId,
context
} = props;
const {
tagName,
textAlign,
linkTarget,
placeholder,
rel,
style,
text,
url,
width,
metadata
} = attributes;
const TagName = tagName || 'a';
function onKeyDown(event) {
if (_keycodes.isKeyboardEvent.primary(event, 'k')) {
startEditing(event);
} else if (_keycodes.isKeyboardEvent.primaryShift(event, 'k')) {
unlink();
richTextRef.current?.focus();
}
}
// Use internal state instead of a ref to make sure that the component
// re-renders when the popover's anchor updates.
const [popoverAnchor, setPopoverAnchor] = (0, _element.useState)(null);
const borderProps = (0, _blockEditor.__experimentalUseBorderProps)(attributes);
const colorProps = (0, _blockEditor.__experimentalUseColorProps)(attributes);
const spacingProps = (0, _blockEditor.__experimentalGetSpacingClassesAndStyles)(attributes);
const shadowProps = (0, _blockEditor.__experimentalGetShadowClassesAndStyles)(attributes);
const ref = (0, _element.useRef)();
const richTextRef = (0, _element.useRef)();
const blockProps = (0, _blockEditor.useBlockProps)({
ref: (0, _compose.useMergeRefs)([setPopoverAnchor, ref]),
onKeyDown
});
const blockEditingMode = (0, _blockEditor.useBlockEditingMode)();
const [isEditingURL, setIsEditingURL] = (0, _element.useState)(false);
const isURLSet = !!url;
const opensInNewTab = linkTarget === _constants.NEW_TAB_TARGET;
const nofollow = !!rel?.includes(_constants.NOFOLLOW_REL);
const isLinkTag = 'a' === TagName;
const {
createPageEntity,
userCanCreatePages,
lockUrlControls = false
} = (0, _data.useSelect)(select => {
if (!isSelected) {
return {};
}
const _settings = select(_blockEditor.store).getSettings();
const blockBindingsSource = (0, _blocks.getBlockBindingsSource)(metadata?.bindings?.url?.source);
return {
createPageEntity: _settings.__experimentalCreatePageEntity,
userCanCreatePages: _settings.__experimentalUserCanCreatePages,
lockUrlControls: !!metadata?.bindings?.url && !blockBindingsSource?.canUserEditValue?.({
select,
context,
args: metadata?.bindings?.url?.args
})
};
}, [context, isSelected, metadata?.bindings?.url]);
async function handleCreate(pageTitle) {
const page = await createPageEntity({
title: pageTitle,
status: 'draft'
});
return {
id: page.id,
type: page.type,
title: page.title.rendered,
url: page.link,
kind: 'post-type'
};
}
function createButtonText(searchTerm) {
return (0, _element.createInterpolateElement)((0, _i18n.sprintf)(/* translators: %s: search term. */
(0, _i18n.__)('Create page: <mark>%s</mark>'), searchTerm), {
mark: /*#__PURE__*/(0, _jsxRuntime.jsx)("mark", {})
});
}
function startEditing(event) {
event.preventDefault();
setIsEditingURL(true);
}
function unlink() {
setAttributes({
url: undefined,
linkTarget: undefined,
rel: undefined
});
setIsEditingURL(false);
}
(0, _element.useEffect)(() => {
if (!isSelected) {
setIsEditingURL(false);
}
}, [isSelected]);
// Memoize link value to avoid overriding the LinkControl's internal state.
// This is a temporary fix. See https://github.com/WordPress/gutenberg/issues/51256.
const linkValue = (0, _element.useMemo)(() => ({
url,
opensInNewTab,
nofollow
}), [url, opensInNewTab, nofollow]);
const useEnterRef = useEnter({
content: text,
clientId
});
const mergedRef = (0, _compose.useMergeRefs)([useEnterRef, richTextRef]);
const [fluidTypographySettings, layout] = (0, _blockEditor.useSettings)('typography.fluid', 'layout');
const typographyProps = (0, _blockEditor.getTypographyClassesAndStyles)(attributes, {
typography: {
fluid: fluidTypographySettings
},
layout: {
wideSize: layout?.wideSize
}
});
const hasNonContentControls = blockEditingMode === 'default';
const hasBlockControls = hasNonContentControls || isLinkTag && !lockUrlControls;
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
...blockProps,
className: (0, _clsx.default)(blockProps.className, {
[`has-custom-width wp-block-button__width-${width}`]: width
}),
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.RichText, {
ref: mergedRef,
"aria-label": (0, _i18n.__)('Button text'),
placeholder: placeholder || (0, _i18n.__)('Add text…'),
value: text,
onChange: value => setAttributes({
text: (0, _removeAnchorTag.default)(value)
}),
withoutInteractiveFormatting: true,
className: (0, _clsx.default)(className, 'wp-block-button__link', colorProps.className, borderProps.className, typographyProps.className, {
[`has-text-align-${textAlign}`]: textAlign,
// For backwards compatibility add style that isn't
// provided via block support.
'no-border-radius': style?.border?.radius === 0,
[`has-custom-font-size`]: blockProps.style.fontSize
}, (0, _blockEditor.__experimentalGetElementClassName)('button')),
style: {
...borderProps.style,
...colorProps.style,
...spacingProps.style,
...shadowProps.style,
...typographyProps.style,
writingMode: undefined
},
onReplace: onReplace,
onMerge: mergeBlocks,
identifier: "text"
})
}), hasBlockControls && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.BlockControls, {
group: "block",
children: [hasNonContentControls && /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.AlignmentControl, {
value: textAlign,
onChange: nextAlign => {
setAttributes({
textAlign: nextAlign
});
}
}), isLinkTag && !lockUrlControls && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToolbarButton, {
name: "link",
icon: !isURLSet ? _icons.link : _icons.linkOff,
title: !isURLSet ? (0, _i18n.__)('Link') : (0, _i18n.__)('Unlink'),
shortcut: !isURLSet ? _keycodes.displayShortcut.primary('k') : _keycodes.displayShortcut.primaryShift('k'),
onClick: !isURLSet ? startEditing : unlink,
isActive: isURLSet
})]
}), isLinkTag && isSelected && (isEditingURL || isURLSet) && !lockUrlControls && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Popover, {
placement: "bottom",
onClose: () => {
setIsEditingURL(false);
richTextRef.current?.focus();
},
anchor: popoverAnchor,
focusOnMount: isEditingURL ? 'firstElement' : false,
__unstableSlotName: "__unstable-block-tools-after",
shift: true,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.LinkControl, {
value: linkValue,
onChange: ({
url: newURL,
opensInNewTab: newOpensInNewTab,
nofollow: newNofollow
}) => setAttributes((0, _getUpdatedLinkAttributes.getUpdatedLinkAttributes)({
rel,
url: newURL,
opensInNewTab: newOpensInNewTab,
nofollow: newNofollow
})),
onRemove: () => {
unlink();
richTextRef.current?.focus();
},
forceIsEditingLink: isEditingURL,
settings: LINK_SETTINGS,
createSuggestion: createPageEntity && handleCreate,
withCreateSuggestion: userCanCreatePages,
createSuggestionButtonText: createButtonText
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.InspectorControls, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(WidthPanel, {
selectedWidth: width,
setAttributes: setAttributes
})
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.InspectorControls, {
group: "advanced",
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(HTMLElementControl, {
tagName: tagName,
onChange: value => setAttributes({
tagName: value
}),
options: [{
label: (0, _i18n.__)('Default (<a>)'),
value: 'a'
}, {
label: '<button>',
value: 'button'
}]
}), isLinkTag && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.TextControl, {
__next40pxDefaultSize: true,
__nextHasNoMarginBottom: true,
label: (0, _i18n.__)('Link rel'),
value: rel || '',
onChange: newRel => setAttributes({
rel: newRel
})
})]
})]
});
}
var _default = exports.default = ButtonEdit;
//# sourceMappingURL=edit.js.map