UNPKG

@wordpress/block-editor

Version:
168 lines (160 loc) 6.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = usePasteStyles; var _element = require("@wordpress/element"); var _blocks = require("@wordpress/blocks"); var _data = require("@wordpress/data"); var _notices = require("@wordpress/notices"); var _i18n = require("@wordpress/i18n"); var _store = require("../../store"); var _supports = require("../../hooks/supports"); /** * WordPress dependencies */ /** * Internal dependencies */ /** * Determine if the copied text looks like serialized blocks or not. * Since plain text will always get parsed into a freeform block, * we check that if the parsed blocks is anything other than that. * * @param {string} text The copied text. * @return {boolean} True if the text looks like serialized blocks, false otherwise. */ function hasSerializedBlocks(text) { try { const blocks = (0, _blocks.parse)(text, { __unstableSkipMigrationLogs: true, __unstableSkipAutop: true }); if (blocks.length === 1 && blocks[0].name === 'core/freeform') { // It's likely that the text is just plain text and not serialized blocks. return false; } return true; } catch (err) { // Parsing error, the text is not serialized blocks. // (Even though that it technically won't happen) return false; } } /** * Style attributes are attributes being added in `block-editor/src/hooks/*`. * (Except for some unrelated to style like `anchor` or `settings`.) * They generally represent the default block supports. */ const STYLE_ATTRIBUTES = { align: _supports.hasAlignSupport, borderColor: nameOrType => (0, _supports.hasBorderSupport)(nameOrType, 'color'), backgroundColor: _supports.hasBackgroundColorSupport, textAlign: _supports.hasTextAlignSupport, textColor: _supports.hasTextColorSupport, gradient: _supports.hasGradientSupport, className: _supports.hasCustomClassNameSupport, fontFamily: _supports.hasFontFamilySupport, fontSize: _supports.hasFontSizeSupport, layout: _supports.hasLayoutSupport, style: _supports.hasStyleSupport }; /** * Get the "style attributes" from a given block to a target block. * * @param {WPBlock} sourceBlock The source block. * @param {WPBlock} targetBlock The target block. * @return {Object} the filtered attributes object. */ function getStyleAttributes(sourceBlock, targetBlock) { return Object.entries(STYLE_ATTRIBUTES).reduce((attributes, [attributeKey, hasSupport]) => { // Only apply the attribute if both blocks support it. if (hasSupport(sourceBlock.name) && hasSupport(targetBlock.name)) { // Override attributes that are not present in the block to their defaults. attributes[attributeKey] = sourceBlock.attributes[attributeKey]; } return attributes; }, {}); } /** * Update the target blocks with style attributes recursively. * * @param {WPBlock[]} targetBlocks The target blocks to be updated. * @param {WPBlock[]} sourceBlocks The source blocks to get th style attributes from. * @param {Function} updateBlockAttributes The function to update the attributes. */ function recursivelyUpdateBlockAttributes(targetBlocks, sourceBlocks, updateBlockAttributes) { for (let index = 0; index < Math.min(sourceBlocks.length, targetBlocks.length); index += 1) { updateBlockAttributes(targetBlocks[index].clientId, getStyleAttributes(sourceBlocks[index], targetBlocks[index])); recursivelyUpdateBlockAttributes(targetBlocks[index].innerBlocks, sourceBlocks[index].innerBlocks, updateBlockAttributes); } } /** * A hook to return a pasteStyles event function for handling pasting styles to blocks. * * @return {Function} A function to update the styles to the blocks. */ function usePasteStyles() { const registry = (0, _data.useRegistry)(); const { updateBlockAttributes } = (0, _data.useDispatch)(_store.store); const { createSuccessNotice, createWarningNotice, createErrorNotice } = (0, _data.useDispatch)(_notices.store); return (0, _element.useCallback)(async targetBlocks => { let html = ''; try { // `http:` sites won't have the clipboard property on navigator. // (with the exception of localhost.) if (!window.navigator.clipboard) { createErrorNotice((0, _i18n.__)('Unable to paste styles. This feature is only available on secure (https) sites in supporting browsers.'), { type: 'snackbar' }); return; } html = await window.navigator.clipboard.readText(); } catch (error) { // Possibly the permission is denied. createErrorNotice((0, _i18n.__)('Unable to paste styles. Please allow browser clipboard permissions before continuing.'), { type: 'snackbar' }); return; } // Abort if the copied text is empty or doesn't look like serialized blocks. if (!html || !hasSerializedBlocks(html)) { createWarningNotice((0, _i18n.__)("Unable to paste styles. Block styles couldn't be found within the copied content."), { type: 'snackbar' }); return; } const copiedBlocks = (0, _blocks.parse)(html); if (copiedBlocks.length === 1) { // Apply styles of the block to all the target blocks. registry.batch(() => { recursivelyUpdateBlockAttributes(targetBlocks, targetBlocks.map(() => copiedBlocks[0]), updateBlockAttributes); }); } else { registry.batch(() => { recursivelyUpdateBlockAttributes(targetBlocks, copiedBlocks, updateBlockAttributes); }); } if (targetBlocks.length === 1) { const title = (0, _blocks.getBlockType)(targetBlocks[0].name)?.title; createSuccessNotice((0, _i18n.sprintf)( // Translators: Name of the block being pasted, e.g. "Paragraph". (0, _i18n.__)('Pasted styles to %s.'), title), { type: 'snackbar' }); } else { createSuccessNotice((0, _i18n.sprintf)( // Translators: The number of the blocks. (0, _i18n.__)('Pasted styles to %d blocks.'), targetBlocks.length), { type: 'snackbar' }); } }, [registry.batch, updateBlockAttributes, createSuccessNotice, createWarningNotice, createErrorNotice]); } //# sourceMappingURL=index.js.map