Skip to content

Commit

Permalink
fix: Make the commands ordering the registration order of hte mode (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
wayfarer3130 authored Nov 22, 2024
1 parent f0dee87 commit edfaf72
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 56 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "ohif-monorepo-root",
"private": true,
"packageManager": "[email protected]",
"workspaces": {
"packages": [
"platform/*",
Expand Down
40 changes: 17 additions & 23 deletions platform/core/src/classes/CommandsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ import { Command, Commands, ComplexCommand } from '../types/Command';
* to extend this class, please check it's source before adding new methods.
*/
export class CommandsManager {
constructor({} = {}) {
this.contexts = {};
private contexts = {};
// Has the reverse order in which contexts are created, used for the default ordering
private contextOrder = new Array<string>();

constructor(_options = {}) {
// No-op
}

/**
Expand All @@ -33,7 +37,7 @@ export class CommandsManager {
* @param {string} contextName - Namespace for commands
* @returns {undefined}
*/
createContext(contextName) {
createContext(contextName, priority?: number) {
if (!contextName) {
return;
}
Expand All @@ -43,6 +47,8 @@ export class CommandsManager {
}

this.contexts[contextName] = {};
// Add the context name to the start of the list.
this.contextOrder.splice(0, 0, contextName);
}

/**
Expand Down Expand Up @@ -104,34 +110,22 @@ export class CommandsManager {
*
* @method
* @param {String} commandName - Command to find
* @param {String} [contextName] - Specific command to look in. Defaults to current activeContexts
* @param {String} [contextName] - Specific command to look in. Defaults to current activeContexts.
* Also allows an array of contexts to look in.
*/
getCommand = (commandName: string, contextName?: string) => {
getCommand = (commandName: string, contextName: string | string[] = this.contextOrder) => {
const contexts = [];

if (contextName) {
if (Array.isArray(contextName)) {
contexts.push(...contextName.map(name => this.getContext(name)).filter(it => !!it));
} else if (contextName) {
const context = this.getContext(contextName);
if (context) {
contexts.push(context);
}
} else {
Object.keys(this.contexts).forEach(contextName => {
contexts.push(this.getContext(contextName));
});
}

if (contexts.length === 0) {
return;
}

let foundCommand;
contexts.forEach(context => {
if (context[commandName]) {
foundCommand = context[commandName];
}
});

return foundCommand;
return contexts.find(context => !!context[commandName])?.[commandName];
};

/**
Expand All @@ -141,7 +135,7 @@ export class CommandsManager {
* @param {Object} [options={}] - Extra options to pass the command. Like a mousedown event
* @param {String} [contextName]
*/
public runCommand(commandName: string, options = {}, contextName?: string) {
public runCommand(commandName: string, options = {}, contextName?: string | string[]) {
const definition = this.getCommand(commandName, contextName);
if (!definition) {
log.warn(`Command "${commandName}" not found in current context`);
Expand Down
59 changes: 27 additions & 32 deletions platform/docs/docs/platform/managers/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,30 @@ sidebar_label: Commands Manager
## Overview


The `CommandsManager` is a class defined in the `@ohif/core` project. The Commands Manager tracks named commands (or functions) that are scoped to
The `CommandsManager` is a class defined in the `@ohif/core` project.
The Commands Manager tracks named commands (or functions) that are scoped to
a context. When we attempt to run a command with a given name, we look for it
in our active contexts. If found, we run the command, passing in any application
in our active contexts, in the order specified.
If found, we run the command, passing in any application
or call specific data specified in the command's definition.

> Note: A single instance of `CommandsManager` should be defined in the consuming application, and it is used when constructing the `ExtensionManager`.
The order specified is the REVERSE of the order the modules are registered in
for the mode dependency. That is, the last module registered has the highest priority
and will be searched first for commands. This has nothing to do with when the
command itself is registered, although registrations for the same command in different
modules in the same context will also use last registration wins.

> Note: A single instance of `CommandsManager` should be defined in the consuming
application, and it is used when constructing the `ExtensionManager`.

A `simplified skeleton` of the `CommandsManager` is shown below:

```js
export class CommandsManager {
constructor({ getActiveContexts } = {}) {
this.contexts = {};
this._getActiveContexts = getActiveContexts;
}
contexts = {};
contextOrder = [];

constructor(_ignoredConfig) {}

getContext(contextName) {
const context = this.contexts[contextName];
Expand All @@ -33,6 +42,7 @@ export class CommandsManager {
createContext(contextName) {
/** ... **/
this.contexts[contextName] = {};
this.contextOrder.push at beginning(contextName)
}


Expand All @@ -43,6 +53,11 @@ export class CommandsManager {
context[commandName] = definition;
}

getCommand(commandName, contextName) {
const useContext = contextName || first context having commandName in contextOrder
return useContext[commandName];
}

runCommand(commandName, options = {}, contextName) {
const definition = this.getCommand(commandName, contextName);
/**...**/
Expand All @@ -64,33 +79,13 @@ export class CommandsManager {

### Instantiating

When we instantiate the `CommandsManager`, we are passing two methods:

- `getAppState` - Should return the application's state when called (Not implemented in `v3`)
- `getActiveContexts` - Should return the application's active contexts when
called

These methods are used internally to help determine which commands are currently
valid, and how to provide them with any state they may need at the time they are
called.

```js title="platform/app/src/appInit.js"
const commandsManagerConfig = {
getAppState: () => {},
/** Used by commands to determine active context */
getActiveContexts: () => [
'VIEWER',
'DEFAULT',
'ACTIVE_VIEWPORT::CORNERSTONE',
],
};

const commandsManager = new CommandsManager(commandsManagerConfig);
```
No methods or configuration is used within the construction.


## Commands/Context Registration
The `ExtensionManager` handles registering commands and creating contexts, so you don't need to register all your commands manually. Simply, create a `commandsModule` in your extension, and it will get automatically registered in the `context` provided.
The `ExtensionManager` handles registering commands and creating contexts, so you
don't need to register all your commands manually. Simply, create a `commandsModule`
in your extension, and it will get automatically registered in the `context` provided.

A *simplified version* of this registration is shown below to give an idea about the process.

Expand Down Expand Up @@ -173,7 +168,7 @@ use `runCommand(commandName, options = {}, contextName)`.
commandsManager.runCommand('speak', { command: 'hello' });

// Run command, from Default context
commandsManager.runCommand('speak', { command: 'hello' }, ['DEFAULT']);
commandsManager.runCommand('speak', { command: 'hello' }, 'DEFAULT');

// Returns all commands for a given context
commandsManager.getContext('string');
Expand Down

0 comments on commit edfaf72

Please sign in to comment.