@wordpress/commands 0.3.0 → 0.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 (51) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +16 -0
  3. package/build/components/command-menu.js +83 -57
  4. package/build/components/command-menu.js.map +1 -1
  5. package/build/hooks/use-command-context.js +45 -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 +5 -3
  10. package/build/hooks/use-command.js.map +1 -1
  11. package/build/index.js +18 -0
  12. package/build/index.js.map +1 -1
  13. package/build/private-apis.js +4 -4
  14. package/build/private-apis.js.map +1 -1
  15. package/build/store/actions.js +61 -40
  16. package/build/store/actions.js.map +1 -1
  17. package/build/store/reducer.js +63 -22
  18. package/build/store/reducer.js.map +1 -1
  19. package/build/store/selectors.js +25 -17
  20. package/build/store/selectors.js.map +1 -1
  21. package/build-module/components/command-menu.js +83 -57
  22. package/build-module/components/command-menu.js.map +1 -1
  23. package/build-module/hooks/use-command-context.js +35 -0
  24. package/build-module/hooks/use-command-context.js.map +1 -0
  25. package/build-module/hooks/use-command-loader.js +6 -11
  26. package/build-module/hooks/use-command-loader.js.map +1 -1
  27. package/build-module/hooks/use-command.js +5 -3
  28. package/build-module/hooks/use-command.js.map +1 -1
  29. package/build-module/index.js +2 -0
  30. package/build-module/index.js.map +1 -1
  31. package/build-module/private-apis.js +4 -4
  32. package/build-module/private-apis.js.map +1 -1
  33. package/build-module/store/actions.js +55 -40
  34. package/build-module/store/actions.js.map +1 -1
  35. package/build-module/store/reducer.js +63 -22
  36. package/build-module/store/reducer.js.map +1 -1
  37. package/build-module/store/selectors.js +19 -15
  38. package/build-module/store/selectors.js.map +1 -1
  39. package/build-style/style-rtl.css +9 -1
  40. package/build-style/style.css +9 -1
  41. package/package.json +9 -9
  42. package/src/components/command-menu.js +57 -43
  43. package/src/components/style.scss +11 -1
  44. package/src/hooks/use-command-context.js +32 -0
  45. package/src/hooks/use-command-loader.js +12 -6
  46. package/src/hooks/use-command.js +7 -3
  47. package/src/index.js +2 -0
  48. package/src/private-apis.js +4 -4
  49. package/src/store/actions.js +53 -26
  50. package/src/store/reducer.js +53 -27
  51. package/src/store/selectors.js +22 -19
@@ -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
  }
@@ -136,6 +136,9 @@
136
136
  border-bottom-right-radius: 0;
137
137
  }
138
138
 
139
+ .commands-command-menu__container {
140
+ will-change: transform;
141
+ }
139
142
  .commands-command-menu__container [cmdk-input] {
140
143
  border: none;
141
144
  width: 100%;
@@ -181,6 +184,8 @@
181
184
  .commands-command-menu__container [cmdk-root] > [cmdk-list] {
182
185
  max-height: 400px;
183
186
  overflow: auto;
187
+ }
188
+ .commands-command-menu__container [cmdk-root] > [cmdk-list] > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
184
189
  padding: 8px;
185
190
  }
186
191
  .commands-command-menu__container [cmdk-empty] {
@@ -197,6 +202,9 @@
197
202
  .commands-command-menu__container [cmdk-list-sizer] {
198
203
  position: relative;
199
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
+ }
200
208
 
201
209
  .commands-command-menu__item mark {
202
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
  }
@@ -136,6 +136,9 @@
136
136
  border-bottom-left-radius: 0;
137
137
  }
138
138
 
139
+ .commands-command-menu__container {
140
+ will-change: transform;
141
+ }
139
142
  .commands-command-menu__container [cmdk-input] {
140
143
  border: none;
141
144
  width: 100%;
@@ -181,6 +184,8 @@
181
184
  .commands-command-menu__container [cmdk-root] > [cmdk-list] {
182
185
  max-height: 400px;
183
186
  overflow: auto;
187
+ }
188
+ .commands-command-menu__container [cmdk-root] > [cmdk-list] > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
184
189
  padding: 8px;
185
190
  }
186
191
  .commands-command-menu__container [cmdk-empty] {
@@ -197,6 +202,9 @@
197
202
  .commands-command-menu__container [cmdk-list-sizer] {
198
203
  position: relative;
199
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
+ }
200
208
 
201
209
  .commands-command-menu__item mark {
202
210
  color: inherit;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/commands",
3
- "version": "0.3.0",
3
+ "version": "0.5.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": "^23.9.0",
31
- "@wordpress/data": "^9.2.0",
32
- "@wordpress/element": "^5.9.0",
33
- "@wordpress/i18n": "^4.32.0",
34
- "@wordpress/icons": "^9.23.0",
35
- "@wordpress/keyboard-shortcuts": "^4.9.0",
36
- "@wordpress/private-apis": "^0.14.0",
30
+ "@wordpress/components": "^25.0.0",
31
+ "@wordpress/data": "^9.4.0",
32
+ "@wordpress/element": "^5.11.0",
33
+ "@wordpress/i18n": "^4.34.0",
34
+ "@wordpress/icons": "^9.25.0",
35
+ "@wordpress/keyboard-shortcuts": "^4.11.0",
36
+ "@wordpress/private-apis": "^0.16.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": "6df0c62d43b8901414ccd22ffbe56eaa99d012a6"
46
+ "gitHead": "c7c79cb11b677adcbf06cf5f8cfb6c5ec1699f19"
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,14 +143,13 @@ 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 [ open, setOpen ] = useState( false );
143
- const { groups } = useSelect( ( select ) => {
144
- const { getGroups } = select( commandsStore );
145
- return {
146
- groups: getGroups(),
147
- };
148
- }, [] );
146
+ const isOpen = useSelect(
147
+ ( select ) => select( commandsStore ).isOpen(),
148
+ []
149
+ );
150
+ const { open, close } = useDispatch( commandsStore );
149
151
  const [ loaders, setLoaders ] = useState( {} );
152
+ const commandMenuInput = useRef();
150
153
 
151
154
  useEffect( () => {
152
155
  registerShortcut( {
@@ -164,7 +167,11 @@ export function CommandMenu() {
164
167
  'core/commands',
165
168
  ( event ) => {
166
169
  event.preventDefault();
167
- setOpen( ( prevOpen ) => ! prevOpen );
170
+ if ( isOpen ) {
171
+ close();
172
+ } else {
173
+ open();
174
+ }
168
175
  },
169
176
  {
170
177
  bindGlobal: true,
@@ -179,12 +186,19 @@ export function CommandMenu() {
179
186
  } ) ),
180
187
  []
181
188
  );
182
- const close = () => {
189
+ const closeAndReset = () => {
183
190
  setSearch( '' );
184
- setOpen( false );
191
+ close();
185
192
  };
186
193
 
187
- if ( ! open ) {
194
+ useEffect( () => {
195
+ // Focus the command menu input when mounting the modal.
196
+ if ( isOpen ) {
197
+ commandMenuInput.current.focus();
198
+ }
199
+ }, [ isOpen ] );
200
+
201
+ if ( ! isOpen ) {
188
202
  return false;
189
203
  }
190
204
  const isLoading = Object.values( loaders ).some( Boolean );
@@ -193,39 +207,39 @@ export function CommandMenu() {
193
207
  <Modal
194
208
  className="commands-command-menu"
195
209
  overlayClassName="commands-command-menu__overlay"
196
- onRequestClose={ close }
210
+ onRequestClose={ closeAndReset }
197
211
  __experimentalHideHeader
198
212
  >
199
213
  <div className="commands-command-menu__container">
200
214
  <Command label={ __( 'Global Command Menu' ) }>
201
215
  <div className="commands-command-menu__header">
202
216
  <Command.Input
203
- // The input should be focused when the modal is opened.
204
- // eslint-disable-next-line jsx-a11y/no-autofocus
205
- autoFocus
217
+ ref={ commandMenuInput }
206
218
  value={ search }
207
219
  onValueChange={ setSearch }
208
220
  placeholder={ __( 'Type a command or search' ) }
209
221
  />
210
222
  </div>
211
- { search && (
212
- <Command.List>
213
- { ! isLoading && (
214
- <Command.Empty>
215
- { __( 'No results found.' ) }
216
- </Command.Empty>
217
- ) }
218
- { groups.map( ( group ) => (
219
- <CommandMenuGroup
220
- key={ group }
221
- group={ group }
222
- search={ search }
223
- setLoader={ setLoader }
224
- close={ close }
225
- />
226
- ) ) }
227
- </Command.List>
228
- ) }
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>
229
243
  </Command>
230
244
  </div>
231
245
  </Modal>
@@ -36,6 +36,9 @@
36
36
  }
37
37
 
38
38
  .commands-command-menu__container {
39
+ // the style here is a hack to force safari to repaint to avoid a style glitch
40
+ will-change: transform;
41
+
39
42
  [cmdk-input] {
40
43
  border: none;
41
44
  width: 100%;
@@ -90,7 +93,10 @@
90
93
  [cmdk-root] > [cmdk-list] {
91
94
  max-height: 400px;
92
95
  overflow: auto;
93
- padding: $grid-unit;
96
+
97
+ & > [cmdk-list-sizer] :has([cmdk-group-items]:not(:empty)) {
98
+ padding: $grid-unit;
99
+ }
94
100
  }
95
101
 
96
102
  [cmdk-empty] {
@@ -109,6 +115,10 @@
109
115
  [cmdk-list-sizer] {
110
116
  position: relative;
111
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
+ }
112
122
  }
113
123
 
114
124
  .commands-command-menu__item mark {
@@ -0,0 +1,32 @@
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
+
12
+ /**
13
+ * Sets the active context of the command center
14
+ *
15
+ * @param {string} context Context to set.
16
+ */
17
+ export default function useCommandContext( context ) {
18
+ const { getContext } = useSelect( commandsStore );
19
+ const initialContext = useRef( getContext() );
20
+ const { setContext } = useDispatch( commandsStore );
21
+
22
+ useEffect( () => {
23
+ setContext( context );
24
+ }, [ context, setContext ] );
25
+
26
+ // This effects ensures that on unmount, we restore the context
27
+ // that was set before the component actually mounts.
28
+ useEffect( () => {
29
+ const initialContextRef = initialContext.current;
30
+ return () => setContext( initialContextRef );
31
+ }, [ setContext ] );
32
+ }
@@ -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,17 +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,
30
+ icon: command.icon,
29
31
  callback: currentCallback.current,
30
32
  } );
31
33
  return () => {
32
- unregisterCommand( command.name, command.group );
34
+ unregisterCommand( command.name );
33
35
  };
34
36
  }, [
35
37
  command.name,
36
38
  command.label,
37
- command.group,
39
+ command.searchLabel,
40
+ command.icon,
41
+ command.context,
38
42
  registerCommand,
39
43
  unregisterCommand,
40
44
  ] );
package/src/index.js CHANGED
@@ -1,2 +1,4 @@
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';
@@ -6,8 +6,8 @@ import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/pri
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
- import { default as useCommand } from './hooks/use-command';
10
- import { default as useCommandLoader } from './hooks/use-command-loader';
9
+ import { default as useCommandContext } from './hooks/use-command-context';
10
+ import { store } from './store';
11
11
 
12
12
  export const { lock, unlock } =
13
13
  __dangerousOptInToUnstableAPIsOnlyForCoreModules(
@@ -17,6 +17,6 @@ export const { lock, unlock } =
17
17
 
18
18
  export const privateApis = {};
19
19
  lock( privateApis, {
20
- useCommand,
21
- useCommandLoader,
20
+ useCommandContext,
21
+ store,
22
22
  } );
@@ -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,27 +62,59 @@ 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,
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Opens the command center.
88
+ *
89
+ * @return {Object} action.
90
+ */
91
+ export function open() {
92
+ return {
93
+ type: 'OPEN',
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Closes the command center.
99
+ *
100
+ * @return {Object} action.
101
+ */
102
+ export function close() {
103
+ return {
104
+ type: 'CLOSE',
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Sets the active context.
110
+ *
111
+ * @param {string} context Context.
112
+ *
113
+ * @return {Object} action.
114
+ */
115
+ export function setContext( context ) {
116
+ return {
117
+ type: 'SET_CONTEXT',
118
+ context,
92
119
  };
93
120
  }
@@ -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,30 +47,62 @@ 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
 
74
62
  return state;
75
63
  }
76
64
 
65
+ /**
66
+ * Reducer returning the command center open state.
67
+ *
68
+ * @param {Object} state Current state.
69
+ * @param {Object} action Dispatched action.
70
+ *
71
+ * @return {boolean} Updated state.
72
+ */
73
+ function isOpen( state = false, action ) {
74
+ switch ( action.type ) {
75
+ case 'OPEN':
76
+ return true;
77
+ case 'CLOSE':
78
+ return false;
79
+ }
80
+
81
+ return state;
82
+ }
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
+
77
101
  const reducer = combineReducers( {
78
102
  commands,
79
103
  commandLoaders,
104
+ isOpen,
105
+ context,
80
106
  } );
81
107
 
82
108
  export default reducer;