@openmrs/esm-react-utils 3.1.15-pre.754 → 3.1.15-pre.767

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-react-utils",
3
- "version": "3.1.15-pre.754",
3
+ "version": "3.1.15-pre.767",
4
4
  "license": "MPL-2.0",
5
5
  "description": "React utilities for OpenMRS.",
6
6
  "browser": "dist/openmrs-esm-react-utils.js",
@@ -53,11 +53,11 @@
53
53
  "react-i18next": "11.x"
54
54
  },
55
55
  "devDependencies": {
56
- "@openmrs/esm-api": "^3.1.15-pre.754",
57
- "@openmrs/esm-config": "^3.1.15-pre.754",
58
- "@openmrs/esm-error-handling": "^3.1.15-pre.754",
59
- "@openmrs/esm-extensions": "^3.1.15-pre.754",
60
- "@openmrs/esm-globals": "^3.1.15-pre.754",
56
+ "@openmrs/esm-api": "^3.1.15-pre.767",
57
+ "@openmrs/esm-config": "^3.1.15-pre.767",
58
+ "@openmrs/esm-error-handling": "^3.1.15-pre.767",
59
+ "@openmrs/esm-extensions": "^3.1.15-pre.767",
60
+ "@openmrs/esm-globals": "^3.1.15-pre.767",
61
61
  "i18next": "^19.6.0",
62
62
  "react": "^16.13.1",
63
63
  "react-dom": "^16.13.1",
@@ -65,5 +65,5 @@
65
65
  "rxjs": "^6.5.3",
66
66
  "unistore": "^3.5.2"
67
67
  },
68
- "gitHead": "527e5cfe5c9478a382f03db49bd75f9be2c4b15d"
68
+ "gitHead": "c7b2c24b8fb56043e8165d95cdccd2c6ab34670b"
69
69
  }
package/src/Extension.tsx CHANGED
@@ -1,7 +1,6 @@
1
- import { renderExtension } from "@openmrs/esm-extensions";
2
- import React, { useCallback, useContext, useEffect, useState } from "react";
3
- import { ComponentContext } from ".";
1
+ import React from "react";
4
2
  import { ExtensionData } from "./ComponentContext";
3
+ import { useExtension } from "./useExtension";
5
4
 
