├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── README.md ├── assets └── js │ ├── extend-block-example.js │ └── spacing-control.js ├── dist ├── extend-block-example.js └── extend-block-example.js.map ├── extend-block-example.php ├── package-lock.json └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-react-jsx"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | indent_size = 4 16 | 17 | [*.{yml,json}] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.md] 22 | indent_style = space 23 | trim_trailing_whitespace = false 24 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.min.js 2 | **/*.build.js 3 | **/node_modules/** 4 | **/vendor/** 5 | build 6 | coverage 7 | cypress 8 | node_modules 9 | vendor 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": [ 5 | "wordpress", 6 | "plugin:react/recommended", 7 | "plugin:jsx-a11y/recommended", 8 | "plugin:jest/recommended" 9 | ], 10 | "env": { 11 | "browser": false, 12 | "es6": true, 13 | "node": true, 14 | "mocha": true, 15 | "jest/globals": true 16 | }, 17 | "parserOptions": { 18 | "sourceType": "module", 19 | "ecmaFeatures": { 20 | "jsx": true 21 | } 22 | }, 23 | "globals": { 24 | "wp": true, 25 | "wpApiSettings": true, 26 | "window": true, 27 | "document": true 28 | }, 29 | "plugins": ["react", "jsx-a11y", "jest"], 30 | "settings": { 31 | "react": { 32 | "pragma": "wp" 33 | } 34 | }, 35 | "rules": { 36 | "array-bracket-spacing": ["error", "always"], 37 | "brace-style": ["error", "1tbs"], 38 | "camelcase": ["error", { "properties": "never" }], 39 | "comma-dangle": ["error", "always-multiline"], 40 | "comma-spacing": "error", 41 | "comma-style": "error", 42 | "computed-property-spacing": ["error", "always"], 43 | "constructor-super": "error", 44 | "dot-notation": "error", 45 | "eol-last": "error", 46 | "eqeqeq": "error", 47 | "func-call-spacing": "error", 48 | "indent": ["error", "tab", { "SwitchCase": 1 }], 49 | "jsx-a11y/label-has-for": [ 50 | "error", 51 | { 52 | "required": "id" 53 | } 54 | ], 55 | "jsx-a11y/media-has-caption": "off", 56 | "jsx-a11y/no-noninteractive-tabindex": "off", 57 | "jsx-a11y/role-has-required-aria-props": "off", 58 | "jsx-quotes": "error", 59 | "key-spacing": "error", 60 | "keyword-spacing": "error", 61 | "lines-around-comment": "off", 62 | "no-alert": "error", 63 | "no-bitwise": "error", 64 | "no-caller": "error", 65 | "no-console": "error", 66 | "no-const-assign": "error", 67 | "no-debugger": "error", 68 | "no-dupe-args": "error", 69 | "no-dupe-class-members": "error", 70 | "no-dupe-keys": "error", 71 | "no-duplicate-case": "error", 72 | "no-duplicate-imports": "error", 73 | "no-else-return": "error", 74 | "no-eval": "error", 75 | "no-extra-semi": "error", 76 | "no-fallthrough": "error", 77 | "no-lonely-if": "error", 78 | "no-mixed-operators": "error", 79 | "no-mixed-spaces-and-tabs": "error", 80 | "no-multiple-empty-lines": ["error", { "max": 1 }], 81 | "no-multi-spaces": "error", 82 | "no-multi-str": "off", 83 | "no-negated-in-lhs": "error", 84 | "no-nested-ternary": "error", 85 | "no-redeclare": "error", 86 | "no-restricted-syntax": [ 87 | "error", 88 | { 89 | "selector": 90 | "ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]", 91 | "message": "Path access on WordPress dependencies is not allowed." 92 | }, 93 | { 94 | "selector": "ImportDeclaration[source.value=/^blocks$/]", 95 | "message": "Use @wordpress/blocks as import path instead." 96 | }, 97 | { 98 | "selector": "ImportDeclaration[source.value=/^components$/]", 99 | "message": "Use @wordpress/components as import path instead." 100 | }, 101 | { 102 | "selector": "ImportDeclaration[source.value=/^date$/]", 103 | "message": "Use @wordpress/date as import path instead." 104 | }, 105 | { 106 | "selector": "ImportDeclaration[source.value=/^editor$/]", 107 | "message": "Use @wordpress/editor as import path instead." 108 | }, 109 | { 110 | "selector": "ImportDeclaration[source.value=/^element$/]", 111 | "message": "Use @wordpress/element as import path instead." 112 | }, 113 | { 114 | "selector": "ImportDeclaration[source.value=/^i18n$/]", 115 | "message": "Use @wordpress/i18n as import path instead." 116 | }, 117 | { 118 | "selector": "ImportDeclaration[source.value=/^data$/]", 119 | "message": "Use @wordpress/data as import path instead." 120 | }, 121 | { 122 | "selector": "ImportDeclaration[source.value=/^utils$/]", 123 | "message": "Use @wordpress/utils as import path instead." 124 | }, 125 | { 126 | "selector": 127 | "CallExpression[callee.name=/^__|_n|_x$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])", 128 | "message": "Translate function arguments must be string literals." 129 | }, 130 | { 131 | "selector": 132 | "CallExpression[callee.name=/^_n|_x$/]:not([arguments.1.type=/^Literal|BinaryExpression$/])", 133 | "message": "Translate function arguments must be string literals." 134 | }, 135 | { 136 | "selector": 137 | "CallExpression[callee.name=_nx]:not([arguments.2.type=/^Literal|BinaryExpression$/])", 138 | "message": "Translate function arguments must be string literals." 139 | } 140 | ], 141 | "no-shadow": "error", 142 | "no-undef": "error", 143 | "no-undef-init": "error", 144 | "no-unreachable": "error", 145 | "no-unsafe-negation": "error", 146 | "no-unused-expressions": "error", 147 | "no-unused-vars": "error", 148 | "no-useless-computed-key": "error", 149 | "no-useless-constructor": "error", 150 | "no-useless-return": "error", 151 | "no-var": "error", 152 | "no-whitespace-before-property": "error", 153 | "object-curly-spacing": ["error", "always"], 154 | "padded-blocks": ["error", "never"], 155 | "prefer-const": "error", 156 | "quote-props": ["error", "as-needed"], 157 | "react/display-name": "off", 158 | "react/jsx-curly-spacing": [ 159 | "error", 160 | { 161 | "when": "always", 162 | "children": true 163 | } 164 | ], 165 | "react/jsx-equals-spacing": "error", 166 | "react/jsx-indent": ["error", "tab"], 167 | "react/jsx-indent-props": ["error", "tab"], 168 | "react/jsx-key": "error", 169 | "react/jsx-tag-spacing": "error", 170 | "react/no-children-prop": "off", 171 | "react/no-find-dom-node": "warn", 172 | "react/prop-types": "off", 173 | "semi": "error", 174 | "semi-spacing": "error", 175 | "space-before-blocks": ["error", "always"], 176 | "space-before-function-paren": ["error", "never"], 177 | "space-in-parens": ["error", "always"], 178 | "space-infix-ops": ["error", { "int32Hint": false }], 179 | "space-unary-ops": [ 180 | "error", 181 | { 182 | "overrides": { 183 | "!": true 184 | } 185 | } 186 | ], 187 | "template-curly-spacing": ["error", "always"], 188 | "valid-jsdoc": ["error", { "requireReturn": false }], 189 | "valid-typeof": "error", 190 | "yoda": "off" 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | vendor 3 | .cache/ 4 | composer.phar 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example how to extend an existing Gutenberg block in WordPress 2 | 3 | This is a WordPress plugin which shows how to extend an existing Gutenberg block. 4 | This blog post explains how it works: https://www.liip.ch/en/blog/how-to-extend-existing-gutenberg-blocks-in-wordpress. 5 | Feel free to use this plugin as a starting point to extend Gutenberg blocks in your WordPress site. 6 | 7 | ## Developer information 8 | 9 | ### Installation 10 | 11 | 1. Clone this repository 12 | 13 | 1. Install Node dependencies 14 | 15 | ``` 16 | $ npm install 17 | ``` 18 | 19 | ### Code style 20 | 21 | Run eslint with the following command: 22 | 23 | ``` 24 | $ npm run lint 25 | ``` 26 | 27 | ### Compile assets 28 | 29 | #### `npm start` 30 | - Use to compile and run the block in development mode. 31 | - Watches for any changes and reports back any errors in your code. 32 | 33 | #### `npm run build` 34 | - Use to build production code for your block inside `dist` folder. 35 | - Runs once and reports back the gzip file sizes of the produced code. 36 | -------------------------------------------------------------------------------- /assets/js/extend-block-example.js: -------------------------------------------------------------------------------- 1 | import './spacing-control'; 2 | -------------------------------------------------------------------------------- /assets/js/spacing-control.js: -------------------------------------------------------------------------------- 1 | import assign from 'lodash.assign'; 2 | 3 | const { createHigherOrderComponent } = wp.compose; 4 | const { Fragment } = wp.element; 5 | const { InspectorControls } = wp.editor; 6 | const { PanelBody, SelectControl } = wp.components; 7 | const { addFilter } = wp.hooks; 8 | const { __ } = wp.i18n; 9 | 10 | // Enable spacing control on the following blocks 11 | const enableSpacingControlOnBlocks = [ 12 | 'core/image', 13 | ]; 14 | 15 | // Available spacing control options 16 | const spacingControlOptions = [ 17 | { 18 | label: __( 'None' ), 19 | value: '', 20 | }, 21 | { 22 | label: __( 'Small' ), 23 | value: 'small', 24 | }, 25 | { 26 | label: __( 'Medium' ), 27 | value: 'medium', 28 | }, 29 | { 30 | label: __( 'Large' ), 31 | value: 'large', 32 | }, 33 | ]; 34 | 35 | /** 36 | * Add spacing control attribute to block. 37 | * 38 | * @param {object} settings Current block settings. 39 | * @param {string} name Name of block. 40 | * 41 | * @returns {object} Modified block settings. 42 | */ 43 | const addSpacingControlAttribute = ( settings, name ) => { 44 | // Do nothing if it's another block than our defined ones. 45 | if ( ! enableSpacingControlOnBlocks.includes( name ) ) { 46 | return settings; 47 | } 48 | 49 | // Use Lodash's assign to gracefully handle if attributes are undefined 50 | settings.attributes = assign( settings.attributes, { 51 | spacing: { 52 | type: 'string', 53 | default: spacingControlOptions[ 0 ].value, 54 | }, 55 | } ); 56 | 57 | return settings; 58 | }; 59 | 60 | addFilter( 'blocks.registerBlockType', 'extend-block-example/attribute/spacing', addSpacingControlAttribute ); 61 | 62 | /** 63 | * Create HOC to add spacing control to inspector controls of block. 64 | */ 65 | const withSpacingControl = createHigherOrderComponent( ( BlockEdit ) => { 66 | return ( props ) => { 67 | // Do nothing if it's another block than our defined ones. 68 | if ( ! enableSpacingControlOnBlocks.includes( props.name ) ) { 69 | return ( 70 | 71 | ); 72 | } 73 | 74 | const { spacing } = props.attributes; 75 | 76 | // add has-spacing-xy class to block 77 | if ( spacing ) { 78 | props.attributes.className = `has-spacing-${ spacing }`; 79 | } 80 | 81 | return ( 82 | 83 | 84 | 85 | 89 | { 94 | props.setAttributes( { 95 | spacing: selectedSpacingOption, 96 | } ); 97 | } } 98 | /> 99 | 100 | 101 | 102 | ); 103 | }; 104 | }, 'withSpacingControl' ); 105 | 106 | addFilter( 'editor.BlockEdit', 'extend-block-example/with-spacing-control', withSpacingControl ); 107 | 108 | /** 109 | * Add margin style attribute to save element of block. 110 | * 111 | * @param {object} saveElementProps Props of save element. 112 | * @param {Object} blockType Block type information. 113 | * @param {Object} attributes Attributes of block. 114 | * 115 | * @returns {object} Modified props of save element. 116 | */ 117 | const addSpacingExtraProps = ( saveElementProps, blockType, attributes ) => { 118 | // Do nothing if it's another block than our defined ones. 119 | if ( ! enableSpacingControlOnBlocks.includes( blockType.name ) ) { 120 | return saveElementProps; 121 | } 122 | 123 | const margins = { 124 | small: '5px', 125 | medium: '15px', 126 | large: '30px', 127 | }; 128 | 129 | if ( attributes.spacing in margins ) { 130 | // Use Lodash's assign to gracefully handle if attributes are undefined 131 | assign( saveElementProps, { style: { 'margin-bottom': margins[ attributes.spacing ] } } ); 132 | } 133 | 134 | return saveElementProps; 135 | }; 136 | 137 | addFilter( 'blocks.getSaveContent.extraProps', 'extend-block-example/get-save-content/extra-props', addSpacingExtraProps ); 138 | -------------------------------------------------------------------------------- /dist/extend-block-example.js: -------------------------------------------------------------------------------- 1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c1?t[u-1]:void 0,c=u>2?t[2]:void 0;for(o=n.length>3&&"function"==typeof o?(u--,o):void 0,c&&A(t[0],t[1],c)&&(o=u<3?void 0:o,u=1),r=Object(r);++e-1&&r%1==0&&r-1&&r%1==0&&r<=n}function M(n){var r=typeof n;return!!n&&("object"==r||"function"==r)}function P(n){return!!n&&"object"==typeof n}var $=O(function(n,r){if(y||w(r)||k(r))j(r,q(r),n);else for(var t in r)l.call(r,t)&&g(n,t,r[t])});function q(n){return k(n)?h(n):b(n)}module.exports=$; 3 | },{}],"0/j4":[function(require,module,exports) { 4 | "use strict";var e=t(require("lodash.assign"));function t(e){return e&&e.__esModule?e:{default:e}}var a=wp.compose.createHigherOrderComponent,n=wp.element.Fragment,l=wp.editor.InspectorControls,r=wp.components,i=r.PanelBody,c=r.SelectControl,o=wp.hooks.addFilter,s=wp.i18n.__,u=["core/image"],p=[{label:s("None"),value:""},{label:s("Small"),value:"small"},{label:s("Medium"),value:"medium"},{label:s("Large"),value:"large"}],m=function(t,a){return u.includes(a)?(t.attributes=(0,e.default)(t.attributes,{spacing:{type:"string",default:p[0].value}}),t):t};o("blocks.registerBlockType","extend-block-example/attribute/spacing",m);var g=a(function(e){return function(t){if(!u.includes(t.name))return React.createElement(e,t);var a=t.attributes.spacing;return a&&(t.attributes.className="has-spacing-".concat(a)),React.createElement(n,null,React.createElement(e,t),React.createElement(l,null,React.createElement(i,{title:s("My Spacing Control"),initialOpen:!0},React.createElement(c,{label:s("Spacing"),value:a,options:p,onChange:function(e){t.setAttributes({spacing:e})}}))))}},"withSpacingControl");o("editor.BlockEdit","extend-block-example/with-spacing-control",g);var d=function(t,a,n){if(!u.includes(a.name))return t;var l={small:"5px",medium:"15px",large:"30px"};return n.spacing in l&&(0,e.default)(t,{style:{"margin-bottom":l[n.spacing]}}),t};o("blocks.getSaveContent.extraProps","extend-block-example/get-save-content/extra-props",d); 5 | },{"lodash.assign":"9bLa"}],"KQUp":[function(require,module,exports) { 6 | "use strict";require("./spacing-control"); 7 | },{"./spacing-control":"0/j4"}]},{},["KQUp"], null) 8 | //# sourceMappingURL=/extend-block-example.js.map -------------------------------------------------------------------------------- /dist/extend-block-example.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../node_modules/lodash.assign/index.js","spacing-control.js","extend-block-example.js"],"names":["createHigherOrderComponent","wp","compose","Fragment","element","InspectorControls","editor","components","PanelBody","SelectControl","addFilter","hooks","__","i18n","enableSpacingControlOnBlocks","spacingControlOptions","label","value","addSpacingControlAttribute","settings","name","includes","attributes","spacing","type","default","withSpacingControl","BlockEdit","props","className","selectedSpacingOption","setAttributes","addSpacingExtraProps","saveElementProps","blockType","margins","small","medium","large","style"],"mappings":"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","file":"extend-block-example.js","sourceRoot":"../assets/js","sourcesContent":["/**\n * lodash (Custom Build) \n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]';\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\nfunction apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Built-in value references. */\nvar propertyIsEnumerable = objectProto.propertyIsEnumerable;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeKeys = overArg(Object.keys, Object),\n nativeMax = Math.max;\n\n/** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */\nvar nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n // Safari 9 makes `arguments.length` enumerable in strict mode.\n var result = (isArray(value) || isArguments(value))\n ? baseTimes(value.length, String)\n : [];\n\n var length = result.length,\n skipIndexes = !!length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n (value === undefined && !(key in object))) {\n object[key] = value;\n }\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\nfunction baseRest(func, start) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = array;\n return apply(func, this, otherArgs);\n };\n}\n\n/**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\nfunction copyObject(source, props, object, customizer) {\n object || (object = {});\n\n var index = -1,\n length = props.length;\n\n while (++index < length) {\n var key = props[index];\n\n var newValue = customizer\n ? customizer(object[key], source[key], key, object, source)\n : undefined;\n\n assignValue(object, key, newValue === undefined ? source[key] : newValue);\n }\n return object;\n}\n\n/**\n * Creates a function like `_.assign`.\n *\n * @private\n * @param {Function} assigner The function to assign values.\n * @returns {Function} Returns the new assigner function.\n */\nfunction createAssigner(assigner) {\n return baseRest(function(object, sources) {\n var index = -1,\n length = sources.length,\n customizer = length > 1 ? sources[length - 1] : undefined,\n guard = length > 2 ? sources[2] : undefined;\n\n customizer = (assigner.length > 3 && typeof customizer == 'function')\n ? (length--, customizer)\n : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n customizer = length < 3 ? undefined : customizer;\n length = 1;\n }\n object = Object(object);\n while (++index < length) {\n var source = sources[index];\n if (source) {\n assigner(object, source, index, customizer);\n }\n }\n return object;\n });\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if the given arguments are from an iteratee call.\n *\n * @private\n * @param {*} value The potential iteratee value argument.\n * @param {*} index The potential iteratee index or key argument.\n * @param {*} object The potential iteratee object argument.\n * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n * else `false`.\n */\nfunction isIterateeCall(value, index, object) {\n if (!isObject(object)) {\n return false;\n }\n var type = typeof index;\n if (type == 'number'\n ? (isArrayLike(object) && isIndex(index, object.length))\n : (type == 'string' && index in object)\n ) {\n return eq(object[index], value);\n }\n return false;\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 8-9 which returns 'object' for typed array and other constructors.\n var tag = isObject(value) ? objectToString.call(value) : '';\n return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Assigns own enumerable string keyed properties of source objects to the\n * destination object. Source objects are applied from left to right.\n * Subsequent sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object` and is loosely based on\n * [`Object.assign`](https://mdn.io/Object/assign).\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.assignIn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * function Bar() {\n * this.c = 3;\n * }\n *\n * Foo.prototype.b = 2;\n * Bar.prototype.d = 4;\n *\n * _.assign({ 'a': 0 }, new Foo, new Bar);\n * // => { 'a': 1, 'c': 3 }\n */\nvar assign = createAssigner(function(object, source) {\n if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {\n copyObject(source, keys(source), object);\n return;\n }\n for (var key in source) {\n if (hasOwnProperty.call(source, key)) {\n assignValue(object, key, source[key]);\n }\n }\n});\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\nmodule.exports = assign;\n","import assign from 'lodash.assign';\n\nconst { createHigherOrderComponent } = wp.compose;\nconst { Fragment } = wp.element;\nconst { InspectorControls } = wp.editor;\nconst { PanelBody, SelectControl } = wp.components;\nconst { addFilter } = wp.hooks;\nconst { __ } = wp.i18n;\n\n// Enable spacing control on the following blocks\nconst enableSpacingControlOnBlocks = [\n\t'core/image',\n];\n\n// Available spacing control options\nconst spacingControlOptions = [\n\t{\n\t\tlabel: __( 'None' ),\n\t\tvalue: '',\n\t},\n\t{\n\t\tlabel: __( 'Small' ),\n\t\tvalue: 'small',\n\t},\n\t{\n\t\tlabel: __( 'Medium' ),\n\t\tvalue: 'medium',\n\t},\n\t{\n\t\tlabel: __( 'Large' ),\n\t\tvalue: 'large',\n\t},\n];\n\n/**\n * Add spacing control attribute to block.\n *\n * @param {object} settings Current block settings.\n * @param {string} name Name of block.\n *\n * @returns {object} Modified block settings.\n */\nconst addSpacingControlAttribute = ( settings, name ) => {\n\t// Do nothing if it's another block than our defined ones.\n\tif ( ! enableSpacingControlOnBlocks.includes( name ) ) {\n\t\treturn settings;\n\t}\n\n\t// Use Lodash's assign to gracefully handle if attributes are undefined\n\tsettings.attributes = assign( settings.attributes, {\n\t\tspacing: {\n\t\t\ttype: 'string',\n\t\t\tdefault: spacingControlOptions[ 0 ].value,\n\t\t},\n\t} );\n\n\treturn settings;\n};\n\naddFilter( 'blocks.registerBlockType', 'extend-block-example/attribute/spacing', addSpacingControlAttribute );\n\n/**\n * Create HOC to add spacing control to inspector controls of block.\n */\nconst withSpacingControl = createHigherOrderComponent( ( BlockEdit ) => {\n\treturn ( props ) => {\n\t\t// Do nothing if it's another block than our defined ones.\n\t\tif ( ! enableSpacingControlOnBlocks.includes( props.name ) ) {\n\t\t\treturn (\n\t\t\t\t\n\t\t\t);\n\t\t}\n\n\t\tconst { spacing } = props.attributes;\n\n\t\t// add has-spacing-xy class to block\n\t\tif ( spacing ) {\n\t\t\tprops.attributes.className = `has-spacing-${ spacing }`;\n\t\t}\n\n\t\treturn (\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\tprops.setAttributes( {\n\t\t\t\t\t\t\t\t\tspacing: selectedSpacingOption,\n\t\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t/>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t);\n\t};\n}, 'withSpacingControl' );\n\naddFilter( 'editor.BlockEdit', 'extend-block-example/with-spacing-control', withSpacingControl );\n\n/**\n * Add margin style attribute to save element of block.\n *\n * @param {object} saveElementProps Props of save element.\n * @param {Object} blockType Block type information.\n * @param {Object} attributes Attributes of block.\n *\n * @returns {object} Modified props of save element.\n */\nconst addSpacingExtraProps = ( saveElementProps, blockType, attributes ) => {\n\t// Do nothing if it's another block than our defined ones.\n\tif ( ! enableSpacingControlOnBlocks.includes( blockType.name ) ) {\n\t\treturn saveElementProps;\n\t}\n\n\tconst margins = {\n\t\tsmall: '5px',\n\t\tmedium: '15px',\n\t\tlarge: '30px',\n\t};\n\n\tif ( attributes.spacing in margins ) {\n\t\t// Use Lodash's assign to gracefully handle if attributes are undefined\n\t\tassign( saveElementProps, { style: { 'margin-bottom': margins[ attributes.spacing ] } } );\n\t}\n\n\treturn saveElementProps;\n};\n\naddFilter( 'blocks.getSaveContent.extraProps', 'extend-block-example/get-save-content/extra-props', addSpacingExtraProps );\n","import './spacing-control';\n"]} -------------------------------------------------------------------------------- /extend-block-example.php: -------------------------------------------------------------------------------- 1 |