@wordpress/patterns 1.4.1 → 1.5.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 (40) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/category-selector.js +16 -31
  3. package/build/components/category-selector.js.map +1 -1
  4. package/build/components/create-pattern-modal.js +59 -14
  5. package/build/components/create-pattern-modal.js.map +1 -1
  6. package/build/components/duplicate-pattern-modal.js +81 -0
  7. package/build/components/duplicate-pattern-modal.js.map +1 -0
  8. package/build/components/rename-pattern-category-modal.js +106 -0
  9. package/build/components/rename-pattern-category-modal.js.map +1 -0
  10. package/build/components/rename-pattern-modal.js +97 -0
  11. package/build/components/rename-pattern-modal.js.map +1 -0
  12. package/build/lock-unlock.js +1 -1
  13. package/build/lock-unlock.js.map +1 -1
  14. package/build/private-apis.js +6 -0
  15. package/build/private-apis.js.map +1 -1
  16. package/build-module/components/category-selector.js +17 -32
  17. package/build-module/components/category-selector.js.map +1 -1
  18. package/build-module/components/create-pattern-modal.js +61 -16
  19. package/build-module/components/create-pattern-modal.js.map +1 -1
  20. package/build-module/components/duplicate-pattern-modal.js +73 -0
  21. package/build-module/components/duplicate-pattern-modal.js.map +1 -0
  22. package/build-module/components/rename-pattern-category-modal.js +99 -0
  23. package/build-module/components/rename-pattern-category-modal.js.map +1 -0
  24. package/build-module/components/rename-pattern-modal.js +90 -0
  25. package/build-module/components/rename-pattern-modal.js.map +1 -0
  26. package/build-module/lock-unlock.js +1 -1
  27. package/build-module/lock-unlock.js.map +1 -1
  28. package/build-module/private-apis.js +6 -0
  29. package/build-module/private-apis.js.map +1 -1
  30. package/build-style/style-rtl.css +22 -2
  31. package/build-style/style.css +22 -2
  32. package/package.json +16 -17
  33. package/src/components/category-selector.js +28 -42
  34. package/src/components/create-pattern-modal.js +63 -14
  35. package/src/components/duplicate-pattern-modal.js +93 -0
  36. package/src/components/rename-pattern-category-modal.js +121 -0
  37. package/src/components/rename-pattern-modal.js +115 -0
  38. package/src/components/style.scss +28 -2
  39. package/src/lock-unlock.js +1 -1
  40. package/src/private-apis.js +6 -0