6
5
  export interface ExtensionProps {
7
6
  state?: Record<string, any>;
@@ -21,31 +20,7 @@ export interface ExtensionProps {
21
20
  * and *must* only be used once within that `<ExtensionSlot>`.
22
21
  */
23
22
  export const Extension: React.FC<ExtensionProps> = ({ state, wrap }) => {
24
- const [domElement, setDomElement] = useState<HTMLDivElement>();
25
- const { extension } = useContext(ComponentContext);
26
-
27
- const ref = useCallback((node) => {
28
- setDomElement(node);
29
- }, []);
30
-
31
- useEffect(() => {
32
- if (domElement != null && extension) {
33
- return renderExtension(
34
- domElement,
35
- extension.extensionSlotName,
36
- extension.extensionSlotModuleName,
37
- extension.extensionId,
38
- undefined,
39
- state
40
- );
41
- }
42
- }, [
43
- extension?.extensionSlotName,
44
- extension?.extensionId,
45
- extension?.extensionSlotModuleName,
46
- state,
47
- domElement,
48
- ]);
23
+ const [ref, extension] = useExtension<HTMLDivElement>(state);
49
24
 
50
25
  // The extension is rendered into the `<slot>`. It is surrounded by a
51
26
  // `<div>` with relative positioning in order to allow the UI Editor
@@ -1,5 +1,5 @@
1
- import { ConnectedExtension } from "@openmrs/esm-extensions";
2
- import React, { useRef, useMemo, useContext } from "react";
1
+ import React, { useRef, useMemo } from "react";
2
+ import { ConnectedExtension } from "./useConnectedExtensions";
3
3
  import { ComponentContext } from "./ComponentContext";
4
4
  import { Extension } from "./Extension";
5
5
  import { useExtensionSlot } from "./useExtensionSlot";
@@ -72,7 +72,7 @@ export const ExtensionSlot: React.FC<ExtensionSlotProps> = ({
72
72
  <ComponentContext.Provider
73
73
  key={extension.id}
74
74
  value={{
75
- moduleName: extensionSlotModuleName, // moduleName is not used by the receiving Extension
75
+ moduleName: extension.moduleName,
76
76
  extension: {
77
77
  extensionId: extension.id,
78
78
  extensionSlotName,
package/src/index.ts CHANGED
@@ -5,14 +5,14 @@ export * from "./Extension";
5
5
  export * from "./ExtensionSlot";
6
6
  export * from "./getLifecycle";
7
7
  export * from "./openmrsComponentDecorator";
8
- export * from "./useAssignedExtensions";
9
8
  export * from "./useAssignedExtensionIds";
9
+ export * from "./useAttachedExtensionIds";
10
10
  export * from "./useBodyScrollLock";
11
11
  export * from "./useConfig";
12
12
  export * from "./useConnectedExtensions";
13
13
  export * from "./useConnectivity";
14
14
  export * from "./useCurrentPatient";
15
- export * from "./useExtensionInternalStore";
15
+ export * from "./useExtension";
16
16
  export * from "./useExtensionSlotConfig";
17
17
  export * from "./useExtensionSlot";
18
18
  export * from "./useExtensionSlotMeta";
@@ -1,26 +1,29 @@
1
1
  import { useEffect, useState } from "react";
2
- import { getExtensionStore } from "@openmrs/esm-extensions";
3
- import { isEqual } from "lodash";
2
+ import { getAssignedIds } from "@openmrs/esm-extensions";
3
+ import { useExtensionSlotConfig } from "./useExtensionSlotConfig";
4
+ import { useAttachedExtensionIds } from "./useAttachedExtensionIds";
4
5
 
5
6
  /**
6
7
  * Gets the assigned extension ids for a given extension slot name.
7
8
  * Does not consider if offline or online.
8
- * @param slotName The name of the slot to get the assigned IDs for.
9
- *
10
- * @deprecated Use `useAssignedExtensions`
9
+ * @param extensionSlotName The name of the slot to get the assigned IDs for.
11
10
  */
12
- export function useAssignedExtensionIds(slotName: string) {
13
- const [ids, setIds] = useState<Array<string>>([]);
11
+ export function useAssignedExtensionIds(extensionSlotName: string) {
12
+ const config = useExtensionSlotConfig(extensionSlotName);
13
+ const attachedIds = useAttachedExtensionIds(extensionSlotName);
14
+ const [assignedIds, setAssignedIds] = useState<Array<string>>([]);
14
15
 
15
16
  useEffect(() => {
16
- return getExtensionStore().subscribe((state) => {
17
- const newIds =
18
- state.slots[slotName]?.assignedExtensions.map((e) => e.id) ?? [];
19
- if (!isEqual(newIds, ids)) {
20
- setIds(newIds);
21
- }
22
- });
23
- }, []);
17
+ const newAssignedIds = getAssignedIds(
18
+ extensionSlotName,
19
+ config,
20
+ attachedIds
21
+ );
24
22
 
25
- return ids;
23
+ if (newAssignedIds.join(",") !== assignedIds.join(",")) {
24
+ setAssignedIds(newAssignedIds);
25
+ }
26
+ }, [attachedIds, config]);
27
+
28
+ return assignedIds;
26
29
  }
@@ -0,0 +1,18 @@
1
+ import { useCallback } from "react";
2
+ import { extensionStore, ExtensionStore } from "@openmrs/esm-extensions";
3
+ import { useStoreState } from "./useStoreState";
4
+
5
+ const defaultArray: Array<string> = [];
6
+
7
+ /**
8
+ * Gets the assigned extension ids for the given slot.
9
+ * @param extensionSlotName
10
+ */
11
+ export function useAttachedExtensionIds(extensionSlotName: string) {
12
+ const select = useCallback(
13
+ (s: ExtensionStore) => s.slots[extensionSlotName]?.attachedIds,
14
+ [extensionSlotName]
15
+ );
16
+ const extensions = useStoreState(extensionStore, select);
17
+ return extensions || defaultArray;
18
+ }
package/src/useConfig.ts CHANGED
@@ -120,5 +120,8 @@ export function useConfig() {
120
120
  [normalConfig, extensionConfig]
121
121
  );
122
122
 
123
+ const configNameForDebugMessage = extension
124
+ ? `${extension?.extensionSlotModuleName}-${extension?.extensionSlotName}-${extension?.extensionId}`
125
+ : moduleName;
123
126
  return config;
124
127
  }
@@ -1,25 +1,51 @@
1
1
  import { useMemo } from "react";
2
2
  import {
3
- ConnectedExtension,
4
- getConnectedExtensions,
3
+ checkStatusFor,
4
+ ExtensionMeta,
5
+ ExtensionRegistration,
6
+ extensionStore,
7
+ getExtensionRegistrationFrom,
5
8
  } from "@openmrs/esm-extensions";
9
+ import { useAssignedExtensionIds } from "./useAssignedExtensionIds";
6
10
  import { useConnectivity } from "./useConnectivity";
7
- import { useAssignedExtensions } from "./useAssignedExtensions";
11
+
12
+ function isValidExtension(
13
+ extension: ConnectedExtension | { id: string }
14
+ ): extension is ConnectedExtension {
15
+ return extension.hasOwnProperty("name");
16
+ }
17
+
18
+ /**
19
+ * We have the following extension modes:
20
+ *
21
+ * - attached (set via code in form of: attach, detach, ...)
22
+ * - configured (set via configuration in form of: added, removed, ...)
23
+ * - assigned (computed from attached and configured)
24
+ * - connected (computed from assigned using connectivity and online / offline)
25
+ */
26
+
27
+ export interface ConnectedExtension extends ExtensionRegistration {
28
+ id: string;
29
+ }
8
30
 
9
31
  /**
10
32
  * Gets the assigned extension for a given extension slot name.
11
33
  * Considers if offline or online.
12
- * @param slotName The name of the slot to get the assigned extensions for.
34
+ * @param extensionSlotName The name of the slot to get the assigned extensions for.
13
35
  */
14
36
  export function useConnectedExtensions(
15
- slotName: string
37
+ extensionSlotName: string
16
38
  ): Array<ConnectedExtension> {
17
39
  const online = useConnectivity();
18
- const assignedExtensions = useAssignedExtensions(slotName);
40
+ const extensionIdsToRender = useAssignedExtensionIds(extensionSlotName);
19
41
 
20
- const connectedExtensions = useMemo(() => {
21
- return getConnectedExtensions(assignedExtensions, online);
22
- }, [assignedExtensions, online]);
42
+ const extensions = useMemo(() => {
43
+ const state = extensionStore.getState();
44
+ return extensionIdsToRender
45
+ .map((id) => ({ id, ...getExtensionRegistrationFrom(state, id) }))
46
+ .filter(isValidExtension)
47
+ .filter((m) => checkStatusFor(online, m.online, m.offline));
48
+ }, [extensionIdsToRender, online]);
23
49
 
24
- return connectedExtensions;
50
+ return extensions;
25
51
  }
@@ -0,0 +1,31 @@
1
+ import { RefObject, useContext, useRef, useEffect } from "react";
2
+ import { renderExtension } from "@openmrs/esm-extensions";
3
+ import { ComponentContext, ExtensionData } from "./ComponentContext";
4
+
5
+ export function useExtension<TRef extends HTMLElement>(
6
+ state?: Record<string, any>
7
+ ): [RefObject<TRef>, ExtensionData | undefined] {
8
+ const ref = useRef<TRef>(null);
9
+ const { extension } = useContext(ComponentContext);
10
+
11
+ useEffect(() => {
12
+ if (ref.current && extension) {
13
+ return renderExtension(
14
+ ref.current,
15
+ extension.extensionSlotName,
16
+ extension.extensionSlotModuleName,
17
+ extension.extensionId,
18
+ undefined,
19
+ state
20
+ );
21
+ }
22
+ }, [
23
+ extension?.extensionSlotName,
24
+ extension?.extensionId,
25
+ extension?.extensionSlotModuleName,
26
+ ref.current,
27
+ state,
28
+ ]);
29
+
30
+ return [ref, extension];
31
+ }
@@ -1,10 +1,12 @@
1
1
  import { useContext, useEffect } from "react";
2
- import { registerExtensionSlot } from "@openmrs/esm-extensions";
2
+ import {
3
+ registerExtensionSlot,
4
+ unregisterExtensionSlot,
5
+ } from "@openmrs/esm-extensions";
3
6
  import { ComponentContext } from "./ComponentContext";
4
7
  import { useConnectedExtensions } from "./useConnectedExtensions";
5
8
 
6
- /** @internal */
7
- export function useExtensionSlot(slotName: string) {
9
+ export function useExtensionSlot(extensionSlotName: string) {
8
10
  const { moduleName } = useContext(ComponentContext);
9
11
 
10
12
  if (!moduleName) {
@@ -14,14 +16,15 @@ export function useExtensionSlot(slotName: string) {
14
16
  }
15
17
 
16
18
  useEffect(() => {
17
- registerExtensionSlot(moduleName, slotName);
19
+ registerExtensionSlot(moduleName, extensionSlotName);
20
+ return () => unregisterExtensionSlot(moduleName, extensionSlotName);
18
21
  }, []);
19
22
 
20
- const extensions = useConnectedExtensions(slotName);
23
+ const extensions = useConnectedExtensions(extensionSlotName);
21
24
 
22
25
  return {
23
26
  extensions,
24
- extensionSlotName: slotName,
27
+ extensionSlotName,
25
28
  extensionSlotModuleName: moduleName,
26
29
  };
27
30
  }
@@ -1,9 +1,10 @@
1
- import { useCallback } from "react";
1
+ import { useContext, useCallback } from "react";
2
2
  import {
3
3
  ExtensionSlotConfigObject,
4
- ExtensionSlotConfigStore,
5
- getExtensionSlotConfigStore,
4
+ ExtensionSlotConfigsStore,
5
+ getExtensionSlotsConfigStore,
6
6
  } from "@openmrs/esm-config";
7
+ import { ComponentContext } from "./ComponentContext";
7
8
  import { useStoreState } from "./useStoreState";
8
9
 
9
10
  const defaultConfig: ExtensionSlotConfigObject = {
@@ -12,12 +13,12 @@ const defaultConfig: ExtensionSlotConfigObject = {
12
13
  remove: [],
13
14
  };
14
15
 
15
- /** @internal */
16
- export function useExtensionSlotConfig(slotName: string) {
17
- const store = getExtensionSlotConfigStore(slotName);
16
+ export function useExtensionSlotConfig(extensionSlotName: string) {
17
+ const { moduleName } = useContext(ComponentContext);
18
+ const store = getExtensionSlotsConfigStore(moduleName);
18
19
  const select = useCallback(
19
- (s: ExtensionSlotConfigStore) => s.config,
20
- [slotName]
20
+ (s: ExtensionSlotConfigsStore) => s.extensionSlotConfigs[extensionSlotName],
21
+ [extensionSlotName]
21
22
  );
22
23
  const config = useStoreState(store, select);
23
24
  return config || defaultConfig;
@@ -1,13 +1,4 @@
1
- import {
2
- ExtensionInternalStore,
3
- getExtensionInternalStore,
4
- } from "@openmrs/esm-extensions";
1
+ import { ExtensionStore, extensionStore } from "@openmrs/esm-extensions";
5
2
  import { createUseStore } from "./createUseStore";
6
3
 
7
- /**
8
- * The implementation of this will soon undergo a breaking change.
9
- * This will return an `ExtensionStore` rather than `ExtensionInternalStore`.
10
- */
11
- export const useExtensionStore = createUseStore<ExtensionInternalStore>(
12
- getExtensionInternalStore()
13
- );
4
+ export const useExtensionStore = createUseStore<ExtensionStore>(extensionStore);
@@ -1,8 +0,0 @@
1
- /**
2
- * @license
3
- * Lodash <https://lodash.com/>
4
- * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
5
- * Released under MIT license <https://lodash.com/license>
6
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
7
- * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
8
- */
@@ -1,23 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { AssignedExtension, getExtensionStore } from "@openmrs/esm-extensions";
3
- import { isEqual } from "lodash";
4
-
5
- /**
6
- * Gets the assigned extensions for a given extension slot name.
7
- * Does not consider if offline or online.
8
- * @param slotName The name of the slot to get the assigned extensions for.
9
- */
10
- export function useAssignedExtensions(slotName: string) {
11
- const [extensions, setExtensions] = useState<Array<AssignedExtension>>([]);
12
-
13
- useEffect(() => {
14
- return getExtensionStore().subscribe((state) => {
15
- const newExtensions = state.slots[slotName]?.assignedExtensions ?? [];
16
- if (!isEqual(newExtensions, extensions)) {
17
- setExtensions(newExtensions);
18
- }
19
- });
20
- }, []);
21
-
22
- return extensions;
23
- }
@@ -1,10 +0,0 @@
1
- import {
2
- ExtensionInternalStore,
3
- getExtensionInternalStore,
4
- } from "@openmrs/esm-extensions";
5
- import { createUseStore } from "./createUseStore";
6
-
7
- /** @internal */
8
- export const useExtensionInternalStore = createUseStore<ExtensionInternalStore>(
9
- getExtensionInternalStore()
10
- );