UNPKG

@wordpress/block-editor

Version:
8 lines (7 loc) 9.71 kB
{ "version": 3, "sources": ["../../../src/components/use-paste-styles/index.js"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useCallback } from '@wordpress/element';\nimport { getBlockType, parse } from '@wordpress/blocks';\nimport { useDispatch, useRegistry } from '@wordpress/data';\nimport { store as noticesStore } from '@wordpress/notices';\nimport { __, sprintf } from '@wordpress/i18n';\n\n/**\n * Internal dependencies\n */\nimport { store as blockEditorStore } from '../../store';\nimport {\n\thasAlignSupport,\n\thasBorderSupport,\n\thasBackgroundColorSupport,\n\thasTextAlignSupport,\n\thasTextColorSupport,\n\thasGradientSupport,\n\thasCustomClassNameSupport,\n\thasFontFamilySupport,\n\thasFontSizeSupport,\n\thasLayoutSupport,\n\thasStyleSupport,\n} from '../../hooks/supports';\n\n/**\n * Determine if the copied text looks like serialized blocks or not.\n * Since plain text will always get parsed into a freeform block,\n * we check that if the parsed blocks is anything other than that.\n *\n * @param {string} text The copied text.\n * @return {boolean} True if the text looks like serialized blocks, false otherwise.\n */\nfunction hasSerializedBlocks( text ) {\n\ttry {\n\t\tconst blocks = parse( text, {\n\t\t\t__unstableSkipMigrationLogs: true,\n\t\t\t__unstableSkipAutop: true,\n\t\t} );\n\t\tif ( blocks.length === 1 && blocks[ 0 ].name === 'core/freeform' ) {\n\t\t\t// It's likely that the text is just plain text and not serialized blocks.\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t} catch ( err ) {\n\t\t// Parsing error, the text is not serialized blocks.\n\t\t// (Even though that it technically won't happen)\n\t\treturn false;\n\t}\n}\n\n/**\n * Style attributes are attributes being added in `block-editor/src/hooks/*`.\n * (Except for some unrelated to style like `anchor` or `settings`.)\n * They generally represent the default block supports.\n */\nconst STYLE_ATTRIBUTES = {\n\talign: hasAlignSupport,\n\tborderColor: ( nameOrType ) => hasBorderSupport( nameOrType, 'color' ),\n\tbackgroundColor: hasBackgroundColorSupport,\n\ttextAlign: hasTextAlignSupport,\n\ttextColor: hasTextColorSupport,\n\tgradient: hasGradientSupport,\n\tclassName: hasCustomClassNameSupport,\n\tfontFamily: hasFontFamilySupport,\n\tfontSize: hasFontSizeSupport,\n\tlayout: hasLayoutSupport,\n\tstyle: hasStyleSupport,\n};\n\n/**\n * Get the \"style attributes\" from a given block to a target block.\n *\n * @param {WPBlock} sourceBlock The source block.\n * @param {WPBlock} targetBlock The target block.\n * @return {Object} the filtered attributes object.\n */\nfunction getStyleAttributes( sourceBlock, targetBlock ) {\n\treturn Object.entries( STYLE_ATTRIBUTES ).reduce(\n\t\t( attributes, [ attributeKey, hasSupport ] ) => {\n\t\t\t// Only apply the attribute if both blocks support it.\n\t\t\tif (\n\t\t\t\thasSupport( sourceBlock.name ) &&\n\t\t\t\thasSupport( targetBlock.name )\n\t\t\t) {\n\t\t\t\t// Override attributes that are not present in the block to their defaults.\n\t\t\t\tattributes[ attributeKey ] =\n\t\t\t\t\tsourceBlock.attributes[ attributeKey ];\n\t\t\t}\n\t\t\treturn attributes;\n\t\t},\n\t\t{}\n\t);\n}\n\n/**\n * Update the target blocks with style attributes recursively.\n *\n * @param {WPBlock[]} targetBlocks The target blocks to be updated.\n * @param {WPBlock[]} sourceBlocks The source blocks to get th style attributes from.\n * @param {Function} updateBlockAttributes The function to update the attributes.\n */\nfunction recursivelyUpdateBlockAttributes(\n\ttargetBlocks,\n\tsourceBlocks,\n\tupdateBlockAttributes\n) {\n\tfor (\n\t\tlet index = 0;\n\t\tindex < Math.min( sourceBlocks.length, targetBlocks.length );\n\t\tindex += 1\n\t) {\n\t\tupdateBlockAttributes(\n\t\t\ttargetBlocks[ index ].clientId,\n\t\t\tgetStyleAttributes( sourceBlocks[ index ], targetBlocks[ index ] )\n\t\t);\n\n\t\trecursivelyUpdateBlockAttributes(\n\t\t\ttargetBlocks[ index ].innerBlocks,\n\t\t\tsourceBlocks[ index ].innerBlocks,\n\t\t\tupdateBlockAttributes\n\t\t);\n\t}\n}\n\n/**\n * A hook to return a pasteStyles event function for handling pasting styles to blocks.\n *\n * @return {Function} A function to update the styles to the blocks.\n */\nexport default function usePasteStyles() {\n\tconst registry = useRegistry();\n\tconst { updateBlockAttributes } = useDispatch( blockEditorStore );\n\tconst { createSuccessNotice, createWarningNotice, createErrorNotice } =\n\t\tuseDispatch( noticesStore );\n\n\treturn useCallback(\n\t\tasync ( targetBlocks ) => {\n\t\t\tlet html = '';\n\t\t\ttry {\n\t\t\t\t// `http:` sites won't have the clipboard property on navigator.\n\t\t\t\t// (with the exception of localhost.)\n\t\t\t\tif ( ! window.navigator.clipboard ) {\n\t\t\t\t\tcreateErrorNotice(\n\t\t\t\t\t\t__(\n\t\t\t\t\t\t\t'Unable to paste styles. This feature is only available on secure (https) sites in supporting browsers.'\n\t\t\t\t\t\t),\n\t\t\t\t\t\t{ type: 'snackbar' }\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\thtml = await window.navigator.clipboard.readText();\n\t\t\t} catch ( error ) {\n\t\t\t\t// Possibly the permission is denied.\n\t\t\t\tcreateErrorNotice(\n\t\t\t\t\t__(\n\t\t\t\t\t\t'Unable to paste styles. Please allow browser clipboard permissions before continuing.'\n\t\t\t\t\t),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Abort if the copied text is empty or doesn't look like serialized blocks.\n\t\t\tif ( ! html || ! hasSerializedBlocks( html ) ) {\n\t\t\t\tcreateWarningNotice(\n\t\t\t\t\t__(\n\t\t\t\t\t\t\"Unable to paste styles. Block styles couldn't be found within the copied content.\"\n\t\t\t\t\t),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst copiedBlocks = parse( html );\n\n\t\t\tif ( copiedBlocks.length === 1 ) {\n\t\t\t\t// Apply styles of the block to all the target blocks.\n\t\t\t\tregistry.batch( () => {\n\t\t\t\t\trecursivelyUpdateBlockAttributes(\n\t\t\t\t\t\ttargetBlocks,\n\t\t\t\t\t\ttargetBlocks.map( () => copiedBlocks[ 0 ] ),\n\t\t\t\t\t\tupdateBlockAttributes\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\tregistry.batch( () => {\n\t\t\t\t\trecursivelyUpdateBlockAttributes(\n\t\t\t\t\t\ttargetBlocks,\n\t\t\t\t\t\tcopiedBlocks,\n\t\t\t\t\t\tupdateBlockAttributes\n\t\t\t\t\t);\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tif ( targetBlocks.length === 1 ) {\n\t\t\t\tconst title = getBlockType( targetBlocks[ 0 ].name )?.title;\n\t\t\t\tcreateSuccessNotice(\n\t\t\t\t\tsprintf(\n\t\t\t\t\t\t// Translators: %s: Name of the block being pasted, e.g. \"Paragraph\".\n\t\t\t\t\t\t__( 'Pasted styles to %s.' ),\n\t\t\t\t\t\ttitle\n\t\t\t\t\t),\n\t\t\t\t\t{ type: 'snackbar' }\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tcreateSuccessNotice(\n\t\t\t\t\tsprintf(\n\t\t\t\t\t\t// Translators: %d: The number of the blocks.\n\t\t\t\t\t\t__( 'Pasted styles to %d blocks.' ),\n\t\t\t\t\t\ttargetBlocks.length\n\t\t\t\t\t),\n\t\t\t\t\t{ type: 'snackbar' }\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\t[\n\t\t\tregistry.batch,\n\t\t\tupdateBlockAttributes,\n\t\t\tcreateSuccessNotice,\n\t\t\tcreateWarningNotice,\n\t\t\tcreateErrorNotice,\n\t\t]\n\t);\n}\n"], "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,qBAA4B;AAC5B,oBAAoC;AACpC,kBAAyC;AACzC,qBAAsC;AACtC,kBAA4B;AAK5B,mBAA0C;AAC1C,sBAYO;AAUP,SAAS,oBAAqB,MAAO;AACpC,MAAI;AACH,UAAM,aAAS,qBAAO,MAAM;AAAA,MAC3B,6BAA6B;AAAA,MAC7B,qBAAqB;AAAA,IACtB,CAAE;AACF,QAAK,OAAO,WAAW,KAAK,OAAQ,CAAE,EAAE,SAAS,iBAAkB;AAElE,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR,SAAU,KAAM;AAGf,WAAO;AAAA,EACR;AACD;AAOA,IAAM,mBAAmB;AAAA,EACxB,OAAO;AAAA,EACP,aAAa,CAAE,mBAAgB,kCAAkB,YAAY,OAAQ;AAAA,EACrE,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AACR;AASA,SAAS,mBAAoB,aAAa,aAAc;AACvD,SAAO,OAAO,QAAS,gBAAiB,EAAE;AAAA,IACzC,CAAE,YAAY,CAAE,cAAc,UAAW,MAAO;AAE/C,UACC,WAAY,YAAY,IAAK,KAC7B,WAAY,YAAY,IAAK,GAC5B;AAED,mBAAY,YAAa,IACxB,YAAY,WAAY,YAAa;AAAA,MACvC;AACA,aAAO;AAAA,IACR;AAAA,IACA,CAAC;AAAA,EACF;AACD;AASA,SAAS,iCACR,cACA,cACA,uBACC;AACD,WACK,QAAQ,GACZ,QAAQ,KAAK,IAAK,aAAa,QAAQ,aAAa,MAAO,GAC3D,SAAS,GACR;AACD;AAAA,MACC,aAAc,KAAM,EAAE;AAAA,MACtB,mBAAoB,aAAc,KAAM,GAAG,aAAc,KAAM,CAAE;AAAA,IAClE;AAEA;AAAA,MACC,aAAc,KAAM,EAAE;AAAA,MACtB,aAAc,KAAM,EAAE;AAAA,MACtB;AAAA,IACD;AAAA,EACD;AACD;AAOe,SAAR,iBAAkC;AACxC,QAAM,eAAW,yBAAY;AAC7B,QAAM,EAAE,sBAAsB,QAAI,yBAAa,aAAAA,KAAiB;AAChE,QAAM,EAAE,qBAAqB,qBAAqB,kBAAkB,QACnE,yBAAa,eAAAC,KAAa;AAE3B,aAAO;AAAA,IACN,OAAQ,iBAAkB;AACzB,UAAI,OAAO;AACX,UAAI;AAGH,YAAK,CAAE,OAAO,UAAU,WAAY;AACnC;AAAA,gBACC;AAAA,cACC;AAAA,YACD;AAAA,YACA,EAAE,MAAM,WAAW;AAAA,UACpB;AACA;AAAA,QACD;AAEA,eAAO,MAAM,OAAO,UAAU,UAAU,SAAS;AAAA,MAClD,SAAU,OAAQ;AAEjB;AAAA,cACC;AAAA,YACC;AAAA,UACD;AAAA,UACA;AAAA,YACC,MAAM;AAAA,UACP;AAAA,QACD;AACA;AAAA,MACD;AAGA,UAAK,CAAE,QAAQ,CAAE,oBAAqB,IAAK,GAAI;AAC9C;AAAA,cACC;AAAA,YACC;AAAA,UACD;AAAA,UACA;AAAA,YACC,MAAM;AAAA,UACP;AAAA,QACD;AACA;AAAA,MACD;AAEA,YAAM,mBAAe,qBAAO,IAAK;AAEjC,UAAK,aAAa,WAAW,GAAI;AAEhC,iBAAS,MAAO,MAAM;AACrB;AAAA,YACC;AAAA,YACA,aAAa,IAAK,MAAM,aAAc,CAAE,CAAE;AAAA,YAC1C;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH,OAAO;AACN,iBAAS,MAAO,MAAM;AACrB;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,UACD;AAAA,QACD,CAAE;AAAA,MACH;AAEA,UAAK,aAAa,WAAW,GAAI;AAChC,cAAM,YAAQ,4BAAc,aAAc,CAAE,EAAE,IAAK,GAAG;AACtD;AAAA,cACC;AAAA;AAAA,gBAEC,gBAAI,sBAAuB;AAAA,YAC3B;AAAA,UACD;AAAA,UACA,EAAE,MAAM,WAAW;AAAA,QACpB;AAAA,MACD,OAAO;AACN;AAAA,cACC;AAAA;AAAA,gBAEC,gBAAI,6BAA8B;AAAA,YAClC,aAAa;AAAA,UACd;AAAA,UACA,EAAE,MAAM,WAAW;AAAA,QACpB;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACD;", "names": ["blockEditorStore", "noticesStore"] }