diff --git a/packages/framework/esm-extensions/src/workspaces.ts b/packages/framework/esm-extensions/src/workspaces.ts index bebd9a5e1..ba93fd993 100644 --- a/packages/framework/esm-extensions/src/workspaces.ts +++ b/packages/framework/esm-extensions/src/workspaces.ts @@ -14,11 +14,10 @@ export interface WorkspaceRegistration { canHide: boolean; canMaximize: boolean; width: 'narrow' | 'wider' | 'extra-wide'; - hasOwnSidebar: boolean; - sidebarFamily: string; preferredWindowSize: WorkspaceWindowState; load: () => Promise<{ default?: LifeCycles } & LifeCycles>; moduleName: string; + groups?: Array; } interface WorkspaceRegistrationStore { @@ -37,11 +36,10 @@ export interface RegisterWorkspaceOptions { canHide?: boolean; canMaximize?: boolean; width?: 'narrow' | 'wider' | 'extra-wide'; - hasOwnSidebar?: boolean; - sidebarFamily?: string; preferredWindowSize?: WorkspaceWindowState; load: () => Promise<{ default?: LifeCycles } & LifeCycles>; moduleName: string; + groups?: Array; } /** @@ -60,8 +58,7 @@ export function registerWorkspace(workspace: RegisterWorkspaceOptions) { canHide: workspace.canHide ?? false, canMaximize: workspace.canMaximize ?? false, width: workspace.width ?? 'narrow', - hasOwnSidebar: workspace.hasOwnSidebar ?? false, - sidebarFamily: workspace.sidebarFamily ?? 'default', + groups: workspace.groups ?? [], }, }, })); @@ -97,8 +94,7 @@ export function getWorkspaceRegistration(name: string): WorkspaceRegistration { canHide: workspaceExtension.meta?.canHide ?? false, canMaximize: workspaceExtension.meta?.canMaximize ?? false, width: workspaceExtension.meta?.width ?? 'narrow', - sidebarFamily: 'default', - hasOwnSidebar: false, + groups: workspaceExtension?.meta?.groups ?? [], }; } else { throw new Error(`No workspace named '${name}' has been registered.`); diff --git a/packages/framework/esm-framework/docs/API.md b/packages/framework/esm-framework/docs/API.md index 809015f34..d41eac208 100644 --- a/packages/framework/esm-framework/docs/API.md +++ b/packages/framework/esm-framework/docs/API.md @@ -262,6 +262,7 @@ - [closeWorkspace](API.md#closeworkspace) - [launchWorkspace](API.md#launchworkspace) +- [launchWorkspaceGroup](API.md#launchworkspacegroup) - [navigateAndLaunchWorkspace](API.md#navigateandlaunchworkspace) - [useWorkspaces](API.md#useworkspaces) @@ -609,7 +610,7 @@ ___ #### Defined in -[packages/framework/esm-globals/src/types.ts:396](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L396) +[packages/framework/esm-globals/src/types.ts:409](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L409) ___ @@ -622,7 +623,7 @@ Basically, this is the same as the app routes, with each routes definition keyed #### Defined in -[packages/framework/esm-globals/src/types.ts:387](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L387) +[packages/framework/esm-globals/src/types.ts:400](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L400) ___ @@ -792,7 +793,7 @@ ___ ### WorkspaceDefinition -Ƭ **WorkspaceDefinition**: { `canHide?`: `boolean` ; `canMaximize?`: `boolean` ; `hasOwnSidebar?`: `boolean` ; `name`: `string` ; `preferredWindowSize?`: [`WorkspaceWindowState`](API.md#workspacewindowstate) ; `sidebarFamily?`: `string` ; `title`: `string` ; `type`: `string` ; `width?`: ``"narrow"`` \| ``"wider"`` \| ``"extra-wide"`` } & { `component`: `string` } \| { `component?`: `never` } +Ƭ **WorkspaceDefinition**: { `canHide?`: `boolean` ; `canMaximize?`: `boolean` ; `groups`: `string`[] ; `name`: `string` ; `preferredWindowSize?`: [`WorkspaceWindowState`](API.md#workspacewindowstate) ; `title`: `string` ; `type`: `string` ; `width?`: ``"narrow"`` \| ``"wider"`` \| ``"extra-wide"`` } & { `component`: `string` } \| { `component?`: `never` } A definition of a workspace as extracted from an app's routes.json @@ -8048,7 +8049,7 @@ Function to close an opened workspace #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:304](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L304) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:425](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L425) ___ @@ -8099,7 +8100,38 @@ prop named `workspaceTitle` will override the title of the workspace. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:177](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L177) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:289](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L289) + +___ + +### launchWorkspaceGroup + +▸ **launchWorkspaceGroup**(`groupName`, `args`): `void` + +Launches a workspace group with the specified name and configuration. +If there are any open workspaces, it will first close them before launching the new workspace group. + +**`example`** +launchWorkspaceGroup("myGroup", { + state: initialState, + onWorkspaceGroupLaunch: () => console.log("Workspace group launched"), + workspaceGroupCleanup: () => console.log("Cleaning up workspace group") +}); + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `groupName` | `string` | The name of the workspace group to launch | +| `args` | `LaunchWorkspaceGroupArg` | Configuration object for launching the workspace group | + +#### Returns + +`void` + +#### Defined in + +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:205](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L205) ___ @@ -8125,7 +8157,7 @@ Use this function to navigate to a new page and launch a workspace on that page. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:262](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L262) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:382](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L382) ___ @@ -8139,4 +8171,4 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:418](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L418) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:536](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L536) diff --git a/packages/framework/esm-framework/docs/interfaces/CloseWorkspaceOptions.md b/packages/framework/esm-framework/docs/interfaces/CloseWorkspaceOptions.md index cba18139c..0b6851461 100644 --- a/packages/framework/esm-framework/docs/interfaces/CloseWorkspaceOptions.md +++ b/packages/framework/esm-framework/docs/interfaces/CloseWorkspaceOptions.md @@ -6,6 +6,7 @@ ### Workspace Properties +- [closeWorkspaceGroup](CloseWorkspaceOptions.md#closeworkspacegroup) - [ignoreChanges](CloseWorkspaceOptions.md#ignorechanges) ### Workspace Methods @@ -14,6 +15,22 @@ ## Workspace Properties +### closeWorkspaceGroup + +• `Optional` **closeWorkspaceGroup**: `boolean` + +Controls whether the workspace group should be closed and store to be +cleared when this workspace is closed. +Defaults to true except when opening a new workspace of the same group. + +**`default`** true + +#### Defined in + +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:33](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L33) + +___ + ### ignoreChanges • `Optional` **ignoreChanges**: `boolean` diff --git a/packages/framework/esm-framework/docs/interfaces/DefaultWorkspaceProps.md b/packages/framework/esm-framework/docs/interfaces/DefaultWorkspaceProps.md index 1ecd23b3e..57d80a2b7 100644 --- a/packages/framework/esm-framework/docs/interfaces/DefaultWorkspaceProps.md +++ b/packages/framework/esm-framework/docs/interfaces/DefaultWorkspaceProps.md @@ -43,7 +43,7 @@ closed, given the user forcefully closes the workspace. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:46](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L46) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:45](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L45) ___ @@ -66,7 +66,7 @@ will directly close the workspace without any prompt #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:56](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L56) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:55](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L55) ___ @@ -89,7 +89,7 @@ this workspace is closed; e.g. if there is unsaved data. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:51](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L51) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:50](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L50) ___ @@ -117,4 +117,4 @@ title needs to be set dynamically. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:71](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L71) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L70) diff --git a/packages/framework/esm-framework/docs/interfaces/FeatureFlagDefinition.md b/packages/framework/esm-framework/docs/interfaces/FeatureFlagDefinition.md index 87c81cb85..4059543f4 100644 --- a/packages/framework/esm-framework/docs/interfaces/FeatureFlagDefinition.md +++ b/packages/framework/esm-framework/docs/interfaces/FeatureFlagDefinition.md @@ -22,7 +22,7 @@ An explanation of what the flag does, which will be displayed in the Implementer #### Defined in -[packages/framework/esm-globals/src/types.ts:350](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L350) +[packages/framework/esm-globals/src/types.ts:363](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L363) ___ @@ -34,7 +34,7 @@ A code-friendly name for the flag, which will be used to reference it in code #### Defined in -[packages/framework/esm-globals/src/types.ts:346](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L346) +[packages/framework/esm-globals/src/types.ts:359](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L359) ___ @@ -46,4 +46,4 @@ A human-friendly name which will be displayed in the Implementer Tools #### Defined in -[packages/framework/esm-globals/src/types.ts:348](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L348) +[packages/framework/esm-globals/src/types.ts:361](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L361) diff --git a/packages/framework/esm-framework/docs/interfaces/OpenWorkspace.md b/packages/framework/esm-framework/docs/interfaces/OpenWorkspace.md index 78c5f359b..8aab4ab19 100644 --- a/packages/framework/esm-framework/docs/interfaces/OpenWorkspace.md +++ b/packages/framework/esm-framework/docs/interfaces/OpenWorkspace.md @@ -16,11 +16,10 @@ - [canHide](OpenWorkspace.md#canhide) - [canMaximize](OpenWorkspace.md#canmaximize) -- [hasOwnSidebar](OpenWorkspace.md#hasownsidebar) +- [groups](OpenWorkspace.md#groups) - [moduleName](OpenWorkspace.md#modulename) - [name](OpenWorkspace.md#name) - [preferredWindowSize](OpenWorkspace.md#preferredwindowsize) -- [sidebarFamily](OpenWorkspace.md#sidebarfamily) - [title](OpenWorkspace.md#title) - [titleNode](OpenWorkspace.md#titlenode) - [type](OpenWorkspace.md#type) @@ -29,6 +28,7 @@ ### Workspace Properties - [additionalProps](OpenWorkspace.md#additionalprops) +- [currentWorkspaceGroup](OpenWorkspace.md#currentworkspacegroup) ### Methods @@ -68,17 +68,17 @@ ___ ___ -### hasOwnSidebar +### groups -• **hasOwnSidebar**: `boolean` +• `Optional` **groups**: `string`[] #### Inherited from -[WorkspaceRegistration](WorkspaceRegistration.md).[hasOwnSidebar](WorkspaceRegistration.md#hasownsidebar) +[WorkspaceRegistration](WorkspaceRegistration.md).[groups](WorkspaceRegistration.md#groups) #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L17) +[packages/framework/esm-extensions/src/workspaces.ts:20](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L20) ___ @@ -92,7 +92,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:21](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L21) +[packages/framework/esm-extensions/src/workspaces.ts:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L19) ___ @@ -120,21 +120,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L19) - -___ - -### sidebarFamily - -• **sidebarFamily**: `string` - -#### Inherited from - -[WorkspaceRegistration](WorkspaceRegistration.md).[sidebarFamily](WorkspaceRegistration.md#sidebarfamily) - -#### Defined in - -[packages/framework/esm-extensions/src/workspaces.ts:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L18) +[packages/framework/esm-extensions/src/workspaces.ts:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L17) ___ @@ -202,7 +188,17 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:106](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L106) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:109](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L109) + +___ + +### currentWorkspaceGroup + +• `Optional` **currentWorkspaceGroup**: `string` + +#### Defined in + +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:110](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L110) ## Methods @@ -232,7 +228,7 @@ closed, given the user forcefully closes the workspace. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:46](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L46) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:45](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L45) ___ @@ -259,7 +255,7 @@ will directly close the workspace without any prompt #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:56](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L56) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:55](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L55) ___ @@ -277,7 +273,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:20](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L20) +[packages/framework/esm-extensions/src/workspaces.ts:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L18) ___ @@ -304,7 +300,7 @@ this workspace is closed; e.g. if there is unsaved data. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:51](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L51) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:50](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L50) ___ @@ -336,4 +332,4 @@ title needs to be set dynamically. #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:71](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L71) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:70](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L70) diff --git a/packages/framework/esm-framework/docs/interfaces/OpenmrsAppRoutes.md b/packages/framework/esm-framework/docs/interfaces/OpenmrsAppRoutes.md index 797c8579c..1080d7d7a 100644 --- a/packages/framework/esm-framework/docs/interfaces/OpenmrsAppRoutes.md +++ b/packages/framework/esm-framework/docs/interfaces/OpenmrsAppRoutes.md @@ -27,7 +27,7 @@ A list of backend modules necessary for this frontend module and the correspondi #### Defined in -[packages/framework/esm-globals/src/types.ts:358](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L358) +[packages/framework/esm-globals/src/types.ts:371](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L371) ___ @@ -39,7 +39,7 @@ An array of all extensions supported by this frontend module. Extensions can be #### Defined in -[packages/framework/esm-globals/src/types.ts:374](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L374) +[packages/framework/esm-globals/src/types.ts:387](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L387) ___ @@ -51,7 +51,7 @@ An array of all feature flags for any beta-stage features this module provides. #### Defined in -[packages/framework/esm-globals/src/types.ts:376](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L376) +[packages/framework/esm-globals/src/types.ts:389](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L389) ___ @@ -63,7 +63,7 @@ An array of all modals supported by this frontend module. Modals can be launched #### Defined in -[packages/framework/esm-globals/src/types.ts:378](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L378) +[packages/framework/esm-globals/src/types.ts:391](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L391) ___ @@ -81,7 +81,7 @@ The name of the backend dependency and either the required version or an object #### Defined in -[packages/framework/esm-globals/src/types.ts:360](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L360) +[packages/framework/esm-globals/src/types.ts:373](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L373) ___ @@ -93,7 +93,7 @@ An array of all pages supported by this frontend module. Pages are automatically #### Defined in -[packages/framework/esm-globals/src/types.ts:372](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L372) +[packages/framework/esm-globals/src/types.ts:385](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L385) ___ @@ -105,7 +105,7 @@ The version of this frontend module. #### Defined in -[packages/framework/esm-globals/src/types.ts:356](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L356) +[packages/framework/esm-globals/src/types.ts:369](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L369) ___ @@ -117,4 +117,4 @@ An array of all workspaces supported by this frontend module. Workspaces can be #### Defined in -[packages/framework/esm-globals/src/types.ts:380](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L380) +[packages/framework/esm-globals/src/types.ts:393](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L393) diff --git a/packages/framework/esm-framework/docs/interfaces/Prompt.md b/packages/framework/esm-framework/docs/interfaces/Prompt.md index 468ad3480..c9e509fb7 100644 --- a/packages/framework/esm-framework/docs/interfaces/Prompt.md +++ b/packages/framework/esm-framework/docs/interfaces/Prompt.md @@ -23,7 +23,7 @@ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:90](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L90) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:89](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L89) ___ @@ -35,7 +35,7 @@ Defaults to "Cancel" #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:95](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L95) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:94](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L94) ___ @@ -47,7 +47,7 @@ Defaults to "Confirm" #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:92](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L92) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:91](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L91) ___ @@ -57,7 +57,7 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:89](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L89) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:88](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L88) ## Methods @@ -71,4 +71,4 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:93](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L93) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:92](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L92) diff --git a/packages/framework/esm-framework/docs/interfaces/ResourceLoader.md b/packages/framework/esm-framework/docs/interfaces/ResourceLoader.md index e7faed7bc..e4535a5ca 100644 --- a/packages/framework/esm-framework/docs/interfaces/ResourceLoader.md +++ b/packages/framework/esm-framework/docs/interfaces/ResourceLoader.md @@ -20,4 +20,4 @@ #### Defined in -[packages/framework/esm-globals/src/types.ts:390](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L390) +[packages/framework/esm-globals/src/types.ts:403](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-globals/src/types.ts#L403) diff --git a/packages/framework/esm-framework/docs/interfaces/WorkspaceRegistration.md b/packages/framework/esm-framework/docs/interfaces/WorkspaceRegistration.md index 551b9c588..ada48462f 100644 --- a/packages/framework/esm-framework/docs/interfaces/WorkspaceRegistration.md +++ b/packages/framework/esm-framework/docs/interfaces/WorkspaceRegistration.md @@ -16,11 +16,10 @@ See [WorkspaceDefinition](../API.md#workspacedefinition) for more information ab - [canHide](WorkspaceRegistration.md#canhide) - [canMaximize](WorkspaceRegistration.md#canmaximize) -- [hasOwnSidebar](WorkspaceRegistration.md#hasownsidebar) +- [groups](WorkspaceRegistration.md#groups) - [moduleName](WorkspaceRegistration.md#modulename) - [name](WorkspaceRegistration.md#name) - [preferredWindowSize](WorkspaceRegistration.md#preferredwindowsize) -- [sidebarFamily](WorkspaceRegistration.md#sidebarfamily) - [title](WorkspaceRegistration.md#title) - [titleNode](WorkspaceRegistration.md#titlenode) - [type](WorkspaceRegistration.md#type) @@ -52,13 +51,13 @@ ___ ___ -### hasOwnSidebar +### groups -• **hasOwnSidebar**: `boolean` +• `Optional` **groups**: `string`[] #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L17) +[packages/framework/esm-extensions/src/workspaces.ts:20](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L20) ___ @@ -68,7 +67,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:21](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L21) +[packages/framework/esm-extensions/src/workspaces.ts:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L19) ___ @@ -88,17 +87,7 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:19](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L19) - -___ - -### sidebarFamily - -• **sidebarFamily**: `string` - -#### Defined in - -[packages/framework/esm-extensions/src/workspaces.ts:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L18) +[packages/framework/esm-extensions/src/workspaces.ts:17](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L17) ___ @@ -152,4 +141,4 @@ ___ #### Defined in -[packages/framework/esm-extensions/src/workspaces.ts:20](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L20) +[packages/framework/esm-extensions/src/workspaces.ts:18](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-extensions/src/workspaces.ts#L18) diff --git a/packages/framework/esm-framework/docs/interfaces/WorkspacesInfo.md b/packages/framework/esm-framework/docs/interfaces/WorkspacesInfo.md index 2d5a1a4b1..98149ebe4 100644 --- a/packages/framework/esm-framework/docs/interfaces/WorkspacesInfo.md +++ b/packages/framework/esm-framework/docs/interfaces/WorkspacesInfo.md @@ -8,6 +8,7 @@ - [active](WorkspacesInfo.md#active) - [prompt](WorkspacesInfo.md#prompt) +- [workspaceGroup](WorkspacesInfo.md#workspacegroup) - [workspaceWindowState](WorkspacesInfo.md#workspacewindowstate) - [workspaces](WorkspacesInfo.md#workspaces) @@ -19,7 +20,7 @@ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:412](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L412) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:529](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L529) ___ @@ -29,7 +30,24 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:413](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L413) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:530](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L530) + +___ + +### workspaceGroup + +• `Optional` **workspaceGroup**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `cleanup?` | `Function` | +| `name` | `string` | + +#### Defined in + +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:533](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L533) ___ @@ -39,7 +57,7 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:414](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L414) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:531](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L531) ___ @@ -49,4 +67,4 @@ ___ #### Defined in -[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:415](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L415) +[packages/framework/esm-styleguide/src/workspaces/workspaces.ts:532](https://github.com/openmrs/openmrs-esm-core/blob/main/packages/framework/esm-styleguide/src/workspaces/workspaces.ts#L532) diff --git a/packages/framework/esm-globals/src/types.ts b/packages/framework/esm-globals/src/types.ts index bfd9b9c47..5675a6494 100644 --- a/packages/framework/esm-globals/src/types.ts +++ b/packages/framework/esm-globals/src/types.ts @@ -303,18 +303,31 @@ export type WorkspaceDefinition = { */ width?: 'narrow' | 'wider' | 'extra-wide'; /** - * Controls whether the workspace has its own sidebar. If true, the sidebar will be displayed when the workspace is open. + * Launches the workspace in the preferred size, it defaults to the 'narrow' width */ - hasOwnSidebar?: boolean; + preferredWindowSize?: WorkspaceWindowState; + /** - * Sidebars have icons that representing workspaces. The sidebar family is the name of the - * sidebar that should contain the icon for this workspace. This is generally only needed if - * `hasOwnSidebar` is true, in which case this is the name of that sidebar. If multiple - * workspaces have `hasOwnSidebar` set to true and the same family name, then the sidebar - * within the workspace area will have icons for each of those workspaces. + * Workspaces can open either independently or as part of a "workspace group". A + * "workspace group" groups related workspaces together, so that only one is visible + * at a time. For example, + * + * @example + * + * { + * name: 'order-basket', + * type: 'order', + * groups: ['ward-patient'] + * } + * + * This means that the 'order-basket' workspace can be opened independently, or only + * in the 'ward-patient'. + * If a workspace group is already open and a new workspace is launched, and the + * groups in the newly launched workspace do not include the currently open group’s + * name, the entire workspace group will close, and the new workspace will launch independently. + * */ - sidebarFamily?: string; - preferredWindowSize?: WorkspaceWindowState; + groups: Array; } & ( | { /** diff --git a/packages/framework/esm-routes/src/loaders/components.ts b/packages/framework/esm-routes/src/loaders/components.ts index 791fb7541..3027946b2 100644 --- a/packages/framework/esm-routes/src/loaders/components.ts +++ b/packages/framework/esm-routes/src/loaders/components.ts @@ -193,9 +193,8 @@ supported, so the workspace will not be loaded.`, canHide: workspace.canHide, canMaximize: workspace.canMaximize, width: workspace.width, - hasOwnSidebar: workspace.hasOwnSidebar, - sidebarFamily: workspace.sidebarFamily, preferredWindowSize: workspace.preferredWindowSize, + groups: workspace.groups ?? [], }); } } diff --git a/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx b/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx index c4039a77a..0ba4b7afd 100644 --- a/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx +++ b/packages/framework/esm-styleguide/src/workspaces/container/workspace-container.component.tsx @@ -73,11 +73,16 @@ export function WorkspaceContainer({ actionMenuProps, }: WorkspaceContainerProps) { const layout = useLayoutType(); - const { workspaces, workspaceWindowState } = useWorkspaces(); + const { workspaces, workspaceWindowState, workspaceGroup } = useWorkspaces(); + const currentGroupName = workspaceGroup?.name; const activeWorkspace = workspaces[0]; const isHidden = workspaceWindowState === 'hidden' || activeWorkspace == null; const isMaximized = workspaceWindowState === 'maximized'; const width = activeWorkspace?.width ?? (overlay ? 'wider' : 'narrow'); + const showActionMenu = useMemo( + () => showSiderailAndBottomNav || (currentGroupName && !isHidden), + [currentGroupName, isHidden, showSiderailAndBottomNav], + ); useBodyScrollLock(!isHidden && !isDesktop(layout)); @@ -120,7 +125,13 @@ export function WorkspaceContainer({ - {showSiderailAndBottomNav && } + {showActionMenu && ( + + )} ); } @@ -133,7 +144,8 @@ interface WorkspaceProps { function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspaceProps) { const { t } = useTranslation(); const layout = useLayoutType(); - const { workspaceWindowState } = useWorkspaces(); + const { workspaceWindowState, workspaceGroup } = useWorkspaces(); + const currentGroupName = workspaceGroup?.name; const isMaximized = workspaceWindowState === 'maximized'; // We use the feature name of the app containing the workspace in order to set the extension @@ -148,7 +160,7 @@ function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspacePro const { canHide = false, canMaximize = false, - hasOwnSidebar = false, + currentWorkspaceGroup = '', closeWorkspace, } = useMemo(() => workspaceInstance ?? ({} as OpenWorkspace), [workspaceInstance]); @@ -171,7 +183,7 @@ function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspacePro
@@ -197,7 +209,7 @@ function Workspace({ workspaceInstance, additionalWorkspaceProps }: WorkspacePro {isMaximized ? : } )} - {canHide ? ( + {canHide && !currentGroupName ? (
- {hasOwnSidebar && } ) ); diff --git a/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.component.tsx b/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.component.tsx index 6e3b404c8..c44bc106d 100644 --- a/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.component.tsx +++ b/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.component.tsx @@ -5,7 +5,7 @@ import { InlineLoading } from '@carbon/react'; import { getCoreTranslation } from '@openmrs/esm-translations'; import styles from './workspace.module.scss'; import { type OpenWorkspace } from '../workspaces'; -import { useWorkspaceFamilyStore } from '../workspace-sidebar-store/useWorkspaceFamilyStore'; +import { useWorkspaceGroupStore } from '../workspace-sidebar-store/useWorkspaceGroupStore'; interface WorkspaceRendererProps { workspace: OpenWorkspace; @@ -14,7 +14,7 @@ interface WorkspaceRendererProps { export function WorkspaceRenderer({ workspace, additionalPropsFromPage }: WorkspaceRendererProps) { const [lifecycle, setLifecycle] = useState(); - const workspaceFamilyState = useWorkspaceFamilyStore(workspace.sidebarFamily); + const workspaceGroupState = useWorkspaceGroupStore(workspace.currentWorkspaceGroup); useEffect(() => { let active = true; @@ -36,10 +36,10 @@ export function WorkspaceRenderer({ workspace, additionalPropsFromPage }: Worksp promptBeforeClosing: workspace.promptBeforeClosing, setTitle: workspace.setTitle, ...additionalPropsFromPage, - ...workspaceFamilyState, + ...workspaceGroupState, ...workspace.additionalProps, }, - [workspace, additionalPropsFromPage, workspaceFamilyState], + [workspace, additionalPropsFromPage, workspaceGroupState], ); return lifecycle ? ( diff --git a/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.test.tsx b/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.test.tsx index 71f128af0..e8bf5a028 100644 --- a/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.test.tsx +++ b/packages/framework/esm-styleguide/src/workspaces/container/workspace-renderer.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { WorkspaceRenderer } from './workspace-renderer.component'; -import { getWorkspaceFamilyStore, OpenWorkspace } from '../workspaces'; +import { getWorkspaceGroupStore } from '../workspaces'; import Parcel from 'single-spa-react/parcel'; const mockFn = jest.fn(); @@ -20,10 +20,10 @@ describe('WorkspaceRenderer', () => { const mockedPromptBeforeClosing = jest.fn(); const mockedSetTitle = jest.fn(); const mockedLoadFn = jest.fn().mockImplementation(() => Promise.resolve({ default: 'file-content' })); - getWorkspaceFamilyStore('test-sidebar-family')?.setState({ - // Testing that the workspace family state should be overrided by additionalProps + getWorkspaceGroupStore('test-sidebar-store')?.setState({ + // Testing that the workspace group state should be overrided by additionalProps foo: false, - workspaceFamilyState: {}, + workspaceGroupStore: {}, }); render( { additionalProps: { foo: 'true', }, - sidebarFamily: 'test-sidebar-family', }} additionalPropsFromPage={{ bar: 'true' }} />, @@ -58,7 +57,6 @@ describe('WorkspaceRenderer', () => { setTitle: mockedSetTitle, foo: 'true', bar: 'true', - workspaceFamilyState: {}, }); }); }); diff --git a/packages/framework/esm-styleguide/src/workspaces/public.ts b/packages/framework/esm-styleguide/src/workspaces/public.ts index 778ffa68a..1f90d3bb8 100644 --- a/packages/framework/esm-styleguide/src/workspaces/public.ts +++ b/packages/framework/esm-styleguide/src/workspaces/public.ts @@ -1,6 +1,12 @@ export * from './action-menu-button/action-menu-button.component'; export * from './container/workspace-container.component'; -export { closeWorkspace, launchWorkspace, navigateAndLaunchWorkspace, useWorkspaces } from './workspaces'; +export { + closeWorkspace, + launchWorkspace, + navigateAndLaunchWorkspace, + useWorkspaces, + launchWorkspaceGroup, +} from './workspaces'; export { type DefaultWorkspaceProps, type CloseWorkspaceOptions, diff --git a/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceFamilyStore.ts b/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceFamilyStore.ts deleted file mode 100644 index c3e5d8b02..000000000 --- a/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceFamilyStore.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useEffect, useMemo, useState } from 'react'; -import { getWorkspaceFamilyStore } from '../workspaces'; -import type { StoreApi } from 'zustand/vanilla'; - -/** - * This hook is used to interact with the store of a workspace family. - * A workspace family is defined as a group of workspaces that share the same sidebarFamilyName. - * - * In case a workspace doesn't have a sidebarFamilyName, it will be considered as a standalone workspace, and hence this hook will return an empty object and updateFunction as an empty function. - * - * @internal - * - * @param {string} sidebarFamilyName The sidebarFamilyName of the workspace used when registering the workspace in the module's routes.json file. - */ -export function useWorkspaceFamilyStore(sidebarFamilyName: string) { - const [storeState, setStoreState] = useState({}); - const [currentSidebarFamilyName, setCurrentSidebarFamilyName] = useState(sidebarFamilyName); - - useEffect(() => { - if (currentSidebarFamilyName !== sidebarFamilyName) { - setCurrentSidebarFamilyName(sidebarFamilyName); - } - }, [sidebarFamilyName]); - - useEffect(() => { - const store = getWorkspaceFamilyStore(currentSidebarFamilyName); - let unsubscribe: () => void; - if (store) { - setStoreState(store.getState()); - unsubscribe = store.subscribe(setStoreState); - } - return () => { - if (store) { - unsubscribe?.(); - } - }; - }, [currentSidebarFamilyName]); - - return storeState; -} diff --git a/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceGroupStore.ts b/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceGroupStore.ts new file mode 100644 index 000000000..61ff40f3c --- /dev/null +++ b/packages/framework/esm-styleguide/src/workspaces/workspace-sidebar-store/useWorkspaceGroupStore.ts @@ -0,0 +1,39 @@ +import { useEffect, useState } from 'react'; +import { getWorkspaceGroupStore } from '../workspaces'; + +/** + * This hook is used to interact with the store of a workspace store. + * A workspace store is defined as a group of workspaces that open in the same workspace group. + * + * In case a workspace group is not active, it will be considered as a standalone workspace, and hence this hook will return an empty object and updateFunction as an empty function. + * + * @internal + * + * @param {string} workspaceGroupName The workspaceGroupName of the workspace used when registering the workspace in the module's routes.json file. + */ +export function useWorkspaceGroupStore(workspaceGroupName?: string) { + const [storeState, setStoreState] = useState({}); + const [currentWorkspaceGroupName, setCurrentWorkspaceGroupName] = useState(workspaceGroupName); + + useEffect(() => { + if (currentWorkspaceGroupName !== workspaceGroupName) { + setCurrentWorkspaceGroupName(workspaceGroupName); + } + }, [workspaceGroupName]); + + useEffect(() => { + const store = getWorkspaceGroupStore(currentWorkspaceGroupName); + let unsubscribe: () => void; + if (store) { + setStoreState(store.getState()); + unsubscribe = store.subscribe(setStoreState); + } + return () => { + if (store) { + unsubscribe?.(); + } + }; + }, [currentWorkspaceGroupName]); + + return storeState; +} diff --git a/packages/framework/esm-styleguide/src/workspaces/workspaces.test.ts b/packages/framework/esm-styleguide/src/workspaces/workspaces.test.ts index 7c567884d..7e05d3a5a 100644 --- a/packages/framework/esm-styleguide/src/workspaces/workspaces.test.ts +++ b/packages/framework/esm-styleguide/src/workspaces/workspaces.test.ts @@ -2,9 +2,10 @@ import { type Prompt, cancelPrompt, closeWorkspace, - getWorkspaceFamilyStore, + getWorkspaceGroupStore, getWorkspaceStore, launchWorkspace, + launchWorkspaceGroup, resetWorkspaceStore, } from './workspaces'; import { registerExtension, registerWorkspace } from '@openmrs/esm-extensions'; @@ -440,39 +441,39 @@ describe('workspace system', () => { expect(store.getState().openWorkspaces.length).toBe(0); }); - describe('Testing `getWorkspaceFamilyStore` function', () => { + describe('Testing `getWorkspaceGroupStore` function', () => { it('should return undefined if no workspace sidebar name is passed', () => { - let workspaceFamilyStore = getWorkspaceFamilyStore(undefined); - expect(workspaceFamilyStore).toBeUndefined(); - workspaceFamilyStore = getWorkspaceFamilyStore('default'); - expect(workspaceFamilyStore).toBeUndefined(); + let workspaceGroupStore = getWorkspaceGroupStore(undefined); + expect(workspaceGroupStore).toBeUndefined(); + workspaceGroupStore = getWorkspaceGroupStore('default'); + expect(workspaceGroupStore).toBeUndefined(); }); it('should return store if workspace sidebar name is passed', () => { - const workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar', { + const workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store', { foo: true, }); - expect(workspaceFamilyStore).toBeTruthy(); - expect(workspaceFamilyStore?.getState()?.['foo']).toBe(true); + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); }); - it('should update the store state with new additionalProps if workspaces with same sidebarFamily name calls the function', () => { - let workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar', { + it('should update the store state with new additionalProps if workspaces with same workspaceGroup name calls the function', () => { + let workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store', { foo: true, }); - expect(workspaceFamilyStore).toBeTruthy(); - expect(workspaceFamilyStore?.getState()?.['foo']).toBe(true); - workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar', { + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); + workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store', { bar: true, }); - expect(workspaceFamilyStore).toBeTruthy(); - expect(workspaceFamilyStore?.getState()?.['foo']).toBe(true); - expect(workspaceFamilyStore?.getState()?.['bar']).toBe(true); + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); + expect(workspaceGroupStore?.getState()?.['bar']).toBe(true); }); }); - describe('Testing workspace family store', () => { - it('should create store for workspaces with sidebarFamilyName', () => { + describe('Testing workspace group store', () => { + it('should create store for workspace groups', () => { registerWorkspace({ name: 'allergies', title: 'Allergies', @@ -485,53 +486,56 @@ describe('workspace system', () => { load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', + groups: ['ward-patient-store'], }); - launchWorkspace('ward-patient-workspace'); - const workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); + launchWorkspaceGroup('ward-patient-store', { + state: {}, + workspaceToLaunch: { name: 'ward-patient-workspace' }, + }); + const workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store'); const workspaceStore = getWorkspaceStore(); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); expect(workspaceStore.getState().openWorkspaces[0].name).toBe('ward-patient-workspace'); - expect(workspaceFamilyStore).toBeTruthy(); + expect(workspaceGroupStore).toBeTruthy(); workspaceStore.getState().openWorkspaces[0].closeWorkspace({ ignoreChanges: true }); launchWorkspace('allergies'); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); expect(workspaceStore.getState().openWorkspaces[0].name).toBe('allergies'); - expect(workspaceFamilyStore?.getState()).toStrictEqual({}); + expect(workspaceGroupStore?.getState()).toStrictEqual({}); }); - it('should clear workspace family store by default if the workspace is closed, since `clearWorkspaceFamilyStore` is true by default', async () => { + it('should clear workspace group store by default if the workspace is closed, since `closeWorkspaceGroup` is true by default', async () => { registerWorkspace({ name: 'ward-patient-workspace', title: 'Ward Patient Workspace', load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', + groups: ['ward-patient-store'], }); - launchWorkspace('ward-patient-workspace', { - foo: true, + launchWorkspaceGroup('ward-patient-store', { + state: { + foo: true, + }, + workspaceToLaunch: { name: 'ward-patient-workspace' }, }); - const workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); - expect(workspaceFamilyStore).toBeTruthy(); - expect(workspaceFamilyStore?.getState()?.['foo']).toBe(true); + const workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store'); + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); closeWorkspace('ward-patient-workspace'); - expect(workspaceFamilyStore?.getState()?.['foo']).toBeUndefined(); - expect(workspaceFamilyStore?.getState()).toStrictEqual({}); + expect(workspaceGroupStore?.getState()?.['foo']).toBeUndefined(); + expect(workspaceGroupStore?.getState()).toStrictEqual({}); }); - it('should not clear the workspace store if the new workspace opened is of the same type as the already opened workspace', () => { - const sidebarFamily = 'ward-patient-sidebar'; + it('should not clear the workspace store if the new workspace opened can open in the same group', () => { + const workspaceGroup = 'ward-patient-store'; registerWorkspace({ name: 'ward-patient-workspace', title: 'Ward Patient Workspace', load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily, + groups: [workspaceGroup], }); registerWorkspace({ name: 'transfer-patient-workspace', @@ -539,22 +543,26 @@ describe('workspace system', () => { load: jest.fn(), type: 'transfer-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily, + groups: [workspaceGroup], }); - const workspaceStore = getWorkspaceStore(); - launchWorkspace('ward-patient-workspace', { - foo: true, + launchWorkspaceGroup(workspaceGroup, { + state: { + foo: true, + }, + workspaceToLaunch: { name: 'ward-patient-workspace' }, }); - const sidebarFamilyStore = getWorkspaceFamilyStore(sidebarFamily); + const workspaceStore = getWorkspaceStore(); + const workspaceGroupStore = getWorkspaceGroupStore(workspaceGroup); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); - expect(sidebarFamilyStore).toBeTruthy(); - expect(sidebarFamilyStore?.getState()?.['foo']).toBe(true); - launchWorkspace('transfer-patient-workspace', { bar: false }); + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); + launchWorkspace('transfer-patient-workspace', { + bar: false, + }); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); const transferPatientWorkspace = workspaceStore.getState().openWorkspaces[0]; - expect(sidebarFamilyStore?.getState()?.['foo']).toBe(true); - expect(sidebarFamilyStore?.getState()?.['bar']).toBe(false); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); + expect(workspaceGroupStore?.getState()?.['bar']).toBe(false); }); it('should clear the store when new workspace with different sidebar is opened, given the original workspace cannot hide', () => { @@ -564,8 +572,7 @@ describe('workspace system', () => { load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', + groups: ['ward-patient-store'], }); registerWorkspace({ name: 'transfer-patient-workspace', @@ -573,70 +580,42 @@ describe('workspace system', () => { load: jest.fn(), type: 'transfer-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'another-sidebar-family', + groups: ['another-sidebar-group'], }); const workspaceStore = getWorkspaceStore(); - launchWorkspace('ward-patient-workspace', { - foo: true, + launchWorkspaceGroup('ward-patient-store', { + state: { + foo: true, + }, + workspaceToLaunch: { name: 'ward-patient-workspace' }, }); - const wardPatientFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); + const wardPatientGroupStore = getWorkspaceGroupStore('ward-patient-store'); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); - expect(wardPatientFamilyStore).toBeTruthy(); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBe(true); - launchWorkspace('transfer-patient-workspace', { bar: false }); - const anotherSidebarFamilyStore = getWorkspaceFamilyStore('another-sidebar-family'); - expect(workspaceStore.getState().openWorkspaces.length).toBe(1); - expect(anotherSidebarFamilyStore?.getState()?.['bar']).toBe(false); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBeUndefined(); - expect(wardPatientFamilyStore?.getState()).toStrictEqual({}); - }); - - it('should not clear the store when new workspace with different sidebar is opened, given the original workspace can hide', () => { - registerWorkspace({ - name: 'ward-patient-workspace', - title: 'Ward Patient Workspace', - load: jest.fn(), - type: 'ward-patient', - moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', - canHide: true, - }); - registerWorkspace({ - name: 'transfer-patient-workspace', - title: 'Transfer Patient Workspace', - load: jest.fn(), - type: 'transfer-patient', - moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'another-sidebar-family', - }); - const workspaceStore = getWorkspaceStore(); - launchWorkspace('ward-patient-workspace', { - foo: true, - }); - const wardPatientFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); + expect(wardPatientGroupStore).toBeTruthy(); + expect(wardPatientGroupStore?.getState()?.['foo']).toBe(true); + launchWorkspaceGroup('another-sidebar-group', { + state: { + bar: false, + }, + workspaceToLaunch: { name: 'transfer-patient-workspace' }, + }); + expect(workspaceStore.getState().workspaceGroup?.name).toBe('another-sidebar-group'); + const anotherWorkspaceGroupStore = getWorkspaceGroupStore('another-sidebar-group'); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); - expect(wardPatientFamilyStore).toBeTruthy(); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBe(true); - launchWorkspace('transfer-patient-workspace', { bar: false }); - const anotherSidebarFamilyStore = getWorkspaceFamilyStore('another-sidebar-family'); - expect(workspaceStore.getState().openWorkspaces.length).toBe(2); - expect(anotherSidebarFamilyStore?.getState()?.['bar']).toBe(false); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBe(true); + expect(anotherWorkspaceGroupStore?.getState()?.['bar']).toBe(false); + expect(wardPatientGroupStore?.getState()?.['foo']).toBeUndefined(); + expect(wardPatientGroupStore?.getState()).toStrictEqual({}); }); - it('should not clear the workspace if a workspace of same sidebar family is opened', () => { + it('should not clear the workspace if a workspace of same sidebar group is opened', () => { registerWorkspace({ name: 'ward-patient-workspace', title: 'Ward Patient Workspace', load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', canHide: true, + groups: ['ward-patient-store'], }); registerWorkspace({ name: 'transfer-patient-workspace', @@ -644,21 +623,23 @@ describe('workspace system', () => { load: jest.fn(), type: 'transfer-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', + groups: ['ward-patient-store'], }); const workspaceStore = getWorkspaceStore(); - launchWorkspace('ward-patient-workspace', { - foo: true, + launchWorkspaceGroup('ward-patient-store', { + state: { + foo: true, + }, + workspaceToLaunch: { name: 'ward-patient-workspace' }, }); - const wardPatientFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); + const wardPatientGroupStore = getWorkspaceGroupStore('ward-patient-store'); expect(workspaceStore.getState().openWorkspaces.length).toBe(1); - expect(wardPatientFamilyStore).toBeTruthy(); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBe(true); + expect(wardPatientGroupStore).toBeTruthy(); + expect(wardPatientGroupStore?.getState()?.['foo']).toBe(true); launchWorkspace('transfer-patient-workspace', { bar: false }); expect(workspaceStore.getState().openWorkspaces.length).toBe(2); - expect(wardPatientFamilyStore?.getState()?.['foo']).toBe(true); - expect(wardPatientFamilyStore?.getState()?.['bar']).toBe(false); + expect(wardPatientGroupStore?.getState()?.['foo']).toBe(true); + expect(wardPatientGroupStore?.getState()?.['bar']).toBe(false); }); it('should retain default closeWorkspace options in case workspace options are passed', () => { @@ -668,19 +649,19 @@ describe('workspace system', () => { load: jest.fn(), type: 'ward-patient', moduleName: '@openmrs/esm-ward-app', - hasOwnSidebar: true, - sidebarFamily: 'ward-patient-sidebar', + groups: ['ward-patient-store'], }); - launchWorkspace('ward-patient-workspace', { - foo: true, + launchWorkspaceGroup('ward-patient-store', { + state: { foo: true }, + workspaceToLaunch: { name: 'ward-patient-workspace' }, }); - const workspaceFamilyStore = getWorkspaceFamilyStore('ward-patient-sidebar'); - expect(workspaceFamilyStore).toBeTruthy(); - expect(workspaceFamilyStore?.getState()?.['foo']).toBe(true); + const workspaceGroupStore = getWorkspaceGroupStore('ward-patient-store'); + expect(workspaceGroupStore).toBeTruthy(); + expect(workspaceGroupStore?.getState()?.['foo']).toBe(true); // test that default options are interpolated when providing options to `closeWorkspace` closeWorkspace('ward-patient-workspace', { ignoreChanges: true }); - expect(workspaceFamilyStore?.getState()?.['foo']).toBeUndefined(); - expect(workspaceFamilyStore?.getState()).toStrictEqual({}); + expect(workspaceGroupStore?.getState()?.['foo']).toBeUndefined(); + expect(workspaceGroupStore?.getState()).toStrictEqual({}); }); }); }); diff --git a/packages/framework/esm-styleguide/src/workspaces/workspaces.ts b/packages/framework/esm-styleguide/src/workspaces/workspaces.ts index 4b56873ef..3f3fd26b1 100644 --- a/packages/framework/esm-styleguide/src/workspaces/workspaces.ts +++ b/packages/framework/esm-styleguide/src/workspaces/workspaces.ts @@ -23,15 +23,14 @@ export interface CloseWorkspaceOptions { * @returns void */ onWorkspaceClose?: () => void; -} - -interface CloseWorkspaceInternalOptions extends CloseWorkspaceOptions { /** - * Controls whether the workspace family store will be cleared when this workspace is closed. Defaults to true except when opening a new workspace of the same family. + * Controls whether the workspace group should be closed and store to be + * cleared when this workspace is closed. + * Defaults to true except when opening a new workspace of the same group. * * @default true */ - clearWorkspaceFamilyStore?: boolean; + closeWorkspaceGroup?: boolean; } /** The default parameters received by all workspaces */ @@ -100,10 +99,15 @@ export interface WorkspaceStoreState { openWorkspaces: Array; prompt: Prompt | null; workspaceWindowState: WorkspaceWindowState; + workspaceGroup?: { + name: string; + cleanup?: Function; + }; } export interface OpenWorkspace extends WorkspaceRegistration, DefaultWorkspaceProps { additionalProps: object; + currentWorkspaceGroup?: string; } /** @@ -120,6 +124,114 @@ export function canCloseWorkspaceWithoutPrompting(name: string, ignoreChanges: b return !promptBeforeFn || !promptBeforeFn(); } +/** + * Closes a workspace group and performs cleanup operations. + * + * @param groupName - The name of the workspace group to close + * @param onWorkspaceCloseup - Optional callback function to execute after closing the workspace group + * @returns void, or exits early if the current workspace group name doesn't match the provided group name + * + * @remarks + * This function performs the following operations: + * - Validates if the provided group name matches the current workspace group + * - Closes all workspaces associated with the group + * - Clears the workspace group store state + * - Executes cleanup function if defined in the workspace group + * - Updates the main workspace store to remove the workspace group + * - Calls the optional closeup callback if provided + */ +function closeWorkspaceGroup(groupName: string, onWorkspaceCloseup?: () => void) { + const store = getWorkspaceStore(); + const currentWorkspaceGroup = store.getState()?.workspaceGroup; + const currentGroupName = currentWorkspaceGroup?.name; + if (!currentGroupName || groupName !== currentGroupName) { + return; + } + const filter: (workspace: OpenWorkspace) => boolean = currentGroupName + ? (workspace) => workspace.currentWorkspaceGroup === currentGroupName + : () => true; + + closeAllWorkspaces(() => { + // Clearing the workspace group and respective store if the new workspace is not part of the current group, which is handled in the `launchWorkspace` function. + const workspaceGroupStore = getWorkspaceGroupStore(groupName); + if (workspaceGroupStore) { + workspaceGroupStore.setState({}, true); + const unsubscribe = workspaceGroupStore.subscribe(() => {}); + unsubscribe(); + } + + if (typeof currentWorkspaceGroup?.cleanup === 'function') { + currentWorkspaceGroup.cleanup(); + } + + store.setState((prev) => ({ + ...prev, + workspaceGroup: undefined, + })); + + if (typeof onWorkspaceCloseup === 'function') { + onWorkspaceCloseup(); + } + }, filter); +} + +interface LaunchWorkspaceGroupArg { + state: object; + onWorkspaceGroupLaunch?: Function; + workspaceGroupCleanup?: Function; + workspaceToLaunch?: { + name: string; + additionalProps?: object; + }; +} + +/** + * Launches a workspace group with the specified name and configuration. + * If there are any open workspaces, it will first close them before launching the new workspace group. + * + * @param groupName - The name of the workspace group to launch + * @param args - Configuration object for launching the workspace group + * @param args.state - The initial state for the workspace group + * @param args.onWorkspaceGroupLaunch - Optional callback function to be executed after the workspace group is launched + * @param args.workspaceGroupCleanup - Optional cleanup function to be executed when the workspace group is closed + * + * @example + * launchWorkspaceGroup("myGroup", { + * state: initialState, + * onWorkspaceGroupLaunch: () => console.log("Workspace group launched"), + * workspaceGroupCleanup: () => console.log("Cleaning up workspace group") + * }); + */ +export function launchWorkspaceGroup(groupName: string, args: LaunchWorkspaceGroupArg) { + const { state, onWorkspaceGroupLaunch, workspaceGroupCleanup, workspaceToLaunch } = args; + const store = getWorkspaceStore(); + if (store.getState().openWorkspaces.length) { + const workspaceGroup = store.getState().workspaceGroup; + if (workspaceGroup) { + closeWorkspaceGroup(workspaceGroup?.name, () => { + launchWorkspaceGroup(groupName, args); + }); + } else { + closeAllWorkspaces(() => { + launchWorkspaceGroup(groupName, args); + }); + } + } else { + store.setState((prev) => ({ + ...prev, + workspaceGroup: { + name: groupName, + cleanup: workspaceGroupCleanup, + }, + })); + getWorkspaceGroupStore(groupName, state); + onWorkspaceGroupLaunch?.(); + if (workspaceToLaunch) { + launchWorkspace(workspaceToLaunch.name, workspaceToLaunch.additionalProps ?? {}); + } + } +} + function promptBeforeLaunchingWorkspace( workspace: OpenWorkspace, newWorkspaceDetails: { name: string; additionalProps?: object }, @@ -128,13 +240,13 @@ function promptBeforeLaunchingWorkspace( const newWorkspaceRegistration = getWorkspaceRegistration(name); const proceed = () => { - closeWorkspaceInternal(workspace.name, { + closeWorkspace(workspace.name, { ignoreChanges: true, // Calling the launchWorkspace again, since one of the `if` case // might resolve, but we need to check all the cases before launching the form. onWorkspaceClose: () => launchWorkspace(name, additionalProps), - // If the new workspace is of the same sidebar family, then we don't need to clear the workspace family store. - clearWorkspaceFamilyStore: newWorkspaceRegistration.sidebarFamily !== workspace.sidebarFamily, + // If the new workspace is of the same sidebar group, then we don't need to clear the workspace group store. + closeWorkspaceGroup: false, }); }; @@ -179,6 +291,15 @@ export function launchWorkspace< >(name: string, additionalProps?: Omit & { workspaceTitle?: string }) { const store = getWorkspaceStore(); const workspace = getWorkspaceRegistration(name); + const currentWorkspaceGroup = store.getState().workspaceGroup; + + if (currentWorkspaceGroup && !workspace.groups?.includes(currentWorkspaceGroup?.name)) { + closeWorkspaceGroup(currentWorkspaceGroup.name, () => { + launchWorkspace(name, additionalProps); + }); + return; + } + const currentGroupName = store.getState().workspaceGroup?.name; const newWorkspace: OpenWorkspace = { ...workspace, title: getWorkspaceTitle(workspace, additionalProps), @@ -197,14 +318,13 @@ export function launchWorkspace< }; }); }, + currentWorkspaceGroup: currentGroupName, additionalProps: additionalProps ?? {}, }; - if (newWorkspace.sidebarFamily) { - // initialize workspace family store - getWorkspaceFamilyStore(newWorkspace.sidebarFamily, additionalProps); + if (currentGroupName) { + getWorkspaceGroupStore(currentGroupName, additionalProps); } - function updateStoreWithNewWorkspace(workspaceToBeAdded: OpenWorkspace, restOfTheWorkspaces?: Array) { store.setState((state) => { const openWorkspaces = [workspaceToBeAdded, ...(restOfTheWorkspaces ?? state.openWorkspaces)]; @@ -294,6 +414,7 @@ export function cancelPrompt() { const defaultOptions: CloseWorkspaceOptions = { ignoreChanges: false, onWorkspaceClose: () => {}, + closeWorkspaceGroup: true, }; /** @@ -301,39 +422,29 @@ const defaultOptions: CloseWorkspaceOptions = { * @param name Workspace registration name * @param options Options to close workspace */ -export function closeWorkspace(name: string, options: CloseWorkspaceOptions = {}) { - return closeWorkspaceInternal(name, { ...options, clearWorkspaceFamilyStore: true }); -} - -function closeWorkspaceInternal(name: string, options: CloseWorkspaceInternalOptions = {}): boolean { +export function closeWorkspace(name: string, options: CloseWorkspaceOptions = {}): boolean { options = { ...defaultOptions, ...options }; const store = getWorkspaceStore(); const updateStoreWithClosedWorkspace = () => { const state = store.getState(); const workspaceToBeClosed = state.openWorkspaces.find((w) => w.name === name); - const workspaceSidebarFamilyName = workspaceToBeClosed?.sidebarFamily; const newOpenWorkspaces = state.openWorkspaces.filter((w) => w.name != name); - const workspaceFamilyStore = getWorkspaceFamilyStore(workspaceSidebarFamilyName); - if ( - options.clearWorkspaceFamilyStore && - workspaceFamilyStore && - !newOpenWorkspaces.some((w) => w.sidebarFamily === workspaceSidebarFamilyName) - ) { - // Clearing the workspace family store if there are no more workspaces with the same sidebar family name and the new workspace is not of the same sidebar family, which is handled in the `launchWorkspace` function. - workspaceFamilyStore.setState({}, true); - const unsubscribe = workspaceFamilyStore.subscribe(() => {}); - unsubscribe?.(); + + const workspaceGroupName = store.getState().workspaceGroup?.name; + + if (workspaceGroupName && options.closeWorkspaceGroup) { + closeWorkspaceGroup(workspaceGroupName); } // ensure closed workspace will not prompt promptBeforeClosing(name, () => false); - store.setState({ - ...state, + store.setState((prev) => ({ + ...prev, prompt: null, openWorkspaces: newOpenWorkspaces, workspaceWindowState: getUpdatedWorkspaceWindowState(newOpenWorkspaces?.[0]), - }); + })); options?.onWorkspaceClose?.(); }; @@ -371,6 +482,7 @@ const initialState: WorkspaceStoreState = { openWorkspaces: [], prompt: null, workspaceWindowState: 'normal', + workspaceGroup: undefined, }; export const workspaceStore = createGlobalStore('workspace', initialState); @@ -388,13 +500,18 @@ export function updateWorkspaceWindowState(value: WorkspaceWindowState) { function getUpdatedWorkspaceWindowState(workspaceAtTop: OpenWorkspace) { return workspaceAtTop?.preferredWindowSize ?? 'normal'; } -export function closeAllWorkspaces(onClosingWorkspaces: () => void = () => {}) { +export function closeAllWorkspaces( + onClosingWorkspaces: () => void = () => {}, + filter: (workspace: OpenWorkspace) => boolean = () => true, +) { const store = getWorkspaceStore(); - const canCloseAllWorkspaces = store.getState().openWorkspaces.every(({ name }) => { - const canCloseWorkspace = canCloseWorkspaceWithoutPrompting(name); - return canCloseWorkspace; - }); + const canCloseAllWorkspaces = store + .getState() + .openWorkspaces.filter(filter) + .every(({ name }) => { + return canCloseWorkspaceWithoutPrompting(name); + }); const updateWorkspaceStore = () => { resetWorkspaceStore(); @@ -413,21 +530,20 @@ export interface WorkspacesInfo { prompt: Prompt | null; workspaceWindowState: WorkspaceWindowState; workspaces: Array; + workspaceGroup?: WorkspaceStoreState['workspaceGroup']; } export function useWorkspaces(): WorkspacesInfo { - const { workspaceWindowState, openWorkspaces, prompt } = useStore(workspaceStore); - - const memoisedResults = useMemo( - () => ({ + const { workspaceWindowState, openWorkspaces, prompt, workspaceGroup } = useStore(workspaceStore); + const memoisedResults: WorkspacesInfo = useMemo(() => { + return { active: openWorkspaces.length > 0, prompt, workspaceWindowState, workspaces: openWorkspaces, - }), - [openWorkspaces, workspaceWindowState, prompt], - ); - + workspaceGroup, + }; + }, [openWorkspaces, workspaceWindowState, prompt, workspaceGroup]); return memoisedResults; } @@ -527,23 +643,23 @@ export function resetWorkspaceStore() { } /** - * The workspace family store is a store that is specific to the workspace sidebar family. + * The workspace group store is a store that is specific to the workspace group. * If the workspace has its own sidebar, the store will be created. - * This store can be used to store data that is specific to the workspace sidebar family. - * The store will be same for all the workspaces with same sidebar family name. + * This store can be used to store data that is specific to the workspace group. + * The store will be same for all the workspaces with same group name. * - * For workspaces with no sidebarFamilyName or sidebarFamilyName as 'default', the store will be undefined. + * For workspaces launched without a group, the store will be undefined. * - * The store will be cleared when all the workspaces with the store's sidebarFamilyName are closed. + * The store will be cleared when all the workspaces with the store's group name are closed. */ -export function getWorkspaceFamilyStore( - sidebarFamilyName: string | undefined, +export function getWorkspaceGroupStore( + workspaceGroupName: string | undefined, additionalProps: object = {}, ): StoreApi | undefined { - if (!sidebarFamilyName || sidebarFamilyName === 'default') { + if (!workspaceGroupName || workspaceGroupName === 'default') { return undefined; } - const store = getGlobalStore(sidebarFamilyName, {}); + const store = getGlobalStore(workspaceGroupName, {}); if (additionalProps) { store.setState((prev) => ({ ...prev,