@wordpress/commands 0.4.0 → 0.6.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 (66) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +28 -0
  3. package/build/components/command-menu.js +78 -72
  4. package/build/components/command-menu.js.map +1 -1
  5. package/build/hooks/use-command-context.js +47 -0
  6. package/build/hooks/use-command-context.js.map +1 -0
  7. package/build/hooks/use-command-loader.js +6 -11
  8. package/build/hooks/use-command-loader.js.map +1 -1
  9. package/build/hooks/use-command.js +4 -3
  10. package/build/hooks/use-command.js.map +1 -1
  11. package/build/index.js +26 -0
  12. package/build/index.js.map +1 -1
  13. package/build/lock-unlock.js +19 -0
  14. package/build/lock-unlock.js.map +1 -0
  15. package/build/private-apis.js +5 -21
  16. package/build/private-apis.js.map +1 -1
  17. package/build/store/actions.js +19 -40
  18. package/build/store/actions.js.map +1 -1
  19. package/build/store/index.js +5 -0
  20. package/build/store/index.js.map +1 -1
  21. package/build/store/private-actions.js +21 -0
  22. package/build/store/private-actions.js.map +1 -0
  23. package/build/store/reducer.js +38 -34
  24. package/build/store/reducer.js.map +1 -1
  25. package/build/store/selectors.js +14 -17
  26. package/build/store/selectors.js.map +1 -1
  27. package/build-module/components/command-menu.js +78 -72
  28. package/build-module/components/command-menu.js.map +1 -1
  29. package/build-module/hooks/use-command-context.js +36 -0
  30. package/build-module/hooks/use-command-context.js.map +1 -0
  31. package/build-module/hooks/use-command-loader.js +6 -11
  32. package/build-module/hooks/use-command-loader.js.map +1 -1
  33. package/build-module/hooks/use-command.js +4 -3
  34. package/build-module/hooks/use-command.js.map +1 -1
  35. package/build-module/index.js +3 -0
  36. package/build-module/index.js.map +1 -1
  37. package/build-module/lock-unlock.js +9 -0
  38. package/build-module/lock-unlock.js.map +1 -0
  39. package/build-module/private-apis.js +3 -15
  40. package/build-module/private-apis.js.map +1 -1
  41. package/build-module/store/actions.js +19 -40
  42. package/build-module/store/actions.js.map +1 -1
  43. package/build-module/store/index.js +3 -0
  44. package/build-module/store/index.js.map +1 -1
  45. package/build-module/store/private-actions.js +14 -0
  46. package/build-module/store/private-actions.js.map +1 -0
  47. package/build-module/store/reducer.js +38 -34
  48. package/build-module/store/reducer.js.map +1 -1
  49. package/build-module/store/selectors.js +11 -16
  50. package/build-module/store/selectors.js.map +1 -1
  51. package/build-style/style-rtl.css +6 -1
  52. package/build-style/style.css +6 -1
  53. package/package.json +9 -9
  54. package/src/components/command-menu.js +38 -35
  55. package/src/components/style.scss +8 -1
  56. package/src/hooks/use-command-context.js +33 -0
  57. package/src/hooks/use-command-loader.js +12 -6
  58. package/src/hooks/use-command.js +5 -3
  59. package/src/index.js +3 -0
  60. package/src/lock-unlock.js +10 -0
  61. package/src/private-apis.js +3 -17
  62. package/src/store/actions.js +17 -26
  63. package/src/store/index.js +3 -0
  64. package/src/store/private-actions.js +13 -0
  65. package/src/store/reducer.js +33 -27
  66. package/src/store/selectors.js +18 -20
@@ -1 +1 @@
1
- {"version":3,"sources":["@wordpress/commands/src/store/selectors.js"],"names":["createSelector","unique","array","filter","value","index","current","indexOf","getGroups","state","Object","keys","commands","concat","commandLoaders","getCommands","group","values","getCommandLoaders","isOpen"],"mappings":"AAAA;AACA;AACA;AACA,OAAOA,cAAP,MAA2B,QAA3B;;AAEA,SAASC,MAAT,CAAiBC,KAAjB,EAAyB;AACxB,SAAOA,KAAK,CAACC,MAAN,CACN,CAAEC,KAAF,EAASC,KAAT,EAAgBC,OAAhB,KAA6BA,OAAO,CAACC,OAAR,CAAiBH,KAAjB,MAA6BC,KADpD,CAAP;AAGA;;AAED,OAAO,MAAMG,SAAS,GAAGR,cAAc,CACpCS,KAAF,IACCR,MAAM,CACLS,MAAM,CAACC,IAAP,CAAaF,KAAK,CAACG,QAAnB,EAA8BC,MAA9B,CACCH,MAAM,CAACC,IAAP,CAAaF,KAAK,CAACK,cAAnB,CADD,CADK,CAF+B,EAOpCL,KAAF,IAAa,CAAEA,KAAK,CAACG,QAAR,EAAkBH,KAAK,CAACK,cAAxB,CAPyB,CAAhC;AAUP,OAAO,MAAMC,WAAW,GAAGf,cAAc,CACxC,CAAES,KAAF,EAASO,KAAT;AAAA;;AAAA,SAAoBN,MAAM,CAACO,MAAP,0BAAeR,KAAK,CAACG,QAAN,CAAgBI,KAAhB,CAAf,yEAA0C,EAA1C,CAApB;AAAA,CADwC,EAExC,CAAEP,KAAF,EAASO,KAAT,KAAoB,CAAEP,KAAK,CAACG,QAAN,CAAgBI,KAAhB,CAAF,CAFoB,CAAlC;AAKP,OAAO,MAAME,iBAAiB,GAAGlB,cAAc,CAC9C,CAAES,KAAF,EAASO,KAAT;AAAA;;AAAA,SAAoBN,MAAM,CAACO,MAAP,0BAAeR,KAAK,CAACK,cAAN,CAAsBE,KAAtB,CAAf,yEAAgD,EAAhD,CAApB;AAAA,CAD8C,EAE9C,CAAEP,KAAF,EAASO,KAAT,KAAoB,CAAEP,KAAK,CAACK,cAAN,CAAsBE,KAAtB,CAAF,CAF0B,CAAxC;AAKP,OAAO,SAASG,MAAT,CAAiBV,KAAjB,EAAyB;AAC/B,SAAOA,KAAK,CAACU,MAAb;AACA","sourcesContent":["/**\n * External dependencies\n */\nimport createSelector from 'rememo';\n\nfunction unique( array ) {\n\treturn array.filter(\n\t\t( value, index, current ) => current.indexOf( value ) === index\n\t);\n}\n\nexport const getGroups = createSelector(\n\t( state ) =>\n\t\tunique(\n\t\t\tObject.keys( state.commands ).concat(\n\t\t\t\tObject.keys( state.commandLoaders )\n\t\t\t)\n\t\t),\n\t( state ) => [ state.commands, state.commandLoaders ]\n);\n\nexport const getCommands = createSelector(\n\t( state, group ) => Object.values( state.commands[ group ] ?? {} ),\n\t( state, group ) => [ state.commands[ group ] ]\n);\n\nexport const getCommandLoaders = createSelector(\n\t( state, group ) => Object.values( state.commandLoaders[ group ] ?? {} ),\n\t( state, group ) => [ state.commandLoaders[ group ] ]\n);\n\nexport function isOpen( state ) {\n\treturn state.isOpen;\n}\n"]}
1
+ {"version":3,"sources":["@wordpress/commands/src/store/selectors.js"],"names":["createSelector","getCommands","state","contextual","Object","values","commands","filter","command","isContextual","context","getCommandLoaders","commandLoaders","loader","isOpen","getContext"],"mappings":"AAAA;AACA;AACA;AACA,OAAOA,cAAP,MAA2B,QAA3B;AAEA,OAAO,MAAMC,WAAW,GAAGD,cAAc,CACxC,CAAEE,KAAF,EAASC,UAAU,GAAG,KAAtB,KACCC,MAAM,CAACC,MAAP,CAAeH,KAAK,CAACI,QAArB,EAAgCC,MAAhC,CAA0CC,OAAF,IAAe;AACtD,QAAMC,YAAY,GACjBD,OAAO,CAACE,OAAR,IAAmBF,OAAO,CAACE,OAAR,KAAoBR,KAAK,CAACQ,OAD9C;AAEA,SAAOP,UAAU,GAAGM,YAAH,GAAkB,CAAEA,YAArC;AACA,CAJD,CAFuC,EAOtCP,KAAF,IAAa,CAAEA,KAAK,CAACI,QAAR,EAAkBJ,KAAK,CAACQ,OAAxB,CAP2B,CAAlC;AAUP,OAAO,MAAMC,iBAAiB,GAAGX,cAAc,CAC9C,CAAEE,KAAF,EAASC,UAAU,GAAG,KAAtB,KACCC,MAAM,CAACC,MAAP,CAAeH,KAAK,CAACU,cAArB,EAAsCL,MAAtC,CAAgDM,MAAF,IAAc;AAC3D,QAAMJ,YAAY,GACjBI,MAAM,CAACH,OAAP,IAAkBG,MAAM,CAACH,OAAP,KAAmBR,KAAK,CAACQ,OAD5C;AAEA,SAAOP,UAAU,GAAGM,YAAH,GAAkB,CAAEA,YAArC;AACA,CAJD,CAF6C,EAO5CP,KAAF,IAAa,CAAEA,KAAK,CAACU,cAAR,EAAwBV,KAAK,CAACQ,OAA9B,CAPiC,CAAxC;AAUP,OAAO,SAASI,MAAT,CAAiBZ,KAAjB,EAAyB;AAC/B,SAAOA,KAAK,CAACY,MAAb;AACA;AAED,OAAO,SAASC,UAAT,CAAqBb,KAArB,EAA6B;AACnC,SAAOA,KAAK,CAACQ,OAAb;AACA","sourcesContent":["/**\n * External dependencies\n */\nimport createSelector from 'rememo';\n\nexport const getCommands = createSelector(\n\t( state, contextual = false ) =>\n\t\tObject.values( state.commands ).filter( ( command ) => {\n\t\t\tconst isContextual =\n\t\t\t\tcommand.context && command.context === state.context;\n\t\t\treturn contextual ? isContextual : ! isContextual;\n\t\t} ),\n\t( state ) => [ state.commands, state.context ]\n);\n\nexport const getCommandLoaders = createSelector(\n\t( state, contextual = false ) =>\n\t\tObject.values( state.commandLoaders ).filter( ( loader ) => {\n\t\t\tconst isContextual =\n\t\t\t\tloader.context && loader.context === state.context;\n\t\t\treturn contextual ? isContextual : ! isContextual;\n\t\t} ),\n\t( state ) => [ state.commandLoaders, state.context ]\n);\n\nexport function isOpen( state ) {\n\treturn state.isOpen;\n}\n\nexport function getContext( state ) {\n\treturn state.context;\n}\n"]}
@@ -97,7 +97,7 @@
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
99
  }
100
- @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
100
+ @media (min-resolution: 192dpi) {
101
101
  :root {
102
102
  --wp-admin-border-width-focus: 1.5px;
103
103
  }
@@ -184,6 +184,8 @@
184
184
  .commands-command-menu__container [cmdk-root] > [cmdk-list] {
185
185
  max-height: 400px;
186
186
  overflow: auto;
187
+ }
188
+ .commands-command-menu__container [cmdk-root] > [cmdk-list] > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
187
189
  padding: 8px;
188
190
  }
189
191
  .commands-command-menu__container [cmdk-empty] {
@@ -200,6 +202,9 @@
200
202
  .commands-command-menu__container [cmdk-list-sizer] {
201
203
  position: relative;
202
204
  }
205
+ .commands-command-menu__container [cmdk-group]:has([cmdk-group-items]:not(:empty)) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) {
206
+ border-top: 1px solid #e0e0e0;
207
+ }
203
208
 
204
209
  .commands-command-menu__item mark {
205
210
  color: inherit;
@@ -97,7 +97,7 @@
97
97
  --wp-block-synced-color: #7a00df;
98
98
  --wp-block-synced-color--rgb: 122, 0, 223;
99
99
  }
100
- @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
100
+ @media (min-resolution: 192dpi) {
101
101
  :root {
102
102
  --wp-admin-border-width-focus: 1.5px;
103
103
  }
@@ -184,6 +184,8 @@
184
184
  .commands-command-menu__container [cmdk-root] > [cmdk-list] {
185
185
  max-height: 400px;
186
186
  overflow: auto;
187
+ }
188
+ .commands-command-menu__container [cmdk-root] > [cmdk-list] > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
187
189
  padding: 8px;
188
190
  }
189
191
  .commands-command-menu__container [cmdk-empty] {
@@ -200,6 +202,9 @@
200
202
  .commands-command-menu__container [cmdk-list-sizer] {
201
203
  position: relative;
202
204
  }
205
+ .commands-command-menu__container [cmdk-group]:has([cmdk-group-items]:not(:empty)) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) {
206
+ border-top: 1px solid #e0e0e0;
207
+ }
203
208
 
204
209
  .commands-command-menu__item mark {
205
210
  color: inherit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/commands",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Handles the commands menu.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -27,13 +27,13 @@
27
27
  "react-native": "src/index",
28
28
  "dependencies": {
29
29
  "@babel/runtime": "^7.16.0",
30
- "@wordpress/components": "^24.0.0",
31
- "@wordpress/data": "^9.3.0",
32
- "@wordpress/element": "^5.10.0",
33
- "@wordpress/i18n": "^4.33.0",
34
- "@wordpress/icons": "^9.24.0",
35
- "@wordpress/keyboard-shortcuts": "^4.10.0",
36
- "@wordpress/private-apis": "^0.15.0",
30
+ "@wordpress/components": "^25.1.0",
31
+ "@wordpress/data": "^9.5.0",
32
+ "@wordpress/element": "^5.12.0",
33
+ "@wordpress/i18n": "^4.35.0",
34
+ "@wordpress/icons": "^9.26.0",
35
+ "@wordpress/keyboard-shortcuts": "^4.12.0",
36
+ "@wordpress/private-apis": "^0.17.0",
37
37
  "cmdk": "^0.2.0",
38
38
  "rememo": "^4.0.2"
39
39
  },
@@ -43,5 +43,5 @@
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  },
46
- "gitHead": "e936127e1e13881f1a940b7bd1593a9e500147f3"
46
+ "gitHead": "a92f606309b1541b834ff9b0a76ed2a466fc45ed"
47
47
  }
@@ -31,17 +31,17 @@ function CommandMenuLoader( { name, search, hook, setLoader, close } ) {
31
31
  setLoader( name, isLoading );
32
32
  }, [ setLoader, name, isLoading ] );
33
33
 
34
+ if ( ! commands.length ) {
35
+ return null;
36
+ }
37
+
34
38
  return (
35
39
  <>
36
40
  <Command.List>
37
- { isLoading && (
38
- <Command.Loading>{ __( 'Searching…' ) }</Command.Loading>
39
- ) }
40
-
41
41
  { commands.map( ( command ) => (
42
42
  <Command.Item
43
43
  key={ command.name }
44
- value={ command.name }
44
+ value={ command.searchLabel ?? command.label }
45
45
  onSelect={ () => command.callback( { close } ) }
46
46
  >
47
47
  <HStack
@@ -89,24 +89,28 @@ export function CommandMenuLoaderWrapper( { hook, search, setLoader, close } ) {
89
89
  );
90
90
  }
91
91
 
92
- export function CommandMenuGroup( { group, search, setLoader, close } ) {
92
+ export function CommandMenuGroup( { isContextual, search, setLoader, close } ) {
93
93
  const { commands, loaders } = useSelect(
94
94
  ( select ) => {
95
95
  const { getCommands, getCommandLoaders } = select( commandsStore );
96
96
  return {
97
- commands: getCommands( group ),
98
- loaders: getCommandLoaders( group ),
97
+ commands: getCommands( isContextual ),
98
+ loaders: getCommandLoaders( isContextual ),
99
99
  };
100
100
  },
101
- [ group ]
101
+ [ isContextual ]
102
102
  );
103
103
 
104
+ if ( ! commands.length && ! loaders.length ) {
105
+ return null;
106
+ }
107
+
104
108
  return (
105
109
  <Command.Group>
106
110
  { commands.map( ( command ) => (
107
111
  <Command.Item
108
112
  key={ command.name }
109
- value={ command.name }
113
+ value={ command.searchLabel ?? command.label }
110
114
  onSelect={ () => command.callback( { close } ) }
111
115
  >
112
116
  <HStack
@@ -139,13 +143,10 @@ export function CommandMenuGroup( { group, search, setLoader, close } ) {
139
143
  export function CommandMenu() {
140
144
  const { registerShortcut } = useDispatch( keyboardShortcutsStore );
141
145
  const [ search, setSearch ] = useState( '' );
142
- const { groups, isOpen } = useSelect( ( select ) => {
143
- const { getGroups, isOpen: _isOpen } = select( commandsStore );
144
- return {
145
- groups: getGroups(),
146
- isOpen: _isOpen(),
147
- };
148
- }, [] );
146
+ const isOpen = useSelect(
147
+ ( select ) => select( commandsStore ).isOpen(),
148
+ []
149
+ );
149
150
  const { open, close } = useDispatch( commandsStore );
150
151
  const [ loaders, setLoaders ] = useState( {} );
151
152
  const commandMenuInput = useRef();
@@ -219,24 +220,26 @@ export function CommandMenu() {
219
220
  placeholder={ __( 'Type a command or search' ) }
220
221
  />
221
222
  </div>
222
- { search && (
223
- <Command.List>
224
- { ! isLoading && (
225
- <Command.Empty>
226
- { __( 'No results found.' ) }
227
- </Command.Empty>
228
- ) }
229
- { groups.map( ( group ) => (
230
- <CommandMenuGroup
231
- key={ group }
232
- group={ group }
233
- search={ search }
234
- setLoader={ setLoader }
235
- close={ closeAndReset }
236
- />
237
- ) ) }
238
- </Command.List>
239
- ) }
223
+ <Command.List>
224
+ { search && ! isLoading && (
225
+ <Command.Empty>
226
+ { __( 'No results found.' ) }
227
+ </Command.Empty>
228
+ ) }
229
+ <CommandMenuGroup
230
+ search={ search }
231
+ setLoader={ setLoader }
232
+ close={ closeAndReset }
233
+ isContextual
234
+ />
235
+ { search && (
236
+ <CommandMenuGroup
237
+ search={ search }
238
+ setLoader={ setLoader }
239
+ close={ closeAndReset }
240
+ />
241
+ ) }
242
+ </Command.List>
240
243
  </Command>
241
244
  </div>
242
245
  </Modal>
@@ -93,7 +93,10 @@
93
93
  [cmdk-root] > [cmdk-list] {
94
94
  max-height: 400px;
95
95
  overflow: auto;
96
- padding: $grid-unit;
96
+
97
+ & > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
98
+ padding: $grid-unit;
99
+ }
97
100
  }
98
101
 
99
102
  [cmdk-empty] {
@@ -112,6 +115,10 @@
112
115
  [cmdk-list-sizer] {
113
116
  position: relative;
114
117
  }
118
+
119
+ [cmdk-group]:has([cmdk-group-items]:not(:empty)) + [cmdk-group]:has([cmdk-group-items]:not(:empty)) {
120
+ border-top: 1px solid $gray-200;
121
+ }
115
122
  }
116
123
 
117
124
  .commands-command-menu__item mark {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useEffect, useRef } from '@wordpress/element';
5
+ import { useDispatch, useSelect } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as commandsStore } from '../store';
11
+ import { unlock } from '../lock-unlock';
12
+
13
+ /**
14
+ * Sets the active context of the command center
15
+ *
16
+ * @param {string} context Context to set.
17
+ */
18
+ export default function useCommandContext( context ) {
19
+ const { getContext } = useSelect( commandsStore );
20
+ const initialContext = useRef( getContext() );
21
+ const { setContext } = unlock( useDispatch( commandsStore ) );
22
+
23
+ useEffect( () => {
24
+ setContext( context );
25
+ }, [ context, setContext ] );
26
+
27
+ // This effects ensures that on unmount, we restore the context
28
+ // that was set before the component actually mounts.
29
+ useEffect( () => {
30
+ const initialContextRef = initialContext.current;
31
+ return () => setContext( initialContextRef );
32
+ }, [ setContext ] );
33
+ }
@@ -14,17 +14,23 @@ import { store as commandsStore } from '../store';
14
14
  *
15
15
  * @param {import('../store/actions').WPCommandLoaderConfig} loader command loader config.
16
16
  */
17
- export default function useCommandLoader( { name, group, hook } ) {
17
+ export default function useCommandLoader( loader ) {
18
18
  const { registerCommandLoader, unregisterCommandLoader } =
19
19
  useDispatch( commandsStore );
20
20
  useEffect( () => {
21
21
  registerCommandLoader( {
22
- name,
23
- group,
24
- hook,
22
+ name: loader.name,
23
+ hook: loader.hook,
24
+ context: loader.context,
25
25
  } );
26
26
  return () => {
27
- unregisterCommandLoader( name, group );
27
+ unregisterCommandLoader( loader.name );
28
28
  };
29
- }, [ name, group, hook, registerCommandLoader, unregisterCommandLoader ] );
29
+ }, [
30
+ loader.name,
31
+ loader.hook,
32
+ loader.context,
33
+ registerCommandLoader,
34
+ unregisterCommandLoader,
35
+ ] );
30
36
  }
@@ -24,19 +24,21 @@ export default function useCommand( command ) {
24
24
  useEffect( () => {
25
25
  registerCommand( {
26
26
  name: command.name,
27
- group: command.group,
27
+ context: command.context,
28
28
  label: command.label,
29
+ searchLabel: command.searchLabel,
29
30
  icon: command.icon,
30
31
  callback: currentCallback.current,
31
32
  } );
32
33
  return () => {
33
- unregisterCommand( command.name, command.group );
34
+ unregisterCommand( command.name );
34
35
  };
35
36
  }, [
36
37
  command.name,
37
38
  command.label,
38
- command.group,
39
+ command.searchLabel,
39
40
  command.icon,
41
+ command.context,
40
42
  registerCommand,
41
43
  unregisterCommand,
42
44
  ] );
package/src/index.js CHANGED
@@ -1,2 +1,5 @@
1
1
  export { CommandMenu } from './components/command-menu';
2
2
  export { privateApis } from './private-apis';
3
+ export { default as useCommand } from './hooks/use-command';
4
+ export { default as useCommandLoader } from './hooks/use-command-loader';
5
+ export { store } from './store';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
5
+
6
+ export const { lock, unlock } =
7
+ __dangerousOptInToUnstableAPIsOnlyForCoreModules(
8
+ 'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
9
+ '@wordpress/commands'
10
+ );
@@ -1,24 +1,10 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
9
- import { default as useCommand } from './hooks/use-command';
10
- import { default as useCommandLoader } from './hooks/use-command-loader';
11
- import { store } from './store';
12
-
13
- export const { lock, unlock } =
14
- __dangerousOptInToUnstableAPIsOnlyForCoreModules(
15
- 'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
16
- '@wordpress/commands'
17
- );
4
+ import { default as useCommandContext } from './hooks/use-command-context';
5
+ import { lock } from './lock-unlock';
18
6
 
19
7
  export const privateApis = {};
20
8
  lock( privateApis, {
21
- useCommand,
22
- useCommandLoader,
23
- store,
9
+ useCommandContext,
24
10
  } );
@@ -5,11 +5,12 @@
5
5
  *
6
6
  * @typedef {Object} WPCommandConfig
7
7
  *
8
- * @property {string} name Command name.
9
- * @property {string} label Command label.
10
- * @property {string=} group Command group.
11
- * @property {JSX.Element} icon Command icon.
12
- * @property {Function} callback Command callback.
8
+ * @property {string} name Command name.
9
+ * @property {string} label Command label.
10
+ * @property {string=} searchLabel Command search label.
11
+ * @property {string=} context Command context.
12
+ * @property {JSX.Element} icon Command icon.
13
+ * @property {Function} callback Command callback.
13
14
  */
14
15
 
15
16
  /**
@@ -21,9 +22,9 @@
21
22
  *
22
23
  * @typedef {Object} WPCommandLoaderConfig
23
24
  *
24
- * @property {string} name Command loader name.
25
- * @property {string=} group Command loader group.
26
- * @property {WPCommandLoaderHook} hook Command loader hook.
25
+ * @property {string} name Command loader name.
26
+ * @property {string=} context Command loader context.
27
+ * @property {WPCommandLoaderHook} hook Command loader hook.
27
28
  */
28
29
 
29
30
  /**
@@ -33,30 +34,24 @@
33
34
  *
34
35
  * @return {Object} action.
35
36
  */
36
- export function registerCommand( { name, label, icon, callback, group = '' } ) {
37
+ export function registerCommand( config ) {
37
38
  return {
38
39
  type: 'REGISTER_COMMAND',
39
- name,
40
- label,
41
- icon,
42
- callback,
43
- group,
40
+ ...config,
44
41
  };
45
42
  }
46
43
 
47
44
  /**
48
45
  * Returns an action object used to unregister a command.
49
46
  *
50
- * @param {string} name Command name.
51
- * @param {string} group Command group.
47
+ * @param {string} name Command name.
52
48
  *
53
49
  * @return {Object} action.
54
50
  */
55
- export function unregisterCommand( name, group ) {
51
+ export function unregisterCommand( name ) {
56
52
  return {
57
53
  type: 'UNREGISTER_COMMAND',
58
54
  name,
59
- group,
60
55
  };
61
56
  }
62
57
 
@@ -67,28 +62,24 @@ export function unregisterCommand( name, group ) {
67
62
  *
68
63
  * @return {Object} action.
69
64
  */
70
- export function registerCommandLoader( { name, group = '', hook } ) {
65
+ export function registerCommandLoader( config ) {
71
66
  return {
72
67
  type: 'REGISTER_COMMAND_LOADER',
73
- name,
74
- group,
75
- hook,
68
+ ...config,
76
69
  };
77
70
  }
78
71
 
79
72
  /**
80
73
  * Unregister command loader hook.
81
74
  *
82
- * @param {string} name Command loader name.
83
- * @param {string} group Command loader group.
75
+ * @param {string} name Command loader name.
84
76
  *
85
77
  * @return {Object} action.
86
78
  */
87
- export function unregisterCommandLoader( name, group ) {
79
+ export function unregisterCommandLoader( name ) {
88
80
  return {
89
81
  type: 'UNREGISTER_COMMAND_LOADER',
90
82
  name,
91
- group,
92
83
  };
93
84
  }
94
85
 
@@ -9,6 +9,8 @@ import { createReduxStore, register } from '@wordpress/data';
9
9
  import reducer from './reducer';
10
10
  import * as actions from './actions';
11
11
  import * as selectors from './selectors';
12
+ import * as privateActions from './private-actions';
13
+ import { unlock } from '../lock-unlock';
12
14
 
13
15
  const STORE_NAME = 'core/commands';
14
16
 
@@ -26,3 +28,4 @@ export const store = createReduxStore( STORE_NAME, {
26
28
  } );
27
29
 
28
30
  register( store );
31
+ unlock( store ).registerPrivateActions( privateActions );
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Sets the active context.
3
+ *
4
+ * @param {string} context Context.
5
+ *
6
+ * @return {Object} action.
7
+ */
8
+ export function setContext( context ) {
9
+ return {
10
+ type: 'SET_CONTEXT',
11
+ context,
12
+ };
13
+ }
@@ -16,24 +16,18 @@ function commands( state = {}, action ) {
16
16
  case 'REGISTER_COMMAND':
17
17
  return {
18
18
  ...state,
19
- [ action.group ]: {
20
- ...state[ action.group ],
21
- [ action.name ]: {
22
- name: action.name,
23
- label: action.label,
24
- group: action.group,
25
- callback: action.callback,
26
- icon: action.icon,
27
- },
19
+ [ action.name ]: {
20
+ name: action.name,
21
+ label: action.label,
22
+ searchLabel: action.searchLabel,
23
+ context: action.context,
24
+ callback: action.callback,
25
+ icon: action.icon,
28
26
  },
29
27
  };
30
28
  case 'UNREGISTER_COMMAND': {
31
- const { [ action.name ]: _, ...remainingState } =
32
- state?.[ action.group ];
33
- return {
34
- ...state,
35
- [ action.group ]: remainingState,
36
- };
29
+ const { [ action.name ]: _, ...remainingState } = state;
30
+ return remainingState;
37
31
  }
38
32
  }
39
33
 
@@ -53,21 +47,15 @@ function commandLoaders( state = {}, action ) {
53
47
  case 'REGISTER_COMMAND_LOADER':
54
48
  return {
55
49
  ...state,
56
- [ action.group ]: {
57
- ...state[ action.group ],
58
- [ action.name ]: {
59
- name: action.name,
60
- hook: action.hook,
61
- },
50
+ [ action.name ]: {
51
+ name: action.name,
52
+ context: action.context,
53
+ hook: action.hook,
62
54
  },
63
55
  };
64
56
  case 'UNREGISTER_COMMAND_LOADER': {
65
- const { [ action.name ]: _, ...remainingState } =
66
- state?.[ action.group ];
67
- return {
68
- ...state,
69
- [ action.group ]: remainingState,
70
- };
57
+ const { [ action.name ]: _, ...remainingState } = state;
58
+ return remainingState;
71
59
  }
72
60
  }
73
61
 
@@ -93,10 +81,28 @@ function isOpen( state = false, action ) {
93
81
  return state;
94
82
  }
95
83
 
84
+ /**
85
+ * Reducer returning the command center's active context.
86
+ *
87
+ * @param {Object} state Current state.
88
+ * @param {Object} action Dispatched action.
89
+ *
90
+ * @return {boolean} Updated state.
91
+ */
92
+ function context( state = 'root', action ) {
93
+ switch ( action.type ) {
94
+ case 'SET_CONTEXT':
95
+ return action.context;
96
+ }
97
+
98
+ return state;
99
+ }
100
+
96
101
  const reducer = combineReducers( {
97
102
  commands,
98
103
  commandLoaders,
99
104
  isOpen,
105
+ context,
100
106
  } );
101
107
 
102
108
  export default reducer;
@@ -3,32 +3,30 @@
3
3
  */
4
4
  import createSelector from 'rememo';
5
5
 
6
- function unique( array ) {
7
- return array.filter(
8
- ( value, index, current ) => current.indexOf( value ) === index
9
- );
10
- }
11
-
12
- export const getGroups = createSelector(
13
- ( state ) =>
14
- unique(
15
- Object.keys( state.commands ).concat(
16
- Object.keys( state.commandLoaders )
17
- )
18
- ),
19
- ( state ) => [ state.commands, state.commandLoaders ]
20
- );
21
-
22
6
  export const getCommands = createSelector(
23
- ( state, group ) => Object.values( state.commands[ group ] ?? {} ),
24
- ( state, group ) => [ state.commands[ group ] ]
7
+ ( state, contextual = false ) =>
8
+ Object.values( state.commands ).filter( ( command ) => {
9
+ const isContextual =
10
+ command.context && command.context === state.context;
11
+ return contextual ? isContextual : ! isContextual;
12
+ } ),
13
+ ( state ) => [ state.commands, state.context ]
25
14
  );
26
15
 
27
16
  export const getCommandLoaders = createSelector(
28
- ( state, group ) => Object.values( state.commandLoaders[ group ] ?? {} ),
29
- ( state, group ) => [ state.commandLoaders[ group ] ]
17
+ ( state, contextual = false ) =>
18
+ Object.values( state.commandLoaders ).filter( ( loader ) => {
19
+ const isContextual =
20
+ loader.context && loader.context === state.context;
21
+ return contextual ? isContextual : ! isContextual;
22
+ } ),
23
+ ( state ) => [ state.commandLoaders, state.context ]
30
24
  );
31
25
 
32
26
  export function isOpen( state ) {
33
27
  return state.isOpen;
34
28
  }
29
+
30
+ export function getContext( state ) {
31
+ return state.context;
32
+ }