@openmrs/esm-styleguide 10.0.1-pre.5704 → 10.0.1-pre.5707

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/public.d.ts +1 -1
  3. package/dist/public.d.ts.map +1 -1
  4. package/dist/public.js +1 -1
  5. package/dist/workspaces2/active-workspace-window.component.d.ts +11 -1
  6. package/dist/workspaces2/active-workspace-window.component.d.ts.map +1 -1
  7. package/dist/workspaces2/active-workspace-window.component.js +182 -71
  8. package/dist/workspaces2/exported-workspace.component.d.ts +42 -0
  9. package/dist/workspaces2/exported-workspace.component.d.ts.map +1 -0
  10. package/dist/workspaces2/exported-workspace.component.js +110 -0
  11. package/dist/workspaces2/exported-workspace.module.scss +8 -0
  12. package/dist/workspaces2/index.d.ts +1 -0
  13. package/dist/workspaces2/index.d.ts.map +1 -1
  14. package/dist/workspaces2/index.js +1 -0
  15. package/dist/workspaces2/workspace-window-actions.d.ts +36 -0
  16. package/dist/workspaces2/workspace-window-actions.d.ts.map +1 -0
  17. package/dist/workspaces2/workspace-window-actions.js +69 -0
  18. package/dist/workspaces2/workspace-windows-and-menu.component.d.ts.map +1 -1
  19. package/dist/workspaces2/workspace-windows-and-menu.component.js +19 -2
  20. package/dist/workspaces2/workspace2.component.d.ts +10 -0
  21. package/dist/workspaces2/workspace2.component.d.ts.map +1 -1
  22. package/dist/workspaces2/workspace2.component.js +12 -140
  23. package/dist/workspaces2/workspace2.d.ts +39 -13
  24. package/dist/workspaces2/workspace2.d.ts.map +1 -1
  25. package/dist/workspaces2/workspace2.js +173 -100
  26. package/package.json +25 -25
  27. package/src/public.ts +3 -0
  28. package/src/workspaces2/active-workspace-window.component.tsx +241 -66
  29. package/src/workspaces2/active-workspace-window.test.tsx +160 -34
  30. package/src/workspaces2/exported-workspace.component.tsx +140 -0
  31. package/src/workspaces2/exported-workspace.module.scss +8 -0
  32. package/src/workspaces2/exported-workspace.test.tsx +363 -0
  33. package/src/workspaces2/index.tsx +1 -0
  34. package/src/workspaces2/workspace-window-actions.test.ts +93 -0
  35. package/src/workspaces2/workspace-window-actions.ts +105 -0
  36. package/src/workspaces2/workspace-windows-and-menu.component.tsx +29 -2
  37. package/src/workspaces2/workspace2.component.test.tsx +72 -0
  38. package/src/workspaces2/workspace2.component.tsx +24 -150
  39. package/src/workspaces2/workspace2.test.ts +171 -106
  40. package/src/workspaces2/workspace2.ts +215 -109
@@ -1,3 +1,3 @@
1
- [0] Successfully compiled: 115 files, copied 169 files with swc (278.14ms)
1
+ [0] Successfully compiled: 117 files, copied 170 files with swc (252.19ms)
2
2
  [0] swc --strip-leading-paths --copy-files src -d dist && svgo -rf dist --quiet exited with code 0
3
3
  [1] tsc --project tsconfig.build.json exited with code 0
package/dist/public.d.ts CHANGED
@@ -23,5 +23,5 @@ export * from './pictograms/pictograms';
23
23
  export * from './responsive-wrapper';
24
24
  export { showSnackbar, type SnackbarDescriptor, type SnackbarType, type SnackbarMeta } from './snackbars';
25
25
  export { showToast, type ToastDescriptor, type ToastType, type ToastNotificationMeta } from './toasts';
26
- export { launchWorkspace2, launchWorkspaceGroup2, closeWorkspaceGroup2, getRegisteredWorkspace2Names, useWorkspace2Context, ActionMenuButton2, Workspace2, type Workspace2Definition, type Workspace2DefinitionProps, } from './workspaces2';
26
+ export { launchWorkspace2, launchWorkspaceGroup2, closeWorkspaceGroup2, getRegisteredWorkspace2Names, useWorkspace2Context, ActionMenuButton2, Workspace2, type Workspace2Definition, type Workspace2DefinitionProps, ExportedWorkspace, type ExportedWorkspaceProps, type ExportedWorkspaceWindowInfo, } from './workspaces2';
27
27
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,cAAc,SAAS,CAAC;AACxB,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EACL,KAAK,gCAAgC,EACrC,KAAK,0BAA0B,GAChC,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAClH,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1G,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACvG,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,GAC/B,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,cAAc,SAAS,CAAC;AACxB,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EACL,KAAK,gCAAgC,EACrC,KAAK,0BAA0B,GAChC,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAClH,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1G,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACvG,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,GACjC,MAAM,eAAe,CAAC"}
package/dist/public.js CHANGED
@@ -20,4 +20,4 @@ export * from "./pictograms/pictograms.js";
20
20
  export * from "./responsive-wrapper/index.js";
