@wordpress/patterns 1.15.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/api/index.js +22 -0
  3. package/build/api/index.js.map +1 -0
  4. package/build/components/allow-overrides-modal.js +114 -0
  5. package/build/components/allow-overrides-modal.js.map +1 -0
  6. package/build/components/category-selector.js.map +1 -1
  7. package/build/components/create-pattern-modal.js.map +1 -1
  8. package/build/components/duplicate-pattern-modal.js.map +1 -1
  9. package/build/components/index.js.map +1 -1
  10. package/build/components/overrides-panel.js +44 -0
  11. package/build/components/overrides-panel.js.map +1 -0
  12. package/build/components/pattern-convert-button.js.map +1 -1
  13. package/build/components/pattern-overrides-controls.js +107 -0
  14. package/build/components/pattern-overrides-controls.js.map +1 -0
  15. package/build/components/patterns-manage-button.js.map +1 -1
  16. package/build/components/rename-pattern-category-modal.js.map +1 -1
  17. package/build/components/rename-pattern-modal.js.map +1 -1
  18. package/build/components/reset-overrides-control.js.map +1 -1
  19. package/build/constants.js +2 -1
  20. package/build/constants.js.map +1 -1
  21. package/build/index.js.map +1 -1
  22. package/build/index.native.js.map +1 -1
  23. package/build/lock-unlock.js.map +1 -1
  24. package/build/private-apis.js +7 -3
  25. package/build/private-apis.js.map +1 -1
  26. package/build/private-hooks.js.map +1 -1
  27. package/build/store/actions.js.map +1 -1
  28. package/build/store/constants.js.map +1 -1
  29. package/build/store/index.js +1 -1
  30. package/build/store/index.js.map +1 -1
  31. package/build/store/reducer.js.map +1 -1
  32. package/build/store/selectors.js.map +1 -1
  33. package/build-module/api/index.js +16 -0
  34. package/build-module/api/index.js.map +1 -0
  35. package/build-module/components/allow-overrides-modal.js +106 -0
  36. package/build-module/components/allow-overrides-modal.js.map +1 -0
  37. package/build-module/components/category-selector.js.map +1 -1
  38. package/build-module/components/create-pattern-modal.js.map +1 -1
  39. package/build-module/components/duplicate-pattern-modal.js.map +1 -1
  40. package/build-module/components/index.js.map +1 -1
  41. package/build-module/components/overrides-panel.js +37 -0
  42. package/build-module/components/overrides-panel.js.map +1 -0
  43. package/build-module/components/pattern-convert-button.js.map +1 -1
  44. package/build-module/components/pattern-overrides-controls.js +100 -0
  45. package/build-module/components/pattern-overrides-controls.js.map +1 -0
  46. package/build-module/components/patterns-manage-button.js.map +1 -1
  47. package/build-module/components/rename-pattern-category-modal.js.map +1 -1
  48. package/build-module/components/rename-pattern-modal.js.map +1 -1
  49. package/build-module/components/reset-overrides-control.js.map +1 -1
  50. package/build-module/constants.js +1 -0
  51. package/build-module/constants.js.map +1 -1
  52. package/build-module/index.js.map +1 -1
  53. package/build-module/index.native.js.map +1 -1
  54. package/build-module/lock-unlock.js.map +1 -1
  55. package/build-module/private-apis.js +6 -2
  56. package/build-module/private-apis.js.map +1 -1
  57. package/build-module/private-hooks.js.map +1 -1
  58. package/build-module/store/actions.js.map +1 -1
  59. package/build-module/store/constants.js.map +1 -1
  60. package/build-module/store/index.js.map +1 -1
  61. package/build-module/store/reducer.js.map +1 -1
  62. package/build-module/store/selectors.js.map +1 -1
  63. package/build-style/style-rtl.css +6 -1
  64. package/build-style/style.css +6 -1
  65. package/package.json +16 -16
  66. package/src/api/index.js +24 -0
  67. package/src/components/allow-overrides-modal.js +149 -0
  68. package/src/components/overrides-panel.js +45 -0
  69. package/src/components/pattern-overrides-controls.js +145 -0
  70. package/src/components/style.scss +6 -0
  71. package/src/constants.js +2 -0
  72. package/src/private-apis.js +6 -2
  73. package/build/components/use-set-pattern-bindings.js +0 -103
  74. package/build/components/use-set-pattern-bindings.js.map +0 -1
  75. package/build-module/components/use-set-pattern-bindings.js +0 -96
  76. package/build-module/components/use-set-pattern-bindings.js.map +0 -1
  77. package/src/components/use-set-pattern-bindings.js +0 -120
@@ -1 +1 @@
1
- {"version":3,"names":["cloneBlock","store","coreStore","blockEditorStore","PATTERN_SYNC_TYPES","createPattern","title","syncType","content","categories","registry","meta","unsynced","wp_pattern_sync_status","undefined","reusableBlock","status","wp_pattern_category","updatedRecord","dispatch","saveEntityRecord","createPatternFromFile","file","fileContent","text","parsedContent","JSON","parse","e","Error","__file","syncStatus","pattern","convertSyncedPatternToStatic","clientId","patternBlock","select","getBlock","cloneBlocksAndRemoveBindings","blocks","map","block","metadata","attributes","id","bindings","Object","keys","length","innerBlocks","replaceBlocks","setEditingPattern","isEditing","type"],"sources":["@wordpress/patterns/src/store/actions.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\n\nimport { cloneBlock } from '@wordpress/blocks';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { store as blockEditorStore } from '@wordpress/block-editor';\n\n/**\n * Internal dependencies\n */\nimport { PATTERN_SYNC_TYPES } from '../constants';\n\n/**\n * Returns a generator converting one or more static blocks into a pattern, or creating a new empty pattern.\n *\n * @param {string} title Pattern title.\n * @param {'full'|'unsynced'} syncType They way block is synced, 'full' or 'unsynced'.\n * @param {string|undefined} [content] Optional serialized content of blocks to convert to pattern.\n * @param {number[]|undefined} [categories] Ids of any selected categories.\n */\nexport const createPattern =\n\t( title, syncType, content, categories ) =>\n\tasync ( { registry } ) => {\n\t\tconst meta =\n\t\t\tsyncType === PATTERN_SYNC_TYPES.unsynced\n\t\t\t\t? {\n\t\t\t\t\t\twp_pattern_sync_status: syncType,\n\t\t\t\t }\n\t\t\t\t: undefined;\n\n\t\tconst reusableBlock = {\n\t\t\ttitle,\n\t\t\tcontent,\n\t\t\tstatus: 'publish',\n\t\t\tmeta,\n\t\t\twp_pattern_category: categories,\n\t\t};\n\n\t\tconst updatedRecord = await registry\n\t\t\t.dispatch( coreStore )\n\t\t\t.saveEntityRecord( 'postType', 'wp_block', reusableBlock );\n\n\t\treturn updatedRecord;\n\t};\n\n/**\n * Create a pattern from a JSON file.\n * @param {File} file The JSON file instance of the pattern.\n * @param {number[]|undefined} [categories] Ids of any selected categories.\n */\nexport const createPatternFromFile =\n\t( file, categories ) =>\n\tasync ( { dispatch } ) => {\n\t\tconst fileContent = await file.text();\n\t\t/** @type {import('./types').PatternJSON} */\n\t\tlet parsedContent;\n\t\ttry {\n\t\t\tparsedContent = JSON.parse( fileContent );\n\t\t} catch ( e ) {\n\t\t\tthrow new Error( 'Invalid JSON file' );\n\t\t}\n\t\tif (\n\t\t\tparsedContent.__file !== 'wp_block' ||\n\t\t\t! parsedContent.title ||\n\t\t\t! parsedContent.content ||\n\t\t\ttypeof parsedContent.title !== 'string' ||\n\t\t\ttypeof parsedContent.content !== 'string' ||\n\t\t\t( parsedContent.syncStatus &&\n\t\t\t\ttypeof parsedContent.syncStatus !== 'string' )\n\t\t) {\n\t\t\tthrow new Error( 'Invalid pattern JSON file' );\n\t\t}\n\n\t\tconst pattern = await dispatch.createPattern(\n\t\t\tparsedContent.title,\n\t\t\tparsedContent.syncStatus,\n\t\t\tparsedContent.content,\n\t\t\tcategories\n\t\t);\n\n\t\treturn pattern;\n\t};\n\n/**\n * Returns a generator converting a synced pattern block into a static block.\n *\n * @param {string} clientId The client ID of the block to attach.\n */\nexport const convertSyncedPatternToStatic =\n\t( clientId ) =>\n\t( { registry } ) => {\n\t\tconst patternBlock = registry\n\t\t\t.select( blockEditorStore )\n\t\t\t.getBlock( clientId );\n\n\t\tfunction cloneBlocksAndRemoveBindings( blocks ) {\n\t\t\treturn blocks.map( ( block ) => {\n\t\t\t\tlet metadata = block.attributes.metadata;\n\t\t\t\tif ( metadata ) {\n\t\t\t\t\tmetadata = { ...metadata };\n\t\t\t\t\tdelete metadata.id;\n\t\t\t\t\tdelete metadata.bindings;\n\t\t\t\t}\n\t\t\t\treturn cloneBlock(\n\t\t\t\t\tblock,\n\t\t\t\t\t{\n\t\t\t\t\t\tmetadata:\n\t\t\t\t\t\t\tmetadata && Object.keys( metadata ).length > 0\n\t\t\t\t\t\t\t\t? metadata\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t},\n\t\t\t\t\tcloneBlocksAndRemoveBindings( block.innerBlocks )\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\tregistry\n\t\t\t.dispatch( blockEditorStore )\n\t\t\t.replaceBlocks(\n\t\t\t\tpatternBlock.clientId,\n\t\t\t\tcloneBlocksAndRemoveBindings( patternBlock.innerBlocks )\n\t\t\t);\n\t};\n\n/**\n * Returns an action descriptor for SET_EDITING_PATTERN action.\n *\n * @param {string} clientId The clientID of the pattern to target.\n * @param {boolean} isEditing Whether the block should be in editing state.\n * @return {Object} Action descriptor.\n */\nexport function setEditingPattern( clientId, isEditing ) {\n\treturn {\n\t\ttype: 'SET_EDITING_PATTERN',\n\t\tclientId,\n\t\tisEditing,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"names":["cloneBlock","store","coreStore","blockEditorStore","PATTERN_SYNC_TYPES","createPattern","title","syncType","content","categories","registry","meta","unsynced","wp_pattern_sync_status","undefined","reusableBlock","status","wp_pattern_category","updatedRecord","dispatch","saveEntityRecord","createPatternFromFile","file","fileContent","text","parsedContent","JSON","parse","e","Error","__file","syncStatus","pattern","convertSyncedPatternToStatic","clientId","patternBlock","select","getBlock","cloneBlocksAndRemoveBindings","blocks","map","block","metadata","attributes","id","bindings","Object","keys","length","innerBlocks","replaceBlocks","setEditingPattern","isEditing","type"],"sources":["@wordpress/patterns/src/store/actions.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\n\nimport { cloneBlock } from '@wordpress/blocks';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { store as blockEditorStore } from '@wordpress/block-editor';\n\n/**\n * Internal dependencies\n */\nimport { PATTERN_SYNC_TYPES } from '../constants';\n\n/**\n * Returns a generator converting one or more static blocks into a pattern, or creating a new empty pattern.\n *\n * @param {string} title Pattern title.\n * @param {'full'|'unsynced'} syncType They way block is synced, 'full' or 'unsynced'.\n * @param {string|undefined} [content] Optional serialized content of blocks to convert to pattern.\n * @param {number[]|undefined} [categories] Ids of any selected categories.\n */\nexport const createPattern =\n\t( title, syncType, content, categories ) =>\n\tasync ( { registry } ) => {\n\t\tconst meta =\n\t\t\tsyncType === PATTERN_SYNC_TYPES.unsynced\n\t\t\t\t? {\n\t\t\t\t\t\twp_pattern_sync_status: syncType,\n\t\t\t\t }\n\t\t\t\t: undefined;\n\n\t\tconst reusableBlock = {\n\t\t\ttitle,\n\t\t\tcontent,\n\t\t\tstatus: 'publish',\n\t\t\tmeta,\n\t\t\twp_pattern_category: categories,\n\t\t};\n\n\t\tconst updatedRecord = await registry\n\t\t\t.dispatch( coreStore )\n\t\t\t.saveEntityRecord( 'postType', 'wp_block', reusableBlock );\n\n\t\treturn updatedRecord;\n\t};\n\n/**\n * Create a pattern from a JSON file.\n * @param {File} file The JSON file instance of the pattern.\n * @param {number[]|undefined} [categories] Ids of any selected categories.\n */\nexport const createPatternFromFile =\n\t( file, categories ) =>\n\tasync ( { dispatch } ) => {\n\t\tconst fileContent = await file.text();\n\t\t/** @type {import('./types').PatternJSON} */\n\t\tlet parsedContent;\n\t\ttry {\n\t\t\tparsedContent = JSON.parse( fileContent );\n\t\t} catch ( e ) {\n\t\t\tthrow new Error( 'Invalid JSON file' );\n\t\t}\n\t\tif (\n\t\t\tparsedContent.__file !== 'wp_block' ||\n\t\t\t! parsedContent.title ||\n\t\t\t! parsedContent.content ||\n\t\t\ttypeof parsedContent.title !== 'string' ||\n\t\t\ttypeof parsedContent.content !== 'string' ||\n\t\t\t( parsedContent.syncStatus &&\n\t\t\t\ttypeof parsedContent.syncStatus !== 'string' )\n\t\t) {\n\t\t\tthrow new Error( 'Invalid pattern JSON file' );\n\t\t}\n\n\t\tconst pattern = await dispatch.createPattern(\n\t\t\tparsedContent.title,\n\t\t\tparsedContent.syncStatus,\n\t\t\tparsedContent.content,\n\t\t\tcategories\n\t\t);\n\n\t\treturn pattern;\n\t};\n\n/**\n * Returns a generator converting a synced pattern block into a static block.\n *\n * @param {string} clientId The client ID of the block to attach.\n */\nexport const convertSyncedPatternToStatic =\n\t( clientId ) =>\n\t( { registry } ) => {\n\t\tconst patternBlock = registry\n\t\t\t.select( blockEditorStore )\n\t\t\t.getBlock( clientId );\n\n\t\tfunction cloneBlocksAndRemoveBindings( blocks ) {\n\t\t\treturn blocks.map( ( block ) => {\n\t\t\t\tlet metadata = block.attributes.metadata;\n\t\t\t\tif ( metadata ) {\n\t\t\t\t\tmetadata = { ...metadata };\n\t\t\t\t\tdelete metadata.id;\n\t\t\t\t\tdelete metadata.bindings;\n\t\t\t\t}\n\t\t\t\treturn cloneBlock(\n\t\t\t\t\tblock,\n\t\t\t\t\t{\n\t\t\t\t\t\tmetadata:\n\t\t\t\t\t\t\tmetadata && Object.keys( metadata ).length > 0\n\t\t\t\t\t\t\t\t? metadata\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t},\n\t\t\t\t\tcloneBlocksAndRemoveBindings( block.innerBlocks )\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\tregistry\n\t\t\t.dispatch( blockEditorStore )\n\t\t\t.replaceBlocks(\n\t\t\t\tpatternBlock.clientId,\n\t\t\t\tcloneBlocksAndRemoveBindings( patternBlock.innerBlocks )\n\t\t\t);\n\t};\n\n/**\n * Returns an action descriptor for SET_EDITING_PATTERN action.\n *\n * @param {string} clientId The clientID of the pattern to target.\n * @param {boolean} isEditing Whether the block should be in editing state.\n * @return {Object} Action descriptor.\n */\nexport function setEditingPattern( clientId, isEditing ) {\n\treturn {\n\t\ttype: 'SET_EDITING_PATTERN',\n\t\tclientId,\n\t\tisEditing,\n\t};\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["STORE_NAME"],"sources":["@wordpress/patterns/src/store/constants.js"],"sourcesContent":["/**\n * Module Constants\n */\nexport const STORE_NAME = 'core/patterns';\n"],"mappings":"AAAA;AACA;AACA;AACA,OAAO,MAAMA,UAAU,GAAG,eAAe"}
1
+ {"version":3,"names":["STORE_NAME"],"sources":["@wordpress/patterns/src/store/constants.js"],"sourcesContent":["/**\n * Module Constants\n */\nexport const STORE_NAME = 'core/patterns';\n"],"mappings":"AAAA;AACA;AACA;AACA,OAAO,MAAMA,UAAU,GAAG,eAAe","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["createReduxStore","register","reducer","actions","STORE_NAME","selectors","unlock","storeConfig","store","registerPrivateActions","registerPrivateSelectors"],"sources":["@wordpress/patterns/src/store/index.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { createReduxStore, register } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport reducer from './reducer';\nimport * as actions from './actions';\nimport { STORE_NAME } from './constants';\nimport * as selectors from './selectors';\nimport { unlock } from '../lock-unlock';\n\n/**\n * Post editor data store configuration.\n *\n * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#registerStore\n *\n * @type {Object}\n */\nexport const storeConfig = {\n\treducer,\n};\n\n/**\n * Store definition for the editor namespace.\n *\n * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore\n *\n * @type {Object}\n */\nexport const store = createReduxStore( STORE_NAME, {\n\t...storeConfig,\n} );\n\nregister( store );\nunlock( store ).registerPrivateActions( actions );\nunlock( store ).registerPrivateSelectors( selectors );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,gBAAgB,EAAEC,QAAQ,QAAQ,iBAAiB;;AAE5D;AACA;AACA;AACA,OAAOC,OAAO,MAAM,WAAW;AAC/B,OAAO,KAAKC,OAAO,MAAM,WAAW;AACpC,SAASC,UAAU,QAAQ,aAAa;AACxC,OAAO,KAAKC,SAAS,MAAM,aAAa;AACxC,SAASC,MAAM,QAAQ,gBAAgB;;AAEvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,WAAW,GAAG;EAC1BL;AACD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMM,KAAK,GAAGR,gBAAgB,CAAEI,UAAU,EAAE;EAClD,GAAGG;AACJ,CAAE,CAAC;AAEHN,QAAQ,CAAEO,KAAM,CAAC;AACjBF,MAAM,CAAEE,KAAM,CAAC,CAACC,sBAAsB,CAAEN,OAAQ,CAAC;AACjDG,MAAM,CAAEE,KAAM,CAAC,CAACE,wBAAwB,CAAEL,SAAU,CAAC"}
1
+ {"version":3,"names":["createReduxStore","register","reducer","actions","STORE_NAME","selectors","unlock","storeConfig","store","registerPrivateActions","registerPrivateSelectors"],"sources":["@wordpress/patterns/src/store/index.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { createReduxStore, register } from '@wordpress/data';\n\n/**\n * Internal dependencies\n */\nimport reducer from './reducer';\nimport * as actions from './actions';\nimport { STORE_NAME } from './constants';\nimport * as selectors from './selectors';\nimport { unlock } from '../lock-unlock';\n\n/**\n * Post editor data store configuration.\n *\n * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#registerStore\n *\n * @type {Object}\n */\nexport const storeConfig = {\n\treducer,\n};\n\n/**\n * Store definition for the editor namespace.\n *\n * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore\n *\n * @type {Object}\n */\nexport const store = createReduxStore( STORE_NAME, {\n\t...storeConfig,\n} );\n\nregister( store );\nunlock( store ).registerPrivateActions( actions );\nunlock( store ).registerPrivateSelectors( selectors );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,gBAAgB,EAAEC,QAAQ,QAAQ,iBAAiB;;AAE5D;AACA;AACA;AACA,OAAOC,OAAO,MAAM,WAAW;AAC/B,OAAO,KAAKC,OAAO,MAAM,WAAW;AACpC,SAASC,UAAU,QAAQ,aAAa;AACxC,OAAO,KAAKC,SAAS,MAAM,aAAa;AACxC,SAASC,MAAM,QAAQ,gBAAgB;;AAEvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,WAAW,GAAG;EAC1BL;AACD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMM,KAAK,GAAGR,gBAAgB,CAAEI,UAAU,EAAE;EAClD,GAAGG;AACJ,CAAE,CAAC;AAEHN,QAAQ,CAAEO,KAAM,CAAC;AACjBF,MAAM,CAAEE,KAAM,CAAC,CAACC,sBAAsB,CAAEN,OAAQ,CAAC;AACjDG,MAAM,CAAEE,KAAM,CAAC,CAACE,wBAAwB,CAAEL,SAAU,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["combineReducers","isEditingPattern","state","action","type","clientId","isEditing"],"sources":["@wordpress/patterns/src/store/reducer.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { combineReducers } from '@wordpress/data';\n\nexport function isEditingPattern( state = {}, action ) {\n\tif ( action?.type === 'SET_EDITING_PATTERN' ) {\n\t\treturn {\n\t\t\t...state,\n\t\t\t[ action.clientId ]: action.isEditing,\n\t\t};\n\t}\n\n\treturn state;\n}\n\nexport default combineReducers( {\n\tisEditingPattern,\n} );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,eAAe,QAAQ,iBAAiB;AAEjD,OAAO,SAASC,gBAAgBA,CAAEC,KAAK,GAAG,CAAC,CAAC,EAAEC,MAAM,EAAG;EACtD,IAAKA,MAAM,EAAEC,IAAI,KAAK,qBAAqB,EAAG;IAC7C,OAAO;MACN,GAAGF,KAAK;MACR,CAAEC,MAAM,CAACE,QAAQ,GAAIF,MAAM,CAACG;IAC7B,CAAC;EACF;EAEA,OAAOJ,KAAK;AACb;AAEA,eAAeF,eAAe,CAAE;EAC/BC;AACD,CAAE,CAAC"}
1
+ {"version":3,"names":["combineReducers","isEditingPattern","state","action","type","clientId","isEditing"],"sources":["@wordpress/patterns/src/store/reducer.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { combineReducers } from '@wordpress/data';\n\nexport function isEditingPattern( state = {}, action ) {\n\tif ( action?.type === 'SET_EDITING_PATTERN' ) {\n\t\treturn {\n\t\t\t...state,\n\t\t\t[ action.clientId ]: action.isEditing,\n\t\t};\n\t}\n\n\treturn state;\n}\n\nexport default combineReducers( {\n\tisEditingPattern,\n} );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,eAAe,QAAQ,iBAAiB;AAEjD,OAAO,SAASC,gBAAgBA,CAAEC,KAAK,GAAG,CAAC,CAAC,EAAEC,MAAM,EAAG;EACtD,IAAKA,MAAM,EAAEC,IAAI,KAAK,qBAAqB,EAAG;IAC7C,OAAO;MACN,GAAGF,KAAK;MACR,CAAEC,MAAM,CAACE,QAAQ,GAAIF,MAAM,CAACG;IAC7B,CAAC;EACF;EAEA,OAAOJ,KAAK;AACb;AAEA,eAAeF,eAAe,CAAE;EAC/BC;AACD,CAAE,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["isEditingPattern","state","clientId"],"sources":["@wordpress/patterns/src/store/selectors.js"],"sourcesContent":["/**\n * Returns true if pattern is in the editing state.\n *\n * @param {Object} state Global application state.\n * @param {number} clientId the clientID of the block.\n * @return {boolean} Whether the pattern is in the editing state.\n */\nexport function isEditingPattern( state, clientId ) {\n\treturn state.isEditingPattern[ clientId ];\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,gBAAgBA,CAAEC,KAAK,EAAEC,QAAQ,EAAG;EACnD,OAAOD,KAAK,CAACD,gBAAgB,CAAEE,QAAQ,CAAE;AAC1C"}
1
+ {"version":3,"names":["isEditingPattern","state","clientId"],"sources":["@wordpress/patterns/src/store/selectors.js"],"sourcesContent":["/**\n * Returns true if pattern is in the editing state.\n *\n * @param {Object} state Global application state.\n * @param {number} clientId the clientID of the block.\n * @return {boolean} Whether the pattern is in the editing state.\n */\nexport function isEditingPattern( state, clientId ) {\n\treturn state.isEditingPattern[ clientId ];\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,gBAAgBA,CAAEC,KAAK,EAAEC,QAAQ,EAAG;EACnD,OAAOD,KAAK,CAACD,gBAAgB,CAAEE,QAAQ,CAAE;AAC1C","ignoreList":[]}
@@ -96,7 +96,7 @@
96
96
  --wp-admin-border-width-focus: 2px;
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
- --wp-bound-block-color: #9747ff;
99
+ --wp-bound-block-color: var(--wp-block-synced-color);
100
100
  }
101
101
  @media (min-resolution: 192dpi) {
102
102
  :root {
@@ -139,4 +139,9 @@
139
139
  .patterns-rename-pattern-category-modal__validation-message {
140
140
  width: 320px;
141
141
  }
142
+ }
143
+
144
+ .pattern-overrides-control__allow-overrides-button {
145
+ width: 100%;
146
+ justify-content: center;
142
147
  }
@@ -96,7 +96,7 @@
96
96
  --wp-admin-border-width-focus: 2px;
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
- --wp-bound-block-color: #9747ff;
99
+ --wp-bound-block-color: var(--wp-block-synced-color);
100
100
  }
101
101
  @media (min-resolution: 192dpi) {
102
102
  :root {
@@ -139,4 +139,9 @@
139
139
  .patterns-rename-pattern-category-modal__validation-message {
140
140
  width: 320px;
141
141
  }
142
+ }
143
+
144
+ .pattern-overrides-control__allow-overrides-button {
145
+ width: 100%;
146
+ justify-content: center;
142
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/patterns",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "Management of user pattern editing.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -31,20 +31,20 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "@babel/runtime": "^7.16.0",
34
- "@wordpress/a11y": "^3.54.0",
35
- "@wordpress/block-editor": "^12.22.0",
36
- "@wordpress/blocks": "^12.31.0",
37
- "@wordpress/components": "^27.2.0",
38
- "@wordpress/compose": "^6.31.0",
39
- "@wordpress/core-data": "^6.31.0",
40
- "@wordpress/data": "^9.24.0",
41
- "@wordpress/element": "^5.31.0",
42
- "@wordpress/html-entities": "^3.54.0",
43
- "@wordpress/i18n": "^4.54.0",
44
- "@wordpress/icons": "^9.45.0",
45
- "@wordpress/notices": "^4.22.0",
46
- "@wordpress/private-apis": "^0.36.0",
47
- "@wordpress/url": "^3.55.0"
34
+ "@wordpress/a11y": "^3.56.0",
35
+ "@wordpress/block-editor": "^12.24.0",
36
+ "@wordpress/blocks": "^12.33.0",
37
+ "@wordpress/components": "^27.4.0",
38
+ "@wordpress/compose": "^6.33.0",
39
+ "@wordpress/core-data": "^6.33.0",
40
+ "@wordpress/data": "^9.26.0",
41
+ "@wordpress/element": "^5.33.0",
42
+ "@wordpress/html-entities": "^3.56.0",
43
+ "@wordpress/i18n": "^4.56.0",
44
+ "@wordpress/icons": "^9.47.0",
45
+ "@wordpress/notices": "^4.24.0",
46
+ "@wordpress/private-apis": "^0.38.0",
47
+ "@wordpress/url": "^3.57.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": "^18.0.0",
@@ -53,5 +53,5 @@
53
53
  "publishConfig": {
54
54
  "access": "public"
55
55
  },
56
- "gitHead": "ffc07735d0abfb3f69e91d48f25b7fe8d1ef92d2"
56
+ "gitHead": "280403b4c1cf6cc2c55a6c4d56f9ef91145e4191"
57
57
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { PARTIAL_SYNCING_SUPPORTED_BLOCKS } from '../constants';
5
+
6
+ /**
7
+ * Determines whether a block is overridable.
8
+ *
9
+ * @param {WPBlock} block The block to test.
10
+ *
11
+ * @return {boolean} `true` if a block is overridable, `false` otherwise.
12
+ */
13
+ export function isOverridableBlock( block ) {
14
+ return (
15
+ Object.keys( PARTIAL_SYNCING_SUPPORTED_BLOCKS ).includes(
16
+ block.name
17
+ ) &&
18
+ !! block.attributes.metadata?.name &&
19
+ !! block.attributes.metadata?.bindings &&
20
+ Object.values( block.attributes.metadata.bindings ).some(
21
+ ( binding ) => binding.source === 'core/pattern-overrides'
22
+ )
23
+ );
24
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ __experimentalHStack as HStack,
6
+ __experimentalVStack as VStack,
7
+ Button,
8
+ __experimentalText as Text,
9
+ TextControl,
10
+ Modal,
11
+ } from '@wordpress/components';
12
+ import { __, sprintf } from '@wordpress/i18n';
13
+ import { useState, useId } from '@wordpress/element';
14
+ import { speak } from '@wordpress/a11y';
15
+
16
+ export function AllowOverridesModal( {
17
+ placeholder,
18
+ initialName = '',
19
+ onClose,
20
+ onSave,
21
+ } ) {
22
+ const [ editedBlockName, setEditedBlockName ] = useState( initialName );
23
+ const descriptionId = useId();
24
+
25
+ const isNameValid = !! editedBlockName.trim();
26
+
27
+ const handleSubmit = () => {
28
+ if ( editedBlockName !== initialName ) {
29
+ const message = sprintf(
30
+ /* translators: %s: new name/label for the block */
31
+ __( 'Block name changed to: "%s".' ),
32
+ editedBlockName
33
+ );
34
+
35
+ // Must be assertive to immediately announce change.
36
+ speak( message, 'assertive' );
37
+ }
38
+ onSave( editedBlockName );
39
+
40
+ // Immediate close avoids ability to hit save multiple times.
41
+ onClose();
42
+ };
43
+
44
+ return (
45
+ <Modal
46
+ title={ __( 'Enable overrides' ) }
47
+ onRequestClose={ onClose }
48
+ focusOnMount="firstContentElement"
49
+ aria={ { describedby: descriptionId } }
50
+ size="small"
51
+ >
52
+ <form
53
+ onSubmit={ ( event ) => {
54
+ event.preventDefault();
55
+
56
+ if ( ! isNameValid ) {
57
+ return;
58
+ }
59
+
60
+ handleSubmit();
61
+ } }
62
+ >
63
+ <VStack spacing="6">
64
+ <Text id={ descriptionId }>
65
+ { __(
66
+ 'Overrides are changes you make to a block within a synced pattern instance. Use overrides to customize a synced pattern instance to suit its new context. Name this block to specify an override.'
67
+ ) }
68
+ </Text>
69
+ <TextControl
70
+ __nextHasNoMarginBottom
71
+ __next40pxDefaultSize
72
+ value={ editedBlockName }
73
+ label={ __( 'Name' ) }
74
+ help={ __(
75
+ 'For example, if you are creating a recipe pattern, you use "Recipe Title", "Recipe Description", etc.'
76
+ ) }
77
+ placeholder={ placeholder }
78
+ onChange={ setEditedBlockName }
79
+ />
80
+ <HStack justify="right">
81
+ <Button
82
+ __next40pxDefaultSize
83
+ variant="tertiary"
84
+ onClick={ onClose }
85
+ >
86
+ { __( 'Cancel' ) }
87
+ </Button>
88
+
89
+ <Button
90
+ __next40pxDefaultSize
91
+ aria-disabled={ ! isNameValid }
92
+ variant="primary"
93
+ type="submit"
94
+ >
95
+ { __( 'Enable' ) }
96
+ </Button>
97
+ </HStack>
98
+ </VStack>
99
+ </form>
100
+ </Modal>
101
+ );
102
+ }
103
+
104
+ export function DisallowOverridesModal( { onClose, onSave } ) {
105
+ const descriptionId = useId();
106
+
107
+ return (
108
+ <Modal
109
+ title={ __( 'Disable overrides' ) }
110
+ onRequestClose={ onClose }
111
+ aria={ { describedby: descriptionId } }
112
+ size="small"
113
+ >
114
+ <form
115
+ onSubmit={ ( event ) => {
116
+ event.preventDefault();
117
+ onSave();
118
+ onClose();
119
+ } }
120
+ >
121
+ <VStack spacing="6">
122
+ <Text id={ descriptionId }>
123
+ { __(
124
+ 'Are you sure you want to disable overrides? Disabling overrides will revert all applied overrides for this block throughout instances of this pattern.'
125
+ ) }
126
+ </Text>
127
+
128
+ <HStack justify="right">
129
+ <Button
130
+ __next40pxDefaultSize
131
+ variant="tertiary"
132
+ onClick={ onClose }
133
+ >
134
+ { __( 'Cancel' ) }
135
+ </Button>
136
+
137
+ <Button
138
+ __next40pxDefaultSize
139
+ variant="primary"
140
+ type="submit"
141
+ >
142
+ { __( 'Disable' ) }
143
+ </Button>
144
+ </HStack>
145
+ </VStack>
146
+ </form>
147
+ </Modal>
148
+ );
149
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ privateApis as blockEditorPrivateApis,
6
+ store as blockEditorStore,
7
+ } from '@wordpress/block-editor';
8
+ import { PanelBody } from '@wordpress/components';
9
+ import { useSelect } from '@wordpress/data';
10
+ import { useMemo } from '@wordpress/element';
11
+ import { __ } from '@wordpress/i18n';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { isOverridableBlock } from '../api';
17
+ import { unlock } from '../lock-unlock';
18
+
19
+ const { BlockQuickNavigation } = unlock( blockEditorPrivateApis );
20
+
21
+ export default function OverridesPanel() {
22
+ const allClientIds = useSelect(
23
+ ( select ) => select( blockEditorStore ).getClientIdsWithDescendants(),
24
+ []
25
+ );
26
+ const { getBlock } = useSelect( blockEditorStore );
27
+ const clientIdsWithOverrides = useMemo(
28
+ () =>
29
+ allClientIds.filter( ( clientId ) => {
30
+ const block = getBlock( clientId );
31
+ return isOverridableBlock( block );
32
+ } ),
33
+ [ allClientIds, getBlock ]
34
+ );
35
+
36
+ if ( ! clientIdsWithOverrides?.length ) {
37
+ return null;
38
+ }
39
+
40
+ return (
41
+ <PanelBody title={ __( 'Overrides' ) }>
42
+ <BlockQuickNavigation clientIds={ clientIdsWithOverrides } />
43
+ </PanelBody>
44
+ );
45
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useState, useId } from '@wordpress/element';
5
+ import { InspectorControls } from '@wordpress/block-editor';
6
+ import { BaseControl, Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import {
13
+ PARTIAL_SYNCING_SUPPORTED_BLOCKS,
14
+ PATTERN_OVERRIDES_BINDING_SOURCE,
15
+ } from '../constants';
16
+ import {
17
+ AllowOverridesModal,
18
+ DisallowOverridesModal,
19
+ } from './allow-overrides-modal';
20
+
21
+ function removeBindings( bindings, syncedAttributes ) {
22
+ let updatedBindings = {};
23
+ for ( const attributeName of syncedAttributes ) {
24
+ // Omit any bindings that's not the same source from the `updatedBindings` object.
25
+ if (
26
+ bindings?.[ attributeName ]?.source !==
27
+ PATTERN_OVERRIDES_BINDING_SOURCE &&
28
+ bindings?.[ attributeName ]?.source !== undefined
29
+ ) {
30
+ updatedBindings[ attributeName ] = bindings[ attributeName ];
31
+ }
32
+ }
33
+ if ( ! Object.keys( updatedBindings ).length ) {
34
+ updatedBindings = undefined;
35
+ }
36
+ return updatedBindings;
37
+ }
38
+
39
+ function addBindings( bindings, syncedAttributes ) {
40
+ const updatedBindings = { ...bindings };
41
+ for ( const attributeName of syncedAttributes ) {
42
+ if ( ! bindings?.[ attributeName ] ) {
43
+ updatedBindings[ attributeName ] = {
44
+ source: PATTERN_OVERRIDES_BINDING_SOURCE,
45
+ };
46
+ }
47
+ }
48
+ return updatedBindings;
49
+ }
50
+
51
+ function PatternOverridesControls( { attributes, name, setAttributes } ) {
52
+ const controlId = useId();
53
+ const [ showAllowOverridesModal, setShowAllowOverridesModal ] =
54
+ useState( false );
55
+ const [ showDisallowOverridesModal, setShowDisallowOverridesModal ] =
56
+ useState( false );
57
+
58
+ const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ];
59
+ const attributeSources = syncedAttributes.map(
60
+ ( attributeName ) =>
61
+ attributes.metadata?.bindings?.[ attributeName ]?.source
62
+ );
63
+ const isConnectedToOtherSources = attributeSources.every(
64
+ ( source ) => source && source !== 'core/pattern-overrides'
65
+ );
66
+
67
+ function updateBindings( isChecked, customName ) {
68
+ const prevBindings = attributes?.metadata?.bindings;
69
+ const updatedBindings = isChecked
70
+ ? addBindings( prevBindings, syncedAttributes )
71
+ : removeBindings( prevBindings, syncedAttributes );
72
+
73
+ const updatedMetadata = {
74
+ ...attributes.metadata,
75
+ bindings: updatedBindings,
76
+ };
77
+
78
+ if ( customName ) {
79
+ updatedMetadata.name = customName;
80
+ }
81
+
82
+ setAttributes( {
83
+ metadata: updatedMetadata,
84
+ } );
85
+ }
86
+
87
+ // Avoid overwriting other (e.g. meta) bindings.
88
+ if ( isConnectedToOtherSources ) return null;
89
+
90
+ const hasName = !! attributes.metadata?.name;
91
+ const allowOverrides =
92
+ hasName &&
93
+ attributeSources.some(
94
+ ( source ) => source === PATTERN_OVERRIDES_BINDING_SOURCE
95
+ );
96
+
97
+ return (
98
+ <>
99
+ <InspectorControls group="advanced">
100
+ <BaseControl
101
+ id={ controlId }
102
+ label={ __( 'Overrides' ) }
103
+ help={ __(
104
+ 'Allow changes to this block throughout instances of this pattern.'
105
+ ) }
106
+ >
107
+ <Button
108
+ __next40pxDefaultSize
109
+ className="pattern-overrides-control__allow-overrides-button"
110
+ variant="secondary"
111
+ onClick={ () => {
112
+ if ( allowOverrides ) {
113
+ setShowDisallowOverridesModal( true );
114
+ } else {
115
+ setShowAllowOverridesModal( true );
116
+ }
117
+ } }
118
+ >
119
+ { allowOverrides
120
+ ? __( 'Disable overrides' )
121
+ : __( 'Enable overrides' ) }
122
+ </Button>
123
+ </BaseControl>
124
+ </InspectorControls>
125
+
126
+ { showAllowOverridesModal && (
127
+ <AllowOverridesModal
128
+ initialName={ attributes.metadata?.name }
129
+ onClose={ () => setShowAllowOverridesModal( false ) }
130
+ onSave={ ( newName ) => {
131
+ updateBindings( true, newName );
132
+ } }
133
+ />
134
+ ) }
135
+ { showDisallowOverridesModal && (
136
+ <DisallowOverridesModal
137
+ onClose={ () => setShowDisallowOverridesModal( false ) }
138
+ onSave={ () => updateBindings( false ) }
139
+ />
140
+ ) }
141
+ </>
142
+ );
143
+ }
144
+
145
+ export default PatternOverridesControls;
@@ -38,3 +38,9 @@
38
38
  width: $grid-unit * 40;
39
39
  }
40
40
  }
41
+
42
+ .pattern-overrides-control__allow-overrides-button {
43
+ width: 100%;
44
+ justify-content: center;
45
+ }
46
+
package/src/constants.js CHANGED
@@ -22,3 +22,5 @@ export const PARTIAL_SYNCING_SUPPORTED_BLOCKS = {
22
22
  'core/button': [ 'text', 'url', 'linkTarget', 'rel' ],
23
23
  'core/image': [ 'id', 'url', 'title', 'alt' ],
24
24
  };
25
+
26
+ export const PATTERN_OVERRIDES_BINDING_SOURCE = 'core/pattern-overrides';
@@ -2,6 +2,7 @@
2
2
  * Internal dependencies
3
3
  */
4
4
  import { lock } from './lock-unlock';
5
+ import OverridesPanel from './components/overrides-panel';
5
6
  import {
6
7
  default as CreatePatternModal,
7
8
  CreatePatternModalContents,
@@ -10,10 +11,11 @@ import {
10
11
  default as DuplicatePatternModal,
11
12
  useDuplicatePatternProps,
12
13
  } from './components/duplicate-pattern-modal';
14
+ import { isOverridableBlock } from './api';
13
15
  import RenamePatternModal from './components/rename-pattern-modal';
14
16
  import PatternsMenuItems from './components';
15
17
  import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
16
- import useSetPatternBindings from './components/use-set-pattern-bindings';
18
+ import PatternOverridesControls from './components/pattern-overrides-controls';
17
19
  import ResetOverridesControl from './components/reset-overrides-control';
18
20
  import { useAddPatternCategory } from './private-hooks';
19
21
  import {
@@ -27,14 +29,16 @@ import {
27
29
 
28
30
  export const privateApis = {};
29
31
  lock( privateApis, {
32
+ OverridesPanel,
30
33
  CreatePatternModal,
31
34
  CreatePatternModalContents,
32
35
  DuplicatePatternModal,
36
+ isOverridableBlock,
33
37
  useDuplicatePatternProps,
34
38
  RenamePatternModal,
35
39
  PatternsMenuItems,
36
40
  RenamePatternCategoryModal,
37
- useSetPatternBindings,
41
+ PatternOverridesControls,
38
42
  ResetOverridesControl,
39
43
  useAddPatternCategory,
40
44
  PATTERN_TYPES,