@@ -0,0 +1,90 @@
1
+ import { createElement } from "react";
2
+ /**
3
+ * WordPress dependencies
4
+ */
5
+ import { Button, Modal, TextControl, __experimentalHStack as HStack, __experimentalVStack as VStack } from '@wordpress/components';
6
+ import { store as coreStore } from '@wordpress/core-data';
7
+ import { useDispatch } from '@wordpress/data';
8
+ import { useState } from '@wordpress/element';
9
+ import { decodeEntities } from '@wordpress/html-entities';
10
+ import { __ } from '@wordpress/i18n';
11
+ import { store as noticesStore } from '@wordpress/notices';
12
+ export default function RenamePatternModal({
13
+ onClose,
14
+ onError,
15
+ onSuccess,
16
+ pattern,
17
+ ...props
18
+ }) {
19
+ const originalName = decodeEntities(pattern.title);
20
+ const [name, setName] = useState(originalName);
21
+ const [isSaving, setIsSaving] = useState(false);
22
+ const {
23
+ editEntityRecord,
24
+ __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits
25
+ } = useDispatch(coreStore);
26
+ const {
27
+ createSuccessNotice,
28
+ createErrorNotice
29
+ } = useDispatch(noticesStore);
30
+ const onRename = async event => {
31
+ event.preventDefault();
32
+ if (!name || name === pattern.title || isSaving) {
33
+ return;
34
+ }
35
+ try {
36
+ await editEntityRecord('postType', pattern.type, pattern.id, {
37
+ title: name
38
+ });
39
+ setIsSaving(true);
40
+ setName('');
41
+ onClose?.();
42
+ const savedRecord = await saveSpecifiedEntityEdits('postType', pattern.type, pattern.id, ['title'], {
43
+ throwOnError: true
44
+ });
45
+ onSuccess?.(savedRecord);
46
+ createSuccessNotice(__('Pattern renamed'), {
47
+ type: 'snackbar',
48
+ id: 'pattern-update'
49
+ });
50
+ } catch (error) {
51
+ onError?.();
52
+ const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : __('An error occurred while renaming the pattern.');
53
+ createErrorNotice(errorMessage, {
54
+ type: 'snackbar',
55
+ id: 'pattern-update'
56
+ });
57
+ } finally {
58
+ setIsSaving(false);
59
+ setName('');
60
+ }
61
+ };
62
+ const onRequestClose = () => {
63
+ onClose?.();
64
+ setName('');
65
+ };
66
+ return createElement(Modal, {
67
+ title: __('Rename'),
68
+ ...props,
69
+ onRequestClose: onClose
70
+ }, createElement("form", {
71
+ onSubmit: onRename
72
+ }, createElement(VStack, {
73
+ spacing: "5"
74
+ }, createElement(TextControl, {
75
+ __nextHasNoMarginBottom: true,
76
+ label: __('Name'),
77
+ value: name,
78
+ onChange: setName,
79
+ required: true
80
+ }), createElement(HStack, {
81
+ justify: "right"
82
+ }, createElement(Button, {
83
+ variant: "tertiary",
84
+ onClick: onRequestClose
85
+ }, __('Cancel')), createElement(Button, {
86
+ variant: "primary",
87
+ type: "submit"
88
+ }, __('Save'))))));
89
+ }
90
+ //# sourceMappingURL=rename-pattern-modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Button","Modal","TextControl","__experimentalHStack","HStack","__experimentalVStack","VStack","store","coreStore","useDispatch","useState","decodeEntities","__","noticesStore","RenamePatternModal","onClose","onError","onSuccess","pattern","props","originalName","title","name","setName","isSaving","setIsSaving","editEntityRecord","__experimentalSaveSpecifiedEntityEdits","saveSpecifiedEntityEdits","createSuccessNotice","createErrorNotice","onRename","event","preventDefault","type","id","savedRecord","throwOnError","error","errorMessage","message","code","onRequestClose","createElement","onSubmit","spacing","__nextHasNoMarginBottom","label","value","onChange","required","justify","variant","onClick"],"sources":["@wordpress/patterns/src/components/rename-pattern-modal.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport {\n\tButton,\n\tModal,\n\tTextControl,\n\t__experimentalHStack as HStack,\n\t__experimentalVStack as VStack,\n} from '@wordpress/components';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { useDispatch } from '@wordpress/data';\nimport { useState } from '@wordpress/element';\nimport { decodeEntities } from '@wordpress/html-entities';\nimport { __ } from '@wordpress/i18n';\nimport { store as noticesStore } from '@wordpress/notices';\n\nexport default function RenamePatternModal( {\n\tonClose,\n\tonError,\n\tonSuccess,\n\tpattern,\n\t...props\n} ) {\n\tconst originalName = decodeEntities( pattern.title );\n\tconst [ name, setName ] = useState( originalName );\n\tconst [ isSaving, setIsSaving ] = useState( false );\n\n\tconst {\n\t\teditEntityRecord,\n\t\t__experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits,\n\t} = useDispatch( coreStore );\n\n\tconst { createSuccessNotice, createErrorNotice } =\n\t\tuseDispatch( noticesStore );\n\n\tconst onRename = async ( event ) => {\n\t\tevent.preventDefault();\n\n\t\tif ( ! name || name === pattern.title || isSaving ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait editEntityRecord( 'postType', pattern.type, pattern.id, {\n\t\t\t\ttitle: name,\n\t\t\t} );\n\n\t\t\tsetIsSaving( true );\n\t\t\tsetName( '' );\n\t\t\tonClose?.();\n\n\t\t\tconst savedRecord = await saveSpecifiedEntityEdits(\n\t\t\t\t'postType',\n\t\t\t\tpattern.type,\n\t\t\t\tpattern.id,\n\t\t\t\t[ 'title' ],\n\t\t\t\t{ throwOnError: true }\n\t\t\t);\n\n\t\t\tonSuccess?.( savedRecord );\n\n\t\t\tcreateSuccessNotice( __( 'Pattern renamed' ), {\n\t\t\t\ttype: 'snackbar',\n\t\t\t\tid: 'pattern-update',\n\t\t\t} );\n\t\t} catch ( error ) {\n\t\t\tonError?.();\n\n\t\t\tconst errorMessage =\n\t\t\t\terror.message && error.code !== 'unknown_error'\n\t\t\t\t\t? error.message\n\t\t\t\t\t: __( 'An error occurred while renaming the pattern.' );\n\n\t\t\tcreateErrorNotice( errorMessage, {\n\t\t\t\ttype: 'snackbar',\n\t\t\t\tid: 'pattern-update',\n\t\t\t} );\n\t\t} finally {\n\t\t\tsetIsSaving( false );\n\t\t\tsetName( '' );\n\t\t}\n\t};\n\n\tconst onRequestClose = () => {\n\t\tonClose?.();\n\t\tsetName( '' );\n\t};\n\n\treturn (\n\t\t<Modal title={ __( 'Rename' ) } { ...props } onRequestClose={ onClose }>\n\t\t\t<form onSubmit={ onRename }>\n\t\t\t\t<VStack spacing=\"5\">\n\t\t\t\t\t<TextControl\n\t\t\t\t\t\t__nextHasNoMarginBottom\n\t\t\t\t\t\tlabel={ __( 'Name' ) }\n\t\t\t\t\t\tvalue={ name }\n\t\t\t\t\t\tonChange={ setName }\n\t\t\t\t\t\trequired\n\t\t\t\t\t/>\n\n\t\t\t\t\t<HStack justify=\"right\">\n\t\t\t\t\t\t<Button variant=\"tertiary\" onClick={ onRequestClose }>\n\t\t\t\t\t\t\t{ __( 'Cancel' ) }\n\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t<Button variant=\"primary\" type=\"submit\">\n\t\t\t\t\t\t\t{ __( 'Save' ) }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</HStack>\n\t\t\t\t</VStack>\n\t\t\t</form>\n\t\t</Modal>\n\t);\n}\n"],"mappings":"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"}
@@ -5,5 +5,5 @@ import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/pri
5
5
  export const {
6
6
  lock,
7
7
  unlock
8
- } = __dangerousOptInToUnstableAPIsOnlyForCoreModules('I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.', '@wordpress/patterns');
8
+ } = __dangerousOptInToUnstableAPIsOnlyForCoreModules('I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.', '@wordpress/patterns');
9
9
  //# sourceMappingURL=lock-unlock.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["__dangerousOptInToUnstableAPIsOnlyForCoreModules","lock","unlock"],"sources":["@wordpress/patterns/src/lock-unlock.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';\nexport const { lock, unlock } =\n\t__dangerousOptInToUnstableAPIsOnlyForCoreModules(\n\t\t'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',\n\t\t'@wordpress/patterns'\n\t);\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,gDAAgD,QAAQ,yBAAyB;AAC1F,OAAO,MAAM;EAAEC,IAAI;EAAEC;AAAO,CAAC,GAC5BF,gDAAgD,CAC/C,8GAA8G,EAC9G,qBACD,CAAC"}
1
+ {"version":3,"names":["__dangerousOptInToUnstableAPIsOnlyForCoreModules","lock","unlock"],"sources":["@wordpress/patterns/src/lock-unlock.js"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';\nexport const { lock, unlock } =\n\t__dangerousOptInToUnstableAPIsOnlyForCoreModules(\n\t\t'I know using unstable features means my theme or plugin will inevitably break in the next version of WordPress.',\n\t\t'@wordpress/patterns'\n\t);\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,gDAAgD,QAAQ,yBAAyB;AAC1F,OAAO,MAAM;EAAEC,IAAI;EAAEC;AAAO,CAAC,GAC5BF,gDAAgD,CAC/C,iHAAiH,EACjH,qBACD,CAAC"}
@@ -3,12 +3,18 @@
3
3
  */
4
4
  import { lock } from './lock-unlock';
5
5
  import CreatePatternModal from './components/create-pattern-modal';
6
+ import DuplicatePatternModal from './components/duplicate-pattern-modal';
7
+ import RenamePatternModal from './components/rename-pattern-modal';
6
8
  import PatternsMenuItems from './components';
9
+ import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
7
10
  import { PATTERN_TYPES, PATTERN_DEFAULT_CATEGORY, PATTERN_USER_CATEGORY, PATTERN_CORE_SOURCES, PATTERN_SYNC_TYPES } from './constants';
8
11
  export const privateApis = {};
9
12
  lock(privateApis, {
10
13
  CreatePatternModal,
14
+ DuplicatePatternModal,
15
+ RenamePatternModal,
11
16
  PatternsMenuItems,
17
+ RenamePatternCategoryModal,
12
18
  PATTERN_TYPES,
13
19
  PATTERN_DEFAULT_CATEGORY,
14
20
  PATTERN_USER_CATEGORY,
@@ -1 +1 @@
1
- {"version":3,"names":["lock","CreatePatternModal","PatternsMenuItems","PATTERN_TYPES","PATTERN_DEFAULT_CATEGORY","PATTERN_USER_CATEGORY","PATTERN_CORE_SOURCES","PATTERN_SYNC_TYPES","privateApis"],"sources":["@wordpress/patterns/src/private-apis.js"],"sourcesContent":["/**\n * Internal dependencies\n */\nimport { lock } from './lock-unlock';\nimport CreatePatternModal from './components/create-pattern-modal';\nimport PatternsMenuItems from './components';\nimport {\n\tPATTERN_TYPES,\n\tPATTERN_DEFAULT_CATEGORY,\n\tPATTERN_USER_CATEGORY,\n\tPATTERN_CORE_SOURCES,\n\tPATTERN_SYNC_TYPES,\n} from './constants';\n\nexport const privateApis = {};\nlock( privateApis, {\n\tCreatePatternModal,\n\tPatternsMenuItems,\n\tPATTERN_TYPES,\n\tPATTERN_DEFAULT_CATEGORY,\n\tPATTERN_USER_CATEGORY,\n\tPATTERN_CORE_SOURCES,\n\tPATTERN_SYNC_TYPES,\n} );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,IAAI,QAAQ,eAAe;AACpC,OAAOC,kBAAkB,MAAM,mCAAmC;AAClE,OAAOC,iBAAiB,MAAM,cAAc;AAC5C,SACCC,aAAa,EACbC,wBAAwB,EACxBC,qBAAqB,EACrBC,oBAAoB,EACpBC,kBAAkB,QACZ,aAAa;AAEpB,OAAO,MAAMC,WAAW,GAAG,CAAC,CAAC;AAC7BR,IAAI,CAAEQ,WAAW,EAAE;EAClBP,kBAAkB;EAClBC,iBAAiB;EACjBC,aAAa;EACbC,wBAAwB;EACxBC,qBAAqB;EACrBC,oBAAoB;EACpBC;AACD,CAAE,CAAC"}
1
+ {"version":3,"names":["lock","CreatePatternModal","DuplicatePatternModal","RenamePatternModal","PatternsMenuItems","RenamePatternCategoryModal","PATTERN_TYPES","PATTERN_DEFAULT_CATEGORY","PATTERN_USER_CATEGORY","PATTERN_CORE_SOURCES","PATTERN_SYNC_TYPES","privateApis"],"sources":["@wordpress/patterns/src/private-apis.js"],"sourcesContent":["/**\n * Internal dependencies\n */\nimport { lock } from './lock-unlock';\nimport CreatePatternModal from './components/create-pattern-modal';\nimport DuplicatePatternModal from './components/duplicate-pattern-modal';\nimport RenamePatternModal from './components/rename-pattern-modal';\nimport PatternsMenuItems from './components';\nimport RenamePatternCategoryModal from './components/rename-pattern-category-modal';\nimport {\n\tPATTERN_TYPES,\n\tPATTERN_DEFAULT_CATEGORY,\n\tPATTERN_USER_CATEGORY,\n\tPATTERN_CORE_SOURCES,\n\tPATTERN_SYNC_TYPES,\n} from './constants';\n\nexport const privateApis = {};\nlock( privateApis, {\n\tCreatePatternModal,\n\tDuplicatePatternModal,\n\tRenamePatternModal,\n\tPatternsMenuItems,\n\tRenamePatternCategoryModal,\n\tPATTERN_TYPES,\n\tPATTERN_DEFAULT_CATEGORY,\n\tPATTERN_USER_CATEGORY,\n\tPATTERN_CORE_SOURCES,\n\tPATTERN_SYNC_TYPES,\n} );\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,IAAI,QAAQ,eAAe;AACpC,OAAOC,kBAAkB,MAAM,mCAAmC;AAClE,OAAOC,qBAAqB,MAAM,sCAAsC;AACxE,OAAOC,kBAAkB,MAAM,mCAAmC;AAClE,OAAOC,iBAAiB,MAAM,cAAc;AAC5C,OAAOC,0BAA0B,MAAM,4CAA4C;AACnF,SACCC,aAAa,EACbC,wBAAwB,EACxBC,qBAAqB,EACrBC,oBAAoB,EACpBC,kBAAkB,QACZ,aAAa;AAEpB,OAAO,MAAMC,WAAW,GAAG,CAAC,CAAC;AAC7BX,IAAI,CAAEW,WAAW,EAAE;EAClBV,kBAAkB;EAClBC,qBAAqB;EACrBC,kBAAkB;EAClBC,iBAAiB;EACjBC,0BAA0B;EAC1BC,aAAa;EACbC,wBAAwB;EACxBC,qBAAqB;EACrBC,oBAAoB;EACpBC;AACD,CAAE,CAAC"}
@@ -106,10 +106,30 @@
106
106
  .patterns-menu-items__convert-modal {
107
107
  z-index: 1000001;
108
108
  }
109
+ .patterns-menu-items__convert-modal [role=dialog] > [role=document] {
110
+ width: 350px;
111
+ }
109
112
  .patterns-menu-items__convert-modal .patterns-menu-items__convert-modal-categories {
110
- max-width: 300px;
113
+ width: 100%;
114
+ position: relative;
115
+ min-height: 40px;
116
+ }
117
+ .patterns-menu-items__convert-modal .components-form-token-field__suggestions-list {
118
+ position: absolute;
119
+ box-sizing: border-box;
120
+ z-index: 1;
121
+ background-color: #fff;
122
+ width: calc(100% + 2px);
123
+ right: -1px;
124
+ min-width: initial;
125
+ border: 1px solid var(--wp-admin-theme-color);
126
+ border-top: none;
127
+ box-shadow: 0 0 0 0.5px var(--wp-admin-theme-color);
128
+ border-bottom-right-radius: 2px;
129
+ border-bottom-left-radius: 2px;
111
130
  }
112
131
 
113
132
  .patterns-create-modal__name-input input[type=text] {
114
- min-height: 34px;
133
+ min-height: 40px;
134
+ margin: 0;
115
135
  }
@@ -106,10 +106,30 @@
106
106
  .patterns-menu-items__convert-modal {
107
107
  z-index: 1000001;
108
108
  }
109
+ .patterns-menu-items__convert-modal [role=dialog] > [role=document] {
110
+ width: 350px;
111
+ }
109
112
  .patterns-menu-items__convert-modal .patterns-menu-items__convert-modal-categories {
110
- max-width: 300px;
113
+ width: 100%;
114
+ position: relative;
115
+ min-height: 40px;
116
+ }
117
+ .patterns-menu-items__convert-modal .components-form-token-field__suggestions-list {
118
+ position: absolute;
119
+ box-sizing: border-box;
120
+ z-index: 1;
121
+ background-color: #fff;
122
+ width: calc(100% + 2px);
123
+ left: -1px;
124
+ min-width: initial;
125
+ border: 1px solid var(--wp-admin-theme-color);
126
+ border-top: none;
127
+ box-shadow: 0 0 0 0.5px var(--wp-admin-theme-color);
128
+ border-bottom-left-radius: 2px;
129
+ border-bottom-right-radius: 2px;
111
130
  }
112
131
 
113
132
  .patterns-create-modal__name-input input[type=text] {
114
- min-height: 34px;
133
+ min-height: 40px;
134
+ margin: 0;
115
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/patterns",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "Management of user pattern editing.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -19,8 +19,7 @@
19
19
  "url": "https://github.com/WordPress/gutenberg/issues"
20
20
  },
21
21
  "engines": {
22
- "node": ">=16.0.0",
23
- "npm": ">=8 <9"
22
+ "node": ">=16.0.0"
24
23
  },
25
24
  "main": "build/index.js",
26
25
  "module": "build-module/index.js",
@@ -32,19 +31,19 @@
32
31
  ],
33
32
  "dependencies": {
34
33
  "@babel/runtime": "^7.16.0",
35
- "@wordpress/block-editor": "^12.11.1",
36
- "@wordpress/blocks": "^12.20.1",
37
- "@wordpress/components": "^25.9.1",
38
- "@wordpress/compose": "^6.20.0",
39
- "@wordpress/core-data": "^6.20.1",
40
- "@wordpress/data": "^9.13.1",
41
- "@wordpress/element": "^5.20.0",
42
- "@wordpress/html-entities": "^3.43.0",
43
- "@wordpress/i18n": "^4.43.0",
44
- "@wordpress/icons": "^9.34.0",
45
- "@wordpress/notices": "^4.11.1",
46
- "@wordpress/private-apis": "^0.25.0",
47
- "@wordpress/url": "^3.44.0"
34
+ "@wordpress/block-editor": "^12.12.0",
35
+ "@wordpress/blocks": "^12.21.0",
36
+ "@wordpress/components": "^25.10.0",
37
+ "@wordpress/compose": "^6.21.0",
38
+ "@wordpress/core-data": "^6.21.0",
39
+ "@wordpress/data": "^9.14.0",
40
+ "@wordpress/element": "^5.21.0",
41
+ "@wordpress/html-entities": "^3.44.0",
42
+ "@wordpress/i18n": "^4.44.0",
43
+ "@wordpress/icons": "^9.35.0",
44
+ "@wordpress/notices": "^4.12.0",
45
+ "@wordpress/private-apis": "^0.26.0",
46
+ "@wordpress/url": "^3.45.0"
48
47
  },
49
48
  "peerDependencies": {
50
49
  "react": "^18.0.0",
@@ -53,5 +52,5 @@
53
52
  "publishConfig": {
54
53
  "access": "public"
55
54
  },
56
- "gitHead": "e17f760ed0dc11cce78157d7c2f2086b1b3c09d8"
55
+ "gitHead": "f83bb1a71e8fa416131b81a9f282a72a1dc6c694"
57
56
  }
@@ -4,8 +4,6 @@
4
4
  import { __ } from '@wordpress/i18n';
5
5
  import { useMemo, useState } from '@wordpress/element';
6
6
  import { FormTokenField } from '@wordpress/components';
7
- import { useSelect } from '@wordpress/data';
8
- import { store as coreStore } from '@wordpress/core-data';
9
7
  import { useDebounce } from '@wordpress/compose';
10
8
  import { decodeEntities } from '@wordpress/html-entities';
11
9
 
@@ -13,40 +11,29 @@ const unescapeString = ( arg ) => {
13
11
  return decodeEntities( arg );
14
12
  };
15
13
 
16
- const EMPTY_ARRAY = [];
17
- const MAX_TERMS_SUGGESTIONS = 20;
18
- const DEFAULT_QUERY = {
19
- per_page: MAX_TERMS_SUGGESTIONS,
20
- _fields: 'id,name',
21
- context: 'view',
22
- };
23
14
  export const CATEGORY_SLUG = 'wp_pattern_category';
24
15
 
25
- export default function CategorySelector( { values, onChange } ) {
16
+ export default function CategorySelector( {
17
+ categoryTerms,
18
+ onChange,
19
+ categoryMap,
20
+ } ) {
26
21
  const [ search, setSearch ] = useState( '' );
27
22
  const debouncedSearch = useDebounce( setSearch, 500 );
28
23
 
29
- const { searchResults } = useSelect(
30
- ( select ) => {
31
- const { getEntityRecords } = select( coreStore );
32
-
33
- return {
34
- searchResults: !! search
35
- ? getEntityRecords( 'taxonomy', CATEGORY_SLUG, {
36
- ...DEFAULT_QUERY,
37
- search,
38
- } )
39
- : EMPTY_ARRAY,
40
- };
41
- },
42
- [ search ]
43
- );
44
-
45
24
  const suggestions = useMemo( () => {
46
- return ( searchResults ?? [] ).map( ( term ) =>
47
- unescapeString( term.name )
48
- );
49
- }, [ searchResults ] );
25
+ return Array.from( categoryMap.values() )
26
+ .map( ( category ) => unescapeString( category.label ) )
27
+ .filter( ( category ) => {
28
+ if ( search !== '' ) {
29
+ return category
30
+ .toLowerCase()
31
+ .includes( search.toLowerCase() );
32
+ }
33
+ return true;
34
+ } )
35
+ .sort( ( a, b ) => a.localeCompare( b ) );
36
+ }, [ search, categoryMap ] );
50
37
 
51
38
  function handleChange( termNames ) {
52
39
  const uniqueTerms = termNames.reduce( ( terms, newTerm ) => {
@@ -64,17 +51,16 @@ export default function CategorySelector( { values, onChange } ) {
64
51
  }
65
52
 
66
53
  return (
67
- <>
68
- <FormTokenField
69
- className="patterns-menu-items__convert-modal-categories"
70
- value={ values }
71
- suggestions={ suggestions }
72
- onChange={ handleChange }
73
- onInputChange={ debouncedSearch }
74
- maxSuggestions={ MAX_TERMS_SUGGESTIONS }
75
- label={ __( 'Categories' ) }
76
- tokenizeOnBlur={ true }
77
- />
78
- </>
54
+ <FormTokenField
55
+ className="patterns-menu-items__convert-modal-categories"
56
+ value={ categoryTerms }
57
+ suggestions={ suggestions }
58
+ onChange={ handleChange }
59
+ onInputChange={ debouncedSearch }
60
+ label={ __( 'Categories' ) }
61
+ tokenizeOnBlur
62
+ __experimentalExpandOnFocus
63
+ __next40pxDefaultSize
64
+ />
79
65
  );
80
66
  }
@@ -10,8 +10,8 @@ import {
10
10
  ToggleControl,
11
11
  } from '@wordpress/components';
12
12
  import { __ } from '@wordpress/i18n';
13
- import { useState } from '@wordpress/element';
14
- import { useDispatch } from '@wordpress/data';
13
+ import { useState, useMemo } from '@wordpress/element';
14
+ import { useDispatch, useSelect } from '@wordpress/data';
15
15
  import { store as noticesStore } from '@wordpress/notices';
16
16
  import { store as coreStore } from '@wordpress/core-data';
17
17
 
@@ -28,20 +28,62 @@ import CategorySelector, { CATEGORY_SLUG } from './category-selector';
28
28
  import { unlock } from '../lock-unlock';
29
29
 
30
30
  export default function CreatePatternModal( {
31
- onSuccess,
32
- onError,
31
+ confirmLabel = __( 'Create' ),
32
+ defaultCategories = [],
33
+ className = 'patterns-menu-items__convert-modal',
33
34
  content,
35
+ modalTitle = __( 'Create pattern' ),
34
36
  onClose,
35
- className = 'patterns-menu-items__convert-modal',
37
+ onError,
38
+ onSuccess,
39
+ defaultSyncType = PATTERN_SYNC_TYPES.full,
40
+ defaultTitle = '',
36
41
  } ) {
37
- const [ syncType, setSyncType ] = useState( PATTERN_SYNC_TYPES.full );
38
- const [ categoryTerms, setCategoryTerms ] = useState( [] );
39
- const [ title, setTitle ] = useState( '' );
42
+ const [ syncType, setSyncType ] = useState( defaultSyncType );
43
+ const [ categoryTerms, setCategoryTerms ] = useState( defaultCategories );
44
+ const [ title, setTitle ] = useState( defaultTitle );
45
+
40
46
  const [ isSaving, setIsSaving ] = useState( false );
41
47
  const { createPattern } = unlock( useDispatch( patternsStore ) );
42
48
  const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );
43
49
  const { createErrorNotice } = useDispatch( noticesStore );
44
50
 
51
+ const { corePatternCategories, userPatternCategories } = useSelect(
52
+ ( select ) => {
53
+ const { getUserPatternCategories, getBlockPatternCategories } =
54
+ select( coreStore );
55
+
56
+ return {
57
+ corePatternCategories: getBlockPatternCategories(),
58
+ userPatternCategories: getUserPatternCategories(),
59
+ };
60
+ }
61
+ );
62
+
63
+ const categoryMap = useMemo( () => {
64
+ // Merge the user and core pattern categories and remove any duplicates.
65
+ const uniqueCategories = new Map();
66
+ [ ...userPatternCategories, ...corePatternCategories ].forEach(
67
+ ( category ) => {
68
+ if (
69
+ ! uniqueCategories.has( category.label ) &&
70
+ // There are two core categories with `Post` label so explicitly remove the one with
71
+ // the `query` slug to avoid any confusion.
72
+ category.name !== 'query'
73
+ ) {
74
+ // We need to store the name separately as this is used as the slug in the
75
+ // taxonomy and may vary from the label.
76
+ uniqueCategories.set( category.label, {
77
+ label: category.label,
78
+ value: category.label,
79
+ name: category.name,
80
+ } );
81
+ }
82
+ }
83
+ );
84
+ return uniqueCategories;
85
+ }, [ userPatternCategories, corePatternCategories ] );
86
+
45
87
  async function onCreate( patternTitle, sync ) {
46
88
  if ( ! title || isSaving ) {
47
89
  return;
@@ -68,9 +110,9 @@ export default function CreatePatternModal( {
68
110
  } catch ( error ) {
69
111
  createErrorNotice( error.message, {
70
112
  type: 'snackbar',
71
- id: 'convert-to-pattern-error',
113
+ id: 'pattern-create',
72
114
  } );
73
- onError();
115
+ onError?.();
74
116
  } finally {
75
117
  setIsSaving( false );
76
118
  setCategoryTerms( [] );
@@ -84,10 +126,16 @@ export default function CreatePatternModal( {
84
126
  */
85
127
  async function findOrCreateTerm( term ) {
86
128
  try {
129
+ // We need to match any existing term to the correct slug to prevent duplicates, eg.
130
+ // the core `Headers` category uses the singular `header` as the slug.
131
+ const existingTerm = categoryMap.get( term );
132
+ const termData = existingTerm
133
+ ? { name: existingTerm.label, slug: existingTerm.name }
134
+ : { name: term };
87
135
  const newTerm = await saveEntityRecord(
88
136
  'taxonomy',
89
137
  CATEGORY_SLUG,
90
- { name: term },
138
+ termData,
91
139
  { throwOnError: true }
92
140
  );
93
141
  invalidateResolution( 'getUserPatternCategories' );
@@ -103,7 +151,7 @@ export default function CreatePatternModal( {
103
151
 
104
152
  return (
105
153
  <Modal
106
- title={ __( 'Create pattern' ) }
154
+ title={ modalTitle }
107
155
  onRequestClose={ () => {
108
156
  onClose();
109
157
  setTitle( '' );
@@ -126,8 +174,9 @@ export default function CreatePatternModal( {
126
174
  className="patterns-create-modal__name-input"
127
175
  />
128
176
  <CategorySelector
129
- values={ categoryTerms }
177
+ categoryTerms={ categoryTerms }
130
178
  onChange={ setCategoryTerms }
179
+ categoryMap={ categoryMap }
131
180
  />
132
181
  <ToggleControl
133
182
  label={ __( 'Synced' ) }
@@ -160,7 +209,7 @@ export default function CreatePatternModal( {
160
209
  aria-disabled={ ! title || isSaving }
161
210
  isBusy={ isSaving }
162
211
  >
163
- { __( 'Create' ) }
212
+ { confirmLabel }
164
213
  </Button>
165
214
  </HStack>
166
215
  </VStack>
@@ -0,0 +1,93 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { store as coreStore } from '@wordpress/core-data';
5
+ import { useDispatch, useSelect } from '@wordpress/data';
6
+ import { __, sprintf } from '@wordpress/i18n';
7
+ import { store as noticesStore } from '@wordpress/notices';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import CreatePatternModal from './create-pattern-modal';
13
+ import { PATTERN_SYNC_TYPES } from '../constants';
14
+
15
+ function getTermLabels( pattern, categories ) {
16
+ // Theme patterns don't have an id and rely on core pattern categories.
17
+ if ( ! pattern.id ) {
18
+ return categories.core
19
+ ?.filter( ( category ) =>
20
+ pattern.categories.includes( category.name )
21
+ )
22
+ .map( ( category ) => category.label );
23
+ }
24
+
25
+ return categories.user
26
+ ?.filter( ( category ) =>
27
+ pattern.wp_pattern_category.includes( category.id )
28
+ )
29
+ .map( ( category ) => category.label );
30
+ }
31
+
32
+ export default function DuplicatePatternModal( {
33
+ pattern,
34
+ onClose,
35
+ onSuccess,
36
+ } ) {
37
+ const { createSuccessNotice } = useDispatch( noticesStore );
38
+ const categories = useSelect( ( select ) => {
39
+ const { getUserPatternCategories, getBlockPatternCategories } =
40
+ select( coreStore );
41
+
42
+ return {
43
+ core: getBlockPatternCategories(),
44
+ user: getUserPatternCategories(),
45
+ };
46
+ } );
47
+
48
+ if ( ! pattern ) {
49
+ return null;
50
+ }
51
+
52
+ const duplicatedProps = {
53
+ content: pattern.content,
54
+ defaultCategories: getTermLabels( pattern, categories ),
55
+ defaultSyncType: ! pattern.id // Theme patterns don't have an ID.
56
+ ? PATTERN_SYNC_TYPES.unsynced
57
+ : pattern.wp_pattern_sync_status || PATTERN_SYNC_TYPES.full,
58
+ defaultTitle: sprintf(
59
+ /* translators: %s: Existing pattern title */
60
+ __( '%s (Copy)' ),
61
+ typeof pattern.title === 'string'
62
+ ? pattern.title
63
+ : pattern.title.raw
64
+ ),
65
+ };
66
+
67
+ function handleOnSuccess( { pattern: newPattern } ) {
68
+ createSuccessNotice(
69
+ sprintf(
70
+ // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
71
+ __( '"%s" duplicated.' ),
72
+ newPattern.title.raw
73
+ ),
74
+ {
75
+ type: 'snackbar',
76
+ id: 'patterns-create',
77
+ }
78
+ );
79
+
80
+ onSuccess?.( { pattern: newPattern } );
81
+ }
82
+
83
+ return (
84
+ <CreatePatternModal
85
+ modalTitle={ __( 'Duplicate pattern' ) }
86
+ confirmLabel={ __( 'Duplicate' ) }
87
+ onClose={ onClose }
88
+ onError={ onClose }
89
+ onSuccess={ handleOnSuccess }
90
+ { ...duplicatedProps }
91
+ />
92
+ );
93
+ }