21
21
  export { showSnackbar } from "./snackbars/index.js";
22
22
  export { showToast } from "./toasts/index.js";
23
- export { launchWorkspace2, launchWorkspaceGroup2, closeWorkspaceGroup2, getRegisteredWorkspace2Names, useWorkspace2Context, ActionMenuButton2, Workspace2 } from "./workspaces2/index.js";
23
+ export { launchWorkspace2, launchWorkspaceGroup2, closeWorkspaceGroup2, getRegisteredWorkspace2Names, useWorkspace2Context, ActionMenuButton2, Workspace2, ExportedWorkspace } from "./workspaces2/index.js";
@@ -1,11 +1,21 @@
1
1
  import React from 'react';
2
2
  import { type OpenedWindow } from '@openmrs/esm-extensions';
3
+ import { type WorkspaceWindowActions } from './workspace-window-actions';
3
4
  interface WorkspaceWindowProps {
4
5
  openedWindow: OpenedWindow;
6
+ groupProps: Record<string, any> | null;
7
+ actions: WorkspaceWindowActions;
8
+ /**
9
+ * Whether to render the workspace window "chrome" (containers, title bar, maximize / hide / close).
10
+ * Only the global workspace window system renders it; an `<ExportedWorkspace>` renders the bare
11
+ * workspace content.
12
+ */
13
+ renderChrome: boolean;
5
14
  showActionMenu: boolean;
6
15
  }
7
16
  /**
8
- * Renders an opened workspace window.
17
+ * Renders an opened workspace window. `actions` determines which store the window's workspaces
18
+ * navigate within: the global workspace window system, or an `<ExportedWorkspace>`'s local store.
9
19
  */
10
20
  declare const ActiveWorkspaceWindow: React.FC<WorkspaceWindowProps>;
11
21
  export default ActiveWorkspaceWindow;
@@ -1 +1 @@
1
- {"version":3,"file":"active-workspace-window.component.d.ts","sourceRoot":"","sources":["../../src/workspaces2/active-workspace-window.component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAK5D,OAAO,EAAE,KAAK,YAAY,EAA8D,MAAM,yBAAyB,CAAC;AAOxH,UAAU,oBAAoB;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;CACzB;AACD;;GAEG;AACH,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA4CzD,CAAC;AA4IF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"active-workspace-window.component.d.ts","sourceRoot":"","sources":["../../src/workspaces2/active-workspace-window.component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAM5E,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,yBAAyB,CAAC;AAOjC,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAGzE,UAAU,oBAAoB;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACvC,OAAO,EAAE,sBAAsB,CAAC;IAChC;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB;AAKD;;;GAGG;AACH,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA4DzD,CAAC;AAoRF,eAAe,qBAAqB,CAAC"}
@@ -2,15 +2,21 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useEffect, useMemo, useState } from "react";
3
3
  import classNames from "classnames";
4
4
  import Parcel from "single-spa-react/parcel";
5
- import { InlineLoading } from "@carbon/react";
5
+ import { Header, HeaderGlobalAction, HeaderGlobalBar, HeaderName, InlineLoading } from "@carbon/react";
6
+ import { DownToBottom, Maximize, Minimize } from "@carbon/react/icons";
6
7
  import { createParcelMounter, workspace2Store } from "@openmrs/esm-extensions";
8
+ import { isDesktop, useLayoutType, useStore } from "@openmrs/esm-react-utils";
7
9
  import { loadLifeCycles } from "@openmrs/esm-routes";
8
10
  import { getCoreTranslation } from "@openmrs/esm-translations";
9
- import { promptForClosingWorkspaces, useWorkspace2Store } from "./workspace2.js";
11
+ import { ArrowRightIcon, CloseIcon } from "../icons/index.js";
12
+ import { closeWorkspaceGroup2, useWorkspace2Store } from "./workspace2.js";
10
13
  import styles from "./workspace2.module.scss";
14
+ // module-level so the store subscription's snapshot getter stays stable across renders
15
+ const selectRegisteredWorkspacesByName = (state)=>state.registeredWorkspacesByName;
11
16
  /**
12
- * Renders an opened workspace window.
13
- */ const ActiveWorkspaceWindow = ({ openedWindow, showActionMenu })=>{
17
+ * Renders an opened workspace window. `actions` determines which store the window's workspaces
18
+ * navigate within: the global workspace window system, or an `<ExportedWorkspace>`'s local store.
19
+ */ const ActiveWorkspaceWindow = ({ openedWindow, groupProps, actions, renderChrome, showActionMenu })=>{
14
20
  const { openedWorkspaces } = openedWindow;
15
21
  // Keyed by opened workspace uuid rather than stack index. The lifecycles load
16
22
  // asynchronously, so when the stack changes (a workspace at some position is
@@ -20,30 +26,39 @@ import styles from "./workspace2.module.scss";
20
26
  // mounts whatever config it first receives, so the wrong component would then
21
27
  // render with the new workspace's props.
22
28
  const [lifeCyclesByUuid, setLifeCyclesByUuid] = useState({});
23
- const { registeredWorkspacesByName } = workspace2Store.getState();
29
+ // Reactive so the effect retries when a workspace registers after mount.
30
+ const registeredWorkspacesByName = useStore(workspace2Store, selectRegisteredWorkspacesByName);
24
31
  useEffect(()=>{
25
32
  let cancelled = false;
26
33
  Promise.all(openedWorkspaces.map(async (openedWorkspace)=>{
27
- const { moduleName, component } = registeredWorkspacesByName[openedWorkspace.workspaceName];
34
+ const workspaceDef = registeredWorkspacesByName[openedWorkspace.workspaceName];
35
+ if (!workspaceDef) {
36
+ return null;
37
+ }
38
+ const { moduleName, component } = workspaceDef;
28
39
  return [
29
40
  openedWorkspace.uuid,
30
41
  await loadLifeCycles(moduleName, component)
31
42
  ];
32
43
  })).then((entries)=>{
33
44
  if (!cancelled) {
34
- setLifeCyclesByUuid(Object.fromEntries(entries));
45
+ setLifeCyclesByUuid(Object.fromEntries(entries.filter((entry)=>entry != null)));
35
46
  }
36
47
  });
37
48
  return ()=>{
38
49
  cancelled = true;
39
50
  };
40
51
  }, [
41
- openedWorkspaces
52
+ openedWorkspaces,
53
+ registeredWorkspacesByName
42
54
  ]);
43
55
  return /*#__PURE__*/ _jsx(_Fragment, {
44
56
  children: openedWorkspaces.map((openedWorkspace, i)=>/*#__PURE__*/ _jsx(ActiveWorkspace, {
45
57
  openedWorkspace: openedWorkspace,
46
58
  openedWindow: openedWindow,
59
+ groupProps: groupProps,
60
+ actions: actions,
61
+ renderChrome: renderChrome,
47
62
  lifeCycle: lifeCyclesByUuid[openedWorkspace.uuid],
48
63
  isRootWorkspace: i === 0,
49
64
  isLeafWorkspace: i === openedWorkspaces.length - 1,
@@ -52,31 +67,21 @@ import styles from "./workspace2.module.scss";
52
67
  });
53
68
  };
54
69
  const mountParcel = createParcelMounter();
55
- const ActiveWorkspace = ({ lifeCycle, openedWorkspace, openedWindow, isRootWorkspace, isLeafWorkspace, showActionMenu })=>{
56
- const { openedGroup, closeWorkspace, openChildWorkspace } = useWorkspace2Store();
70
+ const ActiveWorkspace = ({ lifeCycle, openedWorkspace, openedWindow, groupProps, actions, renderChrome, isRootWorkspace, isLeafWorkspace, showActionMenu })=>{
57
71
  const props = useMemo(()=>openedWorkspace && {
58
72
  closeWorkspace: async (options = {})=>{
59
73
  const { closeWindow = false, discardUnsavedChanges = false } = options;
60
74
  if (closeWindow) {
61
- const okToCloseWorkspaces = discardUnsavedChanges || await promptForClosingWorkspaces({
62
- reason: 'CLOSE_WINDOW',
63
- explicit: true,
64
- windowName: openedWindow.windowName
65
- });
75
+ const okToCloseWorkspaces = discardUnsavedChanges || await actions.promptForClosingWorkspaces();
66
76
  if (okToCloseWorkspaces) {
67
- closeWorkspace(openedWindow.openedWorkspaces[0].workspaceName);
77
+ actions.closeWorkspace(openedWindow.openedWorkspaces[0].workspaceName);
68
78
  return true;
69
79
  }
70
80
  return false;
71
81
  } else {
72
- const okToCloseWorkspaces = discardUnsavedChanges || await promptForClosingWorkspaces({
73
- reason: 'CLOSE_WORKSPACE',
74
- explicit: true,
75
- windowName: openedWindow.windowName,
76
- workspaceName: openedWorkspace.workspaceName
77
- });
82
+ const okToCloseWorkspaces = discardUnsavedChanges || await actions.promptForClosingWorkspaces(openedWorkspace.workspaceName);
78
83
  if (okToCloseWorkspaces) {
79
- closeWorkspace(openedWorkspace.workspaceName);
84
+ actions.closeWorkspace(openedWorkspace.workspaceName);
80
85
  return true;
81
86
  }
82
87
  return false;
@@ -95,23 +100,20 @@ const ActiveWorkspace = ({ lifeCycle, openedWorkspace, openedWindow, isRootWorks
95
100
  // Prompt if any of them have unsaved changes.
96
101
  const workspacesAboveParent = openedWorkspaces.slice(parentIndex + 1);
97
102
  if (workspacesAboveParent.some((w)=>w.hasUnsavedChanges)) {
98
- const okToClose = await promptForClosingWorkspaces({
99
- reason: 'CLOSE_WORKSPACE',
100
- explicit: true,
101
- windowName: openedWindow.windowName,
102
- workspaceName: openedWorkspaces[parentIndex + 1].workspaceName
103
- });
103
+ const okToClose = await actions.promptForClosingWorkspaces(openedWorkspaces[parentIndex + 1].workspaceName);
104
104
  if (!okToClose) {
105
105
  return;
106
106
  }
107
107
  }
108
108
  }
109
- openChildWorkspace(parentWorkspaceName, childWorkspaceName, childWorkspaceProps || {});
109
+ actions.openChildWorkspace(parentWorkspaceName, childWorkspaceName, childWorkspaceProps || {});
110
110
  },
111
+ setWorkspaceTitle: (title)=>actions.setWorkspaceTitle(openedWorkspace.uuid, title),
112
+ setHasUnsavedChanges: (hasUnsavedChanges)=>actions.setHasUnsavedChanges(openedWorkspace.uuid, hasUnsavedChanges),
111
113
  workspaceName: openedWorkspace.workspaceName,
112
114
  workspaceProps: openedWorkspace.props,
113
115
  windowProps: openedWindow.props,
114
- groupProps: openedGroup && openedGroup.props ? openedGroup.props : null,
116
+ groupProps,
115
117
  workspaceMeta: workspace2Store.getState().registeredWorkspacesByName[openedWorkspace.workspaceName]?.meta ?? {},
116
118
  isRootWorkspace,
117
119
  isLeafWorkspace,
@@ -119,51 +121,160 @@ const ActiveWorkspace = ({ lifeCycle, openedWorkspace, openedWindow, isRootWorks
119
121
  showActionMenu
120
122
  }, [
121
123
  openedWorkspace,
122
- closeWorkspace,
123
- openedGroup,
124
- openedWindow
124
+ openedWindow,
125
+ groupProps,
126
+ actions,
127
+ isRootWorkspace,
128
+ isLeafWorkspace,
129
+ showActionMenu
130
+ ]);
131
+ // In bare mode (<ExportedWorkspace>) there is no chrome to stack workspaces, so every non-leaf
132
+ // workspace is hidden (not unmounted, so it keeps its state) and the leaf covers them. In chrome
133
+ // mode the absolute-positioned WorkspaceChrome containers handle stacking, so this class is not
134
+ // applied there.
135
+ const hideBehindLeaf = !renderChrome && !isLeafWorkspace;
136
+ const content = useMemo(()=>lifeCycle ? /*#__PURE__*/ _jsx(Parcel, {
137
+ config: lifeCycle,
138
+ mountParcel: mountParcel,
139
+ wrapWith: "div",
140
+ wrapClassName: classNames(styles.workspaceContent, {
141
+ [styles.hiddenExtraWorkspace]: hideBehindLeaf
142
+ }),
143
+ ...props
144
+ }, openedWorkspace.workspaceName) : null, [
145
+ lifeCycle,
146
+ openedWorkspace.workspaceName,
147
+ props,
148
+ hideBehindLeaf
125
149
  ]);
126
- if (!lifeCycle) {
127
- const { registeredWorkspacesByName } = workspace2Store.getState();
128
- const workspaceDef = registeredWorkspacesByName[openedWorkspace.workspaceName];
129
- const windowName = workspaceDef && workspaceDef.window ? workspaceDef.window : undefined;
130
- const { registeredWindowsByName } = workspace2Store.getState();
131
- const windowDef = windowName ? registeredWindowsByName[windowName] : undefined;
132
- const width = windowDef && windowDef.width ? windowDef.width : 'narrow';
133
- const isActionMenuOpened = Object.values(registeredWindowsByName).some((window)=>window.group === openedGroup?.groupName && window.icon !== undefined);
134
- return /*#__PURE__*/ _jsxs("div", {
135
- className: classNames(styles.workspaceOuterContainer, {
136
- [styles.narrowWorkspace]: width === 'narrow',
137
- [styles.widerWorkspace]: width === 'wider',
138
- [styles.extraWideWorkspace]: width === 'extra-wide',
139
- [styles.isActionMenuOpened]: isActionMenuOpened
150
+ if (!renderChrome) {
151
+ return content ?? /*#__PURE__*/ _jsx(InlineLoading, {
152
+ className: classNames(styles.loader, {
153
+ [styles.hiddenExtraWorkspace]: hideBehindLeaf
154
+ }),
155
+ description: `${getCoreTranslation('loading')}`
156
+ });
157
+ }
158
+ return /*#__PURE__*/ _jsx(WorkspaceChrome, {
159
+ openedWorkspace: openedWorkspace,
160
+ openedWindow: openedWindow,
161
+ isRootWorkspace: isRootWorkspace,
162
+ showActionMenu: showActionMenu,
163
+ closeWindow: ()=>props.closeWorkspace({
164
+ closeWindow: true
165
+ }),
166
+ children: content
167
+ });
168
+ };
169
+ /**
170
+ * Renders the containers, title bar and window buttons around a workspace in the global workspace
171
+ * window system.
172
+ */ const WorkspaceChrome = ({ openedWorkspace, openedWindow, isRootWorkspace, showActionMenu, closeWindow, children })=>{
173
+ const layout = useLayoutType();
174
+ const { openedGroup, openedWindows, registeredWindowsByName, registeredGroupsByName, isMostRecentlyOpenedWindowHidden, setWindowMaximized, hideWindow } = useWorkspace2Store();
175
+ const windowName = openedWindow.windowName;
176
+ const { maximized } = openedWindow;
177
+ const title = openedWorkspace.title ?? '';
178
+ const windowDef = registeredWindowsByName[windowName];
179
+ const width = windowDef?.width ?? 'narrow';
180
+ const icon = windowDef?.icon;
181
+ const canMaximize = windowDef?.canMaximize;
182
+ const canCloseGroup = openedGroup ? registeredGroupsByName[openedGroup.groupName]?.persistence === 'closable' : false;
183
+ const canHide = !!icon && !canCloseGroup;
184
+ const openedWindowIndex = openedWindows.findIndex((w)=>w.windowName === windowName);
185
+ const isWindowHidden = openedWindowIndex < openedWindows.length - 1 || isMostRecentlyOpenedWindowHidden;
186
+ const isActionMenuOpened = Object.values(registeredWindowsByName).some((window)=>window.group === openedGroup?.groupName && window.icon !== undefined);
187
+ return /*#__PURE__*/ _jsxs("div", {
188
+ className: classNames(styles.workspaceOuterContainer, {
189
+ [styles.narrowWorkspace]: width === 'narrow',
190
+ [styles.widerWorkspace]: width === 'wider',
191
+ [styles.extraWideWorkspace]: width === 'extra-wide',
192
+ [styles.isActionMenuOpened]: isActionMenuOpened
193
+ }),
194
+ children: [
195
+ /*#__PURE__*/ _jsx("div", {
196
+ className: classNames(styles.workspaceSpacer, {
197
+ [styles.hidden]: isWindowHidden
198
+ })
140
199
  }),
141
- children: [
142
- /*#__PURE__*/ _jsx("div", {
143
- className: styles.workspaceSpacer
200
+ /*#__PURE__*/ _jsx("div", {
201
+ className: classNames(styles.workspaceMiddleContainer, {
202
+ [styles.maximized]: maximized,
203
+ [styles.hidden]: isWindowHidden,
204
+ [styles.isRootWorkspace]: isRootWorkspace,
205
+ [styles.showActionMenu]: showActionMenu
144
206
  }),
145
- /*#__PURE__*/ _jsx("div", {
146
- className: classNames(styles.workspaceMiddleContainer, {
147
- [styles.isRootWorkspace]: isRootWorkspace,
148
- [styles.showActionMenu]: showActionMenu
207
+ children: /*#__PURE__*/ _jsx("div", {
208
+ className: classNames(styles.workspaceInnerContainer, {
209
+ [styles.maximized]: maximized,
210
+ [styles.hidden]: isWindowHidden,
211
+ [styles.isRootWorkspace]: isRootWorkspace
149
212
  }),
150
- children: /*#__PURE__*/ _jsx("div", {
151
- className: classNames(styles.workspaceInnerContainer, {
152
- [styles.isRootWorkspace]: isRootWorkspace
153
- }),
154
- children: /*#__PURE__*/ _jsx(InlineLoading, {
155
- className: styles.loader,
156
- description: `${getCoreTranslation('loading')} ...`
157
- })
213
+ children: children ? /*#__PURE__*/ _jsxs(_Fragment, {
214
+ children: [
215
+ /*#__PURE__*/ _jsxs(Header, {
216
+ "aria-label": getCoreTranslation('workspaceHeader'),
217
+ className: styles.header,
218
+ children: [
219
+ /*#__PURE__*/ _jsx(HeaderName, {
220
+ prefix: "",
221
+ children: title
222
+ }),
223
+ /*#__PURE__*/ _jsx("div", {
224
+ className: styles.overlayHeaderSpacer
225
+ }),
226
+ /*#__PURE__*/ _jsx(HeaderGlobalBar, {
227
+ className: styles.headerButtons,
228
+ children: isDesktop(layout) ? /*#__PURE__*/ _jsxs(_Fragment, {
229
+ children: [
230
+ (canMaximize || maximized) && /*#__PURE__*/ _jsx(HeaderGlobalAction, {
231
+ "aria-label": maximized ? getCoreTranslation('minimize') : getCoreTranslation('maximize'),
232
+ onClick: ()=>setWindowMaximized(windowName, !maximized),
233
+ children: maximized ? /*#__PURE__*/ _jsx(Minimize, {}) : /*#__PURE__*/ _jsx(Maximize, {})
234
+ }),
235
+ canHide && /*#__PURE__*/ _jsx(HeaderGlobalAction, {
236
+ "aria-label": getCoreTranslation('hide'),
237
+ onClick: ()=>hideWindow(),
238
+ children: /*#__PURE__*/ _jsx(ArrowRightIcon, {})
239
+ }),
240
+ !canCloseGroup && /*#__PURE__*/ _jsx(HeaderGlobalAction, {
241
+ "aria-label": getCoreTranslation('close'),
242
+ onClick: closeWindow,
243
+ children: /*#__PURE__*/ _jsx(CloseIcon, {})
244
+ })
245
+ ]
246
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
247
+ children: [
248
+ canHide && /*#__PURE__*/ _jsx(HeaderGlobalAction, {
249
+ "aria-label": getCoreTranslation('hide'),
250
+ onClick: ()=>hideWindow(),
251
+ children: /*#__PURE__*/ _jsx(DownToBottom, {})
252
+ }),
253
+ /*#__PURE__*/ _jsx(HeaderGlobalAction, {
254
+ "aria-label": getCoreTranslation('close'),
255
+ onClick: ()=>{
256
+ if (canCloseGroup) {
257
+ void closeWorkspaceGroup2();
258
+ } else {
259
+ closeWindow();
260
+ }
261
+ },
262
+ children: /*#__PURE__*/ _jsx(CloseIcon, {})
263
+ })
264
+ ]
265
+ })
266
+ })
267
+ ]
268
+ }),
269
+ children
270
+ ]
271
+ }) : /*#__PURE__*/ _jsx(InlineLoading, {
272
+ className: styles.loader,
273
+ description: `${getCoreTranslation('loading')} ...`
158
274
  })
159
275
  })
160
- ]
161
- });
162
- }
163
- return /*#__PURE__*/ _jsx(Parcel, {
164
- config: lifeCycle,
165
- mountParcel: mountParcel,
166
- ...props
167
- }, openedWorkspace.workspaceName);
276
+ })
277
+ ]
278
+ });
168
279
  };
169
280
  export default ActiveWorkspaceWindow;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import { type WorkspaceWindow2Width } from '@openmrs/esm-globals';
3
+ export interface ExportedWorkspaceWindowInfo {
4
+ /** The name of the currently-rendered (leaf) workspace, or `null` when all workspaces are closed. */
5
+ workspaceName: string | null;
6
+ /**
7
+ * The emulated workspace window's configured width. Note that this is a static property of the
8
+ * emulated workspace window, and does not change for the duration of the app.
9
+ */
10
+ windowWidth: WorkspaceWindow2Width;
11
+ /** The title of the currently-rendered (leaf) workspace, or `''` when all workspaces are closed. */
12
+ title: string;
13
+ /** The `hasUnsavedChanges` of every workspace in the window, OR'd together. */
14
+ hasUnsavedChanges: boolean;
15
+ }
16
+ export interface ExportedWorkspaceProps {
17
+ /**
18
+ * The name of the workspace to open. Changing it (like changing `workspaceProps`, `windowProps` or
19
+ * `groupProps`) discards the current workspaces, without prompting for unsaved changes, and opens
20
+ * `name` afresh. To force a fresh instance without changing any of these, change the React `key`.
21
+ */
22
+ name: string;
23
+ /**
24
+ * The props passed into the workspace. Changing these (compared shallowly), or `windowProps` /
25
+ * `groupProps`, discards the current workspaces and opens `name` afresh with the new props.
26
+ */
27
+ workspaceProps: Record<string, any>;
28
+ windowProps?: Record<string, any>;
29
+ groupProps?: Record<string, any>;
30
+ /** Callback fired when the emulated window's state changes. */
31
+ onWindowChanged?(info: ExportedWorkspaceWindowInfo): void;
32
+ }
33
+ /**
34
+ * Renders the content of a registered workspace (the `children` of its `<Workspace2>`) within its own
35
+ * DOM subtree, independent of the real global workspace window system. It emulates the workspace
36
+ * window that contains `name`: opening/closing child workspaces (via `launchChildWorkspace()` /
37
+ * `closeWorkspace()` from within the workspace) navigates within this component, in a window held in
38
+ * its own local store. It renders no chrome (no title bar, maximize, hide, close, or icon); callers can
39
+ * render their own around it.
40
+ */
41
+ export declare const ExportedWorkspace: React.FC<ExportedWorkspaceProps>;
42
+ //# sourceMappingURL=exported-workspace.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exported-workspace.component.d.ts","sourceRoot":"","sources":["../../src/workspaces2/exported-workspace.component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAE1D,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AASlE,MAAM,WAAW,2BAA2B;IAC1C,qGAAqG;IACrG,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,WAAW,EAAE,qBAAqB,CAAC;IACnC,oGAAoG;IACpG,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEjC,+DAA+D;IAC/D,eAAe,CAAC,CAAC,IAAI,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC3D;AAOD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAiF9D,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useEffect, useMemo, useRef } from "react";
3
+ import { workspace2Store } from "@openmrs/esm-extensions";
4
+ import { useStore } from "@openmrs/esm-react-utils";
5
+ import { createLocalStore } from "@openmrs/esm-state";
6
+ import { shallowEqual } from "@openmrs/esm-utils";
7
+ import ActiveWorkspaceWindow from "./active-workspace-window.component.js";
8
+ import { createExportedWorkspaceWindow } from "./workspace2.js";
9
+ import { createLocalWindowActions } from "./workspace-window-actions.js";
10
+ import styles from "./exported-workspace.module.scss";
11
+ // module-level so the store subscription's snapshot getter stays stable across renders
12
+ const selectRegisteredWorkspacesByName = (state)=>state.registeredWorkspacesByName;
13
+ /**
14
+ * Renders the content of a registered workspace (the `children` of its `<Workspace2>`) within its own
15
+ * DOM subtree, independent of the real global workspace window system. It emulates the workspace
16
+ * window that contains `name`: opening/closing child workspaces (via `launchChildWorkspace()` /
17
+ * `closeWorkspace()` from within the workspace) navigates within this component, in a window held in
18
+ * its own local store. It renders no chrome (no title bar, maximize, hide, close, or icon); callers can
19
+ * render their own around it.
20
+ */ export const ExportedWorkspace = ({ name, workspaceProps, windowProps, groupProps, onWindowChanged })=>{
21
+ // Reactive so the seed effect below retries when `name` registers after this component mounts.
22
+ const registeredWorkspacesByName = useStore(workspace2Store, selectRegisteredWorkspacesByName);
23
+ const windowDef = useStore(workspace2Store, (state)=>{
24
+ const windowName = state.registeredWorkspacesByName[name]?.window;
25
+ return windowName ? state.registeredWindowsByName[windowName] : undefined;
26
+ });
27
+ const seedInputs = {
28
+ name,
29
+ workspaceProps,
30
+ windowProps,
31
+ groupProps
32
+ };
33
+ const storeRef = useRef();
34
+ // The inputs this instance has already seeded, so we re-seed when the caller changes any of them,
35
+ // but do NOT clobber the user's own navigation/close of the current one.
36
+ const seededInputsRef = useRef(null);
37
+ if (!storeRef.current) {
38
+ const openedWindow = createExportedWorkspaceWindow(name, workspaceProps, windowProps);
39
+ storeRef.current = createLocalStore({
40
+ openedWindow,
41
+ groupProps: groupProps ?? null
42
+ });
43
+ if (openedWindow) {
44
+ seededInputsRef.current = seedInputs;
45
+ }
46
+ }
47
+ const store = storeRef.current;
48
+ useEffect(()=>{
49
+ const seeded = seededInputsRef.current;
50
+ // Props are compared shallowly so that callers passing inline object literals do not re-seed on
51
+ // every render.
52
+ if (seeded && seeded.name === name && shallowEqual(seeded.workspaceProps, workspaceProps) && shallowEqual(seeded.windowProps, windowProps) && shallowEqual(seeded.groupProps, groupProps)) {
53
+ // Already seeded these inputs; leave the user's navigation/close alone.
54
+ return;
55
+ }
56
+ // When `name` is not registered yet, this shows nothing and leaves the inputs unmarked, so we seed
57
+ // once it registers. Changes to props resets the state (like navigation) and bypassed the unsaved-changes prompt
58
+ const openedWindow = createExportedWorkspaceWindow(name, workspaceProps, windowProps);
59
+ store.setState({
60
+ openedWindow,
61
+ groupProps: groupProps ?? null
62
+ }, true);
63
+ // Clear the inputs when nothing was seeded, so switching back to previously seeded inputs re-seeds.
64
+ seededInputsRef.current = openedWindow ? seedInputs : null;
65
+ }, [
66
+ name,
67
+ workspaceProps,
68
+ windowProps,
69
+ groupProps,
70
+ registeredWorkspacesByName,
71
+ store
72
+ ]);
73
+ const actions = useMemo(()=>createLocalWindowActions(store), [
74
+ store
75
+ ]);
76
+ const { openedWindow, groupProps: openedGroupProps } = useStore(store);
77
+ const leaf = openedWindow?.openedWorkspaces[openedWindow.openedWorkspaces.length - 1];
78
+ const workspaceName = leaf?.workspaceName ?? null;
79
+ const title = leaf?.title ?? '';
80
+ const hasUnsavedChanges = openedWindow?.openedWorkspaces.some((w)=>w.hasUnsavedChanges) ?? false;
81
+ const windowWidth = windowDef?.width ?? 'narrow';
82
+ // Hold the callback in a ref so an inline-arrow identity does not retrigger the notify effect.
83
+ const onWindowChangedRef = useRef(onWindowChanged);
84
+ useEffect(()=>{
85
+ onWindowChangedRef.current = onWindowChanged;
86
+ });
87
+ useEffect(()=>{
88
+ onWindowChangedRef.current?.({
89
+ workspaceName,
90
+ windowWidth,
91
+ title,
92
+ hasUnsavedChanges
93
+ });
94
+ }, [
95
+ workspaceName,
96
+ windowWidth,
97
+ title,
98
+ hasUnsavedChanges
99
+ ]);
100
+ return /*#__PURE__*/ _jsx("div", {
101
+ className: styles.exportedWorkspace,
102
+ children: openedWindow ? /*#__PURE__*/ _jsx(ActiveWorkspaceWindow, {
103
+ openedWindow: openedWindow,
104
+ groupProps: openedGroupProps,
105
+ actions: actions,
106
+ renderChrome: false,
107
+ showActionMenu: false
108
+ }) : null
109
+ });
110
+ };
@@ -0,0 +1,8 @@
1
+ // The <ExportedWorkspace> renders only the workspace content (no chrome). It fills its parent as a
2
+ // flex column so that the workspace content (which uses flex-grow / height: 100%) is sized correctly.
3
+ // The caller is responsible for sizing the parent and rendering any title / close controls it wants.
4
+ .exportedWorkspace {
5
+ display: flex;
6
+ flex-direction: column;
7
+ height: 100%;
8
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './workspace2.component';
2
2
  export * from './workspace2';
3
+ export * from './exported-workspace.component';
3
4
  export * from './action-menu2/action-menu-button2.component';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspaces2/index.tsx"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,8CAA8C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspaces2/index.tsx"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8CAA8C,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export * from "./workspace2.component.js";
2
2
  export * from "./workspace2.js";
3
+ export * from "./exported-workspace.component.js";
3
4
  export * from "./action-menu2/action-menu-button2.component.js";
@@ -0,0 +1,36 @@
1
+ import { type OpenedWindow } from '@openmrs/esm-extensions';
2
+ import { type StoreApi } from '@openmrs/esm-state';
3
+ /**
4
+ * The operations the workspace render components (`ActiveWorkspaceWindow` / `ActiveWorkspace`) perform
5
+ * on the one workspace window they render. There is an implementation for a window in the global
6
+ * workspace window system, and one for the window held in an `<ExportedWorkspace>`'s local store.
7
+ * Each implementation reads the current window from its own store at call time.
8
+ */
9
+ export interface WorkspaceWindowActions {
10
+ /** Closes the named workspace, along with its children. */
11
+ closeWorkspace(workspaceName: string): void;
12
+ openChildWorkspace(parentWorkspaceName: string, childWorkspaceName: string, props: Record<string, any>): void;
13
+ setWorkspaceTitle(uuid: string, title: string): void;
14
+ setHasUnsavedChanges(uuid: string, hasUnsavedChanges: boolean): void;
15
+ /**
16
+ * Prompts for confirmation to close the workspaces with unsaved changes in the window: the whole
17
+ * window, or, if `upToWorkspaceName` is given, just that workspace and its children.
18
+ * @returns a Promise that resolves to true if the user confirmed, or there was nothing to confirm.
19
+ */
20
+ promptForClosingWorkspaces(upToWorkspaceName?: string): Promise<boolean>;
21
+ }
22
+ /** The state of an `<ExportedWorkspace>`'s local store. */
23
+ export interface ExportedWorkspaceState {
24
+ /** `null` before the workspace registers, and after its root workspace is closed. */
25
+ openedWindow: OpenedWindow | null;
26
+ groupProps: Record<string, any> | null;
27
+ }
28
+ /**
29
+ * @param windowName The name of a window in the global workspace window system.
30
+ */
31
+ export declare function createGlobalWindowActions(windowName: string): WorkspaceWindowActions;
32
+ /**
33
+ * @param store The local store of an `<ExportedWorkspace>`.
34
+ */
35
+ export declare function createLocalWindowActions(store: StoreApi<ExportedWorkspaceState>): WorkspaceWindowActions;
36
+ //# sourceMappingURL=workspace-window-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-window-actions.d.ts","sourceRoot":"","sources":["../../src/workspaces2/workspace-window-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAmB,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AASnD;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,kBAAkB,CAAC,mBAAmB,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAC9G,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IACrE;;;;OAIG;IACH,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E;AAED,2DAA2D;AAC3D,MAAM,WAAW,sBAAsB;IACrC,qFAAqF;IACrF,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,CAwBpF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,CAmCxG"}