@openmrs/esm-react-utils 6.3.1-pre.2965 → 6.3.1-pre.2986
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/.swcrc +16 -0
- package/.turbo/turbo-build.log +3 -14
- package/dist/ComponentContext.d.ts +15 -0
- package/dist/ComponentContext.js +7 -0
- package/dist/ConfigurableLink.d.ts +21 -0
- package/dist/ConfigurableLink.js +48 -0
- package/dist/Extension.d.ts +14 -0
- package/dist/Extension.js +83 -0
- package/dist/ExtensionSlot.d.ts +69 -0
- package/dist/ExtensionSlot.js +97 -0
- package/dist/OpenmrsContext.d.ts +20 -0
- package/dist/OpenmrsContext.js +17 -0
- package/dist/RenderIfValueIsTruthy.d.ts +19 -0
- package/dist/RenderIfValueIsTruthy.js +20 -0
- package/dist/UserHasAccess.d.ts +35 -0
- package/dist/UserHasAccess.js +43 -0
- package/dist/getLifecycle.d.ts +13 -0
- package/dist/getLifecycle.js +20 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +48 -0
- package/dist/openmrsComponentDecorator.d.ts +19 -0
- package/dist/openmrsComponentDecorator.js +102 -0
- package/dist/public.d.ts +44 -0
- package/dist/public.js +43 -0
- package/dist/useAbortController.d.ts +23 -0
- package/dist/useAbortController.js +33 -0
- package/dist/useAppContext.d.ts +21 -0
- package/dist/useAppContext.js +45 -0
- package/dist/useAssignedExtensionIds.d.ts +8 -0
- package/dist/useAssignedExtensionIds.js +21 -0
- package/dist/useAssignedExtensions.d.ts +5 -0
- package/dist/useAssignedExtensions.js +8 -0
- package/dist/useAttachments.d.ts +11 -0
- package/dist/useAttachments.js +21 -0
- package/dist/useBodyScrollLock.d.ts +1 -0
- package/dist/useBodyScrollLock.js +14 -0
- package/dist/useConfig.d.ts +11 -0
- package/dist/useConfig.js +136 -0
- package/dist/useConnectedExtensions.d.ts +8 -0
- package/dist/useConnectedExtensions.js +6 -0
- package/dist/useConnectivity.d.ts +1 -0
- package/dist/useConnectivity.js +8 -0
- package/dist/useDebounce.d.ts +29 -0
- package/dist/useDebounce.js +43 -0
- package/dist/useDefineAppContext.d.ts +32 -0
- package/dist/useDefineAppContext.js +56 -0
- package/dist/useEmrConfiguration.d.ts +97 -0
- package/dist/useEmrConfiguration.js +236 -0
- package/dist/useExtensionInternalStore.d.ts +9 -0
- package/dist/useExtensionInternalStore.js +5 -0
- package/dist/useExtensionSlot.d.ts +6 -0
- package/dist/useExtensionSlot.js +19 -0
- package/dist/useExtensionSlotMeta.d.ts +9 -0
- package/dist/useExtensionSlotMeta.js +14 -0
- package/dist/useExtensionSlotStore.d.ts +3 -0
- package/dist/useExtensionSlotStore.js +3 -0
- package/dist/useExtensionStore.d.ts +7 -0
- package/dist/useExtensionStore.js +3 -0
- package/dist/useFeatureFlag.d.ts +14 -0
- package/dist/useFeatureFlag.js +19 -0
- package/dist/useFhirFetchAll.d.ts +18 -0
- package/dist/useFhirFetchAll.js +18 -0
- package/dist/useFhirInfinite.d.ts +19 -0
- package/dist/useFhirInfinite.js +20 -0
- package/dist/useFhirPagination.d.ts +43 -0
- package/dist/useFhirPagination.js +51 -0
- package/dist/useForceUpdate.d.ts +1 -0
- package/dist/useForceUpdate.js +8 -0
- package/dist/useLayoutType.d.ts +3 -0
- package/dist/useLayoutType.js +30 -0
- package/dist/useLocations.d.ts +2 -0
- package/dist/useLocations.js +17 -0
- package/dist/useOnClickOutside.d.ts +2 -0
- package/dist/useOnClickOutside.js +22 -0
- package/dist/useOnVisible.d.ts +14 -0
- package/dist/useOnVisible.js +40 -0
- package/dist/useOpenmrsFetchAll.d.ts +29 -0
- package/dist/useOpenmrsFetchAll.js +42 -0
- package/dist/useOpenmrsInfinite.d.ts +85 -0
- package/dist/useOpenmrsInfinite.js +68 -0
- package/dist/useOpenmrsPagination.d.ts +99 -0
- package/dist/useOpenmrsPagination.js +129 -0
- package/dist/useOpenmrsSWR.d.ts +50 -0
- package/dist/useOpenmrsSWR.js +72 -0
- package/dist/usePagination.d.ts +20 -0
- package/dist/usePagination.js +70 -0
- package/dist/usePatient.d.ts +14 -0
- package/dist/usePatient.js +42 -0
- package/dist/usePrimaryIdentifierResource.d.ts +8 -0
- package/dist/usePrimaryIdentifierResource.js +17 -0
- package/dist/useRenderableExtensions.d.ts +30 -0
- package/dist/useRenderableExtensions.js +43 -0
- package/dist/useSession.d.ts +16 -0
- package/dist/useSession.js +108 -0
- package/dist/useStore.d.ts +30 -0
- package/dist/useStore.js +59 -0
- package/dist/useVisit.d.ts +31 -0
- package/dist/useVisit.js +83 -0
- package/dist/useVisitContextStore.d.ts +12 -0
- package/dist/useVisitContextStore.js +55 -0
- package/dist/useVisitTypes.d.ts +3 -0
- package/dist/useVisitTypes.js +12 -0
- package/mock-jest.tsx +153 -0
- package/mock.tsx +55 -53
- package/package.json +44 -19
- package/setup-tests.ts +19 -0
- package/src/ConfigurableLink.test.tsx +7 -4
- package/src/ConfigurableLink.tsx +1 -2
- package/src/extensions.test.tsx +11 -16
- package/src/openmrsComponentDecorator.test.tsx +5 -4
- package/src/useAbortController.test.tsx +2 -1
- package/src/useAttachments.ts +2 -1
- package/src/useConfig.test.tsx +14 -1
- package/src/useConfig.ts +2 -2
- package/src/useEmrConfiguration.ts +1 -1
- package/src/useLocations.tsx +1 -2
- package/src/useOnClickOutside.test.tsx +3 -2
- package/src/useOpenmrsFetchAll.test.tsx +1 -1
- package/src/useOpenmrsInfinite.test.tsx +1 -1
- package/src/useOpenmrsInfinite.ts +0 -1
- package/src/useOpenmrsPagination.test.tsx +1 -1
- package/src/usePatient.ts +1 -1
- package/src/useSession.test.tsx +5 -3
- package/src/useStore.test.ts +2 -1
- package/src/useVisit.ts +4 -4
- package/src/useVisitContextStore.ts +2 -2
- package/src/useVisitTypes.ts +1 -2
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +3 -24
- package/vitest.config.ts +9 -0
- package/dist/openmrs-esm-react-utils.js +0 -3
- package/dist/openmrs-esm-react-utils.js.LICENSE.txt +0 -9
- package/dist/openmrs-esm-react-utils.js.map +0 -1
- package/jest.config.js +0 -18
- package/src/setup-tests.js +0 -11
- package/webpack.config.js +0 -42
package/.swcrc
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://swc.rs/schema.json",
|
|
3
|
+
"exclude": [".*\\.test\\..*", "setup-tests\\..*"],
|
|
4
|
+
"module": {
|
|
5
|
+
"type": "es6",
|
|
6
|
+
"resolveFully": true
|
|
7
|
+
},
|
|
8
|
+
"jsc": {
|
|
9
|
+
"parser": {
|
|
10
|
+
"syntax": "typescript",
|
|
11
|
+
"tsx": false
|
|
12
|
+
},
|
|
13
|
+
"target": "es2020",
|
|
14
|
+
"baseUrl": "src"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
built modules 310 KiB [built]
|
|
5
|
-
modules by path external "@openmrs/ 336 bytes
|
|
6
|
-
external "@openmrs/esm-navigation" 42 bytes [built] [code generated]
|
|
7
|
-
external "@openmrs/esm-extensions" 42 bytes [built] [code generated]
|
|
8
|
-
+ 6 modules
|
|
9
|
-
cacheable modules 309 KiB
|
|
10
|
-
modules by path ../../../node_modules/ 3.11 KiB 4 modules
|
|
11
|
-
./src/index.ts + 141 modules 298 KiB [built] [code generated]
|
|
12
|
-
../esm-state/dist/openmrs-esm-state.js 8.12 KiB [built] [code generated]
|
|
13
|
-
+ 5 modules
|
|
14
|
-
webpack 5.88.0 compiled successfully in 10351 ms
|
|
1
|
+
[0] Successfully compiled: 50 files with swc (174.78ms)
|
|
2
|
+
[0] swc --strip-leading-paths src -d dist exited with code 0
|
|
3
|
+
[1] tsc --project tsconfig.build.json exited with code 0
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ExtensionData {
|
|
3
|
+
extensionSlotName: string;
|
|
4
|
+
extensionSlotModuleName: string;
|
|
5
|
+
extensionId: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ComponentConfig {
|
|
8
|
+
moduleName: string;
|
|
9
|
+
featureName: string;
|
|
10
|
+
extension?: ExtensionData;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Available to all components. Provided by `openmrsComponentDecorator`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ComponentContext: React.Context<ComponentConfig>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @module @category Navigation */
|
|
2
|
+
import React, { type MouseEvent, type AnchorHTMLAttributes, type PropsWithChildren } from 'react';
|
|
3
|
+
import { type TemplateParams } from '@openmrs/esm-navigation';
|
|
4
|
+
/**
|
|
5
|
+
* @noInheritDoc
|
|
6
|
+
*/
|
|
7
|
+
export interface ConfigurableLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
8
|
+
to: string;
|
|
9
|
+
templateParams?: TemplateParams;
|
|
10
|
+
onBeforeNavigate?: (event: MouseEvent) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A React link component which calls [[navigate]] when clicked
|
|
14
|
+
*
|
|
15
|
+
* @param to The target path or URL. Supports interpolation. See [[navigate]]
|
|
16
|
+
* @param templateParams: A dictionary of values to interpolate into the URL, in addition to the default keys `openmrsBase` and `openmrsSpaBase`.
|
|
17
|
+
* @param onBeforeNavigate A callback to be called just before navigation occurs
|
|
18
|
+
* @param children Inline elements within the link
|
|
19
|
+
* @param otherProps Any other valid props for an <a> tag except `href` and `onClick`
|
|
20
|
+
*/
|
|
21
|
+
export declare function ConfigurableLink({ to, templateParams, onBeforeNavigate, children, ...otherProps }: PropsWithChildren<ConfigurableLinkProps>): React.JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** @module @category Navigation */ import React, { useEffect } from "react";
|
|
2
|
+
import { navigate, interpolateUrl } from "@openmrs/esm-navigation";
|
|
3
|
+
function handleClick(event, to, templateParams, onBeforeNavigate) {
|
|
4
|
+
// Left click without modifiers (normal navigation)
|
|
5
|
+
if (event.button === 0 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
|
|
6
|
+
event.preventDefault();
|
|
7
|
+
onBeforeNavigate?.(event);
|
|
8
|
+
navigate({
|
|
9
|
+
to,
|
|
10
|
+
templateParams
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
// Left click with Ctrl key (or Cmd key on Mac) - Open in new tab
|
|
14
|
+
if (event.button === 0 && (event.ctrlKey || event.metaKey)) {
|
|
15
|
+
onBeforeNavigate?.(event);
|
|
16
|
+
}
|
|
17
|
+
// Left click with Shift key - Open in new window
|
|
18
|
+
if (event.button === 0 && event.shiftKey) {
|
|
19
|
+
onBeforeNavigate?.(event);
|
|
20
|
+
}
|
|
21
|
+
// Middle click - Open in new background tab
|
|
22
|
+
if (event.button === 1) {
|
|
23
|
+
onBeforeNavigate?.(event);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A React link component which calls [[navigate]] when clicked
|
|
28
|
+
*
|
|
29
|
+
* @param to The target path or URL. Supports interpolation. See [[navigate]]
|
|
30
|
+
* @param templateParams: A dictionary of values to interpolate into the URL, in addition to the default keys `openmrsBase` and `openmrsSpaBase`.
|
|
31
|
+
* @param onBeforeNavigate A callback to be called just before navigation occurs
|
|
32
|
+
* @param children Inline elements within the link
|
|
33
|
+
* @param otherProps Any other valid props for an <a> tag except `href` and `onClick`
|
|
34
|
+
*/ export function ConfigurableLink({ to, templateParams, onBeforeNavigate, children, ...otherProps }) {
|
|
35
|
+
useEffect(()=>{
|
|
36
|
+
if (otherProps.href) {
|
|
37
|
+
console.warn(`ConfigurableLink does not support the href prop. Use the 'to' prop instead. The provided href value is '${otherProps.href}'`);
|
|
38
|
+
}
|
|
39
|
+
if (otherProps.onClick) {
|
|
40
|
+
console.warn(`ConfigurableLink does not support the onClick prop. Use the 'onBeforeNavigate' prop instead. The 'to' prop of the offending link is ${to}`);
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
43
|
+
return /*#__PURE__*/ React.createElement("a", {
|
|
44
|
+
onClick: (event)=>handleClick(event, to, templateParams, onBeforeNavigate),
|
|
45
|
+
href: interpolateUrl(to, templateParams),
|
|
46
|
+
...otherProps
|
|
47
|
+
}, children);
|
|
48
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type ExtensionProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
state?: Record<string, unknown>;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Represents the position in the DOM where each extension within
|
|
7
|
+
* an extension slot is rendered.
|
|
8
|
+
*
|
|
9
|
+
* Renders once for each extension attached to that extension slot.
|
|
10
|
+
*
|
|
11
|
+
* Usage of this component *must* have an ancestor `<ExtensionSlot>`,
|
|
12
|
+
* and *must* only be used once within that `<ExtensionSlot>`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const Extension: React.FC<ExtensionProps>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** @module @category Extension */ import { renderExtension } from "@openmrs/esm-extensions";
|
|
2
|
+
import React, { useCallback, useContext, useEffect, useRef } from "react";
|
|
3
|
+
import { ComponentContext } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Represents the position in the DOM where each extension within
|
|
6
|
+
* an extension slot is rendered.
|
|
7
|
+
*
|
|
8
|
+
* Renders once for each extension attached to that extension slot.
|
|
9
|
+
*
|
|
10
|
+
* Usage of this component *must* have an ancestor `<ExtensionSlot>`,
|
|
11
|
+
* and *must* only be used once within that `<ExtensionSlot>`.
|
|
12
|
+
*/ export const Extension = ({ state, children, ...divProps })=>{
|
|
13
|
+
const { extension } = useContext(ComponentContext);
|
|
14
|
+
const parcel = useRef(null);
|
|
15
|
+
const updatePromise = useRef(Promise.resolve());
|
|
16
|
+
const ref = useCallback((node)=>{
|
|
17
|
+
if (extension?.extensionSlotName && extension.extensionSlotModuleName && extension.extensionSlotModuleName && !parcel.current) {
|
|
18
|
+
renderExtension(node, extension.extensionSlotName, extension.extensionSlotModuleName, extension.extensionId, undefined, state).then((newParcel)=>{
|
|
19
|
+
parcel.current = newParcel;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}, []);
|
|
23
|
+
useEffect(()=>{
|
|
24
|
+
return ()=>{
|
|
25
|
+
if (parcel && parcel.current) {
|
|
26
|
+
const status = parcel.current.getStatus();
|
|
27
|
+
switch(status){
|
|
28
|
+
case 'MOUNTING':
|
|
29
|
+
parcel.current.mountPromise.then(()=>{
|
|
30
|
+
if (parcel.current?.getStatus() === 'MOUNTED') {
|
|
31
|
+
parcel.current.unmount();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
case 'MOUNTED':
|
|
36
|
+
parcel.current.unmount();
|
|
37
|
+
break;
|
|
38
|
+
case 'UPDATING':
|
|
39
|
+
if (updatePromise.current) {
|
|
40
|
+
updatePromise.current.then(()=>{
|
|
41
|
+
if (parcel.current?.getStatus() === 'MOUNTED') {
|
|
42
|
+
parcel.current.unmount();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}, []);
|
|
50
|
+
useEffect(()=>{
|
|
51
|
+
if (parcel.current && parcel.current.update && parcel.current.getStatus() !== 'UNMOUNTING') {
|
|
52
|
+
Promise.all([
|
|
53
|
+
parcel.current.mountPromise,
|
|
54
|
+
updatePromise.current
|
|
55
|
+
]).then(()=>{
|
|
56
|
+
if (parcel?.current?.getStatus() === 'MOUNTED' && parcel.current.update) {
|
|
57
|
+
updatePromise.current = parcel.current.update({
|
|
58
|
+
...state
|
|
59
|
+
}).catch((err)=>{
|
|
60
|
+
// if we were trying to update but the component was unmounted
|
|
61
|
+
// while this was happening, ignore the error
|
|
62
|
+
if (!(err instanceof Error) || !err.message.includes('minified message #32') || parcel.current?.getStatus() === 'MOUNTED') {
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}, [
|
|
70
|
+
state
|
|
71
|
+
]);
|
|
72
|
+
// The extension is rendered into the `<div>`. The `<div>` has relative
|
|
73
|
+
// positioning in order to allow the UI Editor to absolutely position
|
|
74
|
+
// elements within it.
|
|
75
|
+
return extension ? /*#__PURE__*/ React.createElement("div", {
|
|
76
|
+
ref: ref,
|
|
77
|
+
"data-extension-id": extension?.extensionId,
|
|
78
|
+
style: {
|
|
79
|
+
position: 'relative'
|
|
80
|
+
},
|
|
81
|
+
...divProps
|
|
82
|
+
}) : null;
|
|
83
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** @module @category Extension */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { type AssignedExtension } from '@openmrs/esm-extensions';
|
|
4
|
+
export interface ExtensionSlotBaseProps {
|
|
5
|
+
name: string;
|
|
6
|
+
/** @deprecated Use `name` */
|
|
7
|
+
extensionSlotName?: string;
|
|
8
|
+
select?: (extensions: Array<AssignedExtension>) => Array<AssignedExtension>;
|
|
9
|
+
state?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface OldExtensionSlotBaseProps {
|
|
12
|
+
name?: string;
|
|
13
|
+
/** @deprecated Use `name` */
|
|
14
|
+
extensionSlotName: string;
|
|
15
|
+
select?: (extensions: Array<AssignedExtension>) => Array<AssignedExtension>;
|
|
16
|
+
state?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export type ExtensionSlotProps = (OldExtensionSlotBaseProps | ExtensionSlotBaseProps) & Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
19
|
+
children?: React.ReactNode | ((extension: AssignedExtension, state?: Record<string, unknown>) => React.ReactNode);
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* An [extension slot](https://o3-docs.openmrs.org/docs/extension-system).
|
|
23
|
+
* A place with a name. Extensions that get connected to that name
|
|
24
|
+
* will be rendered into this.
|
|
25
|
+
*
|
|
26
|
+
* @param props.name The name of the extension slot
|
|
27
|
+
* @param props.select An optional function for filtering or otherwise modifying
|
|
28
|
+
* the list of extensions that will be rendered.
|
|
29
|
+
* @param props.state *Only works if no children are provided*. Passes data
|
|
30
|
+
* through as props to the extensions that are mounted here. If `ExtensionSlot`
|
|
31
|
+
* has children, you must pass the state through the `state` param of the
|
|
32
|
+
* `Extension` component.
|
|
33
|
+
* @param props.children There are two different ways to use `ExtensionSlot`
|
|
34
|
+
* children.
|
|
35
|
+
* - Passing a `ReactNode`, the "normal" way. The child must contain the component
|
|
36
|
+
* `Extension`. Whatever is passed as the child will be rendered once per extension.
|
|
37
|
+
* See the first example below.
|
|
38
|
+
* - Passing a function, the "render props" way. The child must be a function
|
|
39
|
+
* which takes a [[ConnectedExtension]] as argument and returns a `ReactNode`.
|
|
40
|
+
* the resulting react node must contain the component `Extension`. It will
|
|
41
|
+
* be run for each extension. See the second example below.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* Passing a react node as children
|
|
45
|
+
*
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <ExtensionSlot name="Foo">
|
|
48
|
+
* <div style={{ width: 10rem }}>
|
|
49
|
+
* <Extension />
|
|
50
|
+
* </div>
|
|
51
|
+
* </ExtensionSlot>
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* Passing a function as children
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <ExtensionSlot name="Bar">
|
|
59
|
+
* {(extension) => (
|
|
60
|
+
* <h1>{extension.name}</h1>
|
|
61
|
+
* <div style={{ color: extension.meta.color }}>
|
|
62
|
+
* <Extension />
|
|
63
|
+
* </div>
|
|
64
|
+
* )}
|
|
65
|
+
* </ExtensionSlot>
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
*/
|
|
69
|
+
export declare function ExtensionSlot({ name: extensionSlotName, extensionSlotName: legacyExtensionSlotName, select, children, state, style, ...divProps }: ExtensionSlotProps): React.JSX.Element;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** @module @category Extension */ import React, { useRef, useMemo } from "react";
|
|
2
|
+
import { ComponentContext } from "./ComponentContext.js";
|
|
3
|
+
import { Extension } from "./Extension.js";
|
|
4
|
+
import { useExtensionSlot } from "./useExtensionSlot.js";
|
|
5
|
+
function defaultSelect(extensions) {
|
|
6
|
+
return extensions;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* An [extension slot](https://o3-docs.openmrs.org/docs/extension-system).
|
|
10
|
+
* A place with a name. Extensions that get connected to that name
|
|
11
|
+
* will be rendered into this.
|
|
12
|
+
*
|
|
13
|
+
* @param props.name The name of the extension slot
|
|
14
|
+
* @param props.select An optional function for filtering or otherwise modifying
|
|
15
|
+
* the list of extensions that will be rendered.
|
|
16
|
+
* @param props.state *Only works if no children are provided*. Passes data
|
|
17
|
+
* through as props to the extensions that are mounted here. If `ExtensionSlot`
|
|
18
|
+
* has children, you must pass the state through the `state` param of the
|
|
19
|
+
* `Extension` component.
|
|
20
|
+
* @param props.children There are two different ways to use `ExtensionSlot`
|
|
21
|
+
* children.
|
|
22
|
+
* - Passing a `ReactNode`, the "normal" way. The child must contain the component
|
|
23
|
+
* `Extension`. Whatever is passed as the child will be rendered once per extension.
|
|
24
|
+
* See the first example below.
|
|
25
|
+
* - Passing a function, the "render props" way. The child must be a function
|
|
26
|
+
* which takes a [[ConnectedExtension]] as argument and returns a `ReactNode`.
|
|
27
|
+
* the resulting react node must contain the component `Extension`. It will
|
|
28
|
+
* be run for each extension. See the second example below.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* Passing a react node as children
|
|
32
|
+
*
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <ExtensionSlot name="Foo">
|
|
35
|
+
* <div style={{ width: 10rem }}>
|
|
36
|
+
* <Extension />
|
|
37
|
+
* </div>
|
|
38
|
+
* </ExtensionSlot>
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* Passing a function as children
|
|
43
|
+
*
|
|
44
|
+
* ```tsx
|
|
45
|
+
* <ExtensionSlot name="Bar">
|
|
46
|
+
* {(extension) => (
|
|
47
|
+
* <h1>{extension.name}</h1>
|
|
48
|
+
* <div style={{ color: extension.meta.color }}>
|
|
49
|
+
* <Extension />
|
|
50
|
+
* </div>
|
|
51
|
+
* )}
|
|
52
|
+
* </ExtensionSlot>
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
*/ export function ExtensionSlot({ name: extensionSlotName, extensionSlotName: legacyExtensionSlotName, select = defaultSelect, children, state, style, ...divProps }) {
|
|
56
|
+
if (children && state) {
|
|
57
|
+
throw new Error('Both children and state have been provided. If children are provided, the state must be passed as a prop to the `Extension` component.');
|
|
58
|
+
}
|
|
59
|
+
const name = extensionSlotName ?? legacyExtensionSlotName;
|
|
60
|
+
const slotRef = useRef(null);
|
|
61
|
+
const { extensions, extensionSlotModuleName } = useExtensionSlot(name);
|
|
62
|
+
const extensionsToRender = useMemo(()=>select(extensions), [
|
|
63
|
+
select,
|
|
64
|
+
extensions
|
|
65
|
+
]);
|
|
66
|
+
const extensionsFromChildrenFunction = useMemo(()=>{
|
|
67
|
+
if (typeof children == 'function' && !/*#__PURE__*/ React.isValidElement(children)) {
|
|
68
|
+
return extensionsToRender.map((extension)=>children(extension, state));
|
|
69
|
+
}
|
|
70
|
+
}, [
|
|
71
|
+
children,
|
|
72
|
+
extensionsToRender
|
|
73
|
+
]);
|
|
74
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
75
|
+
ref: slotRef,
|
|
76
|
+
"data-extension-slot-name": name,
|
|
77
|
+
"data-extension-slot-module-name": extensionSlotModuleName,
|
|
78
|
+
style: {
|
|
79
|
+
...style,
|
|
80
|
+
position: 'relative'
|
|
81
|
+
},
|
|
82
|
+
...divProps
|
|
83
|
+
}, name && extensionsToRender?.map((extension, i)=>/*#__PURE__*/ React.createElement(ComponentContext.Provider, {
|
|
84
|
+
key: extension.id,
|
|
85
|
+
value: {
|
|
86
|
+
moduleName: extensionSlotModuleName,
|
|
87
|
+
featureName: '',
|
|
88
|
+
extension: {
|
|
89
|
+
extensionId: extension.id,
|
|
90
|
+
extensionSlotName: name,
|
|
91
|
+
extensionSlotModuleName
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}, extensionsFromChildrenFunction?.[i] ?? (typeof children !== 'function' ? children : null) ?? /*#__PURE__*/ React.createElement(Extension, {
|
|
95
|
+
state: state
|
|
96
|
+
}))));
|
|
97
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface OpenmrsAppContextProps<T = unknown> {
|
|
2
|
+
/** the namespace that this component defines */
|
|
3
|
+
namespace: string;
|
|
4
|
+
/** used to control the value associated with the namespace */
|
|
5
|
+
value?: T;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* OpenmrsAppContext is a simple React component meant to function similarly to React's Context,
|
|
9
|
+
* but built on top of the OpenmrsAppContext.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* <OpenmrsAppContext namespace="something" value={{ key: "1234" }} />
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* **Notes on usage:** Unlike a proper React context where the value is limited to the subtree under the
|
|
17
|
+
* context component, the `OpenmrsAppContext` is inherently global in scope. That means that _all applications_
|
|
18
|
+
* will see the values that you set for the namespace if they load the value of the namespace.
|
|
19
|
+
*/
|
|
20
|
+
export declare function OpenmrsAppContext<T extends {}>({ namespace, value }: OpenmrsAppContextProps<T>): null;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @module @category Context */ import { useDefineAppContext } from "./useDefineAppContext.js";
|
|
2
|
+
/**
|
|
3
|
+
* OpenmrsAppContext is a simple React component meant to function similarly to React's Context,
|
|
4
|
+
* but built on top of the OpenmrsAppContext.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* <OpenmrsAppContext namespace="something" value={{ key: "1234" }} />
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* **Notes on usage:** Unlike a proper React context where the value is limited to the subtree under the
|
|
12
|
+
* context component, the `OpenmrsAppContext` is inherently global in scope. That means that _all applications_
|
|
13
|
+
* will see the values that you set for the namespace if they load the value of the namespace.
|
|
14
|
+
*/ export function OpenmrsAppContext({ namespace, value }) {
|
|
15
|
+
useDefineAppContext(namespace, value);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, { type PropsWithChildren } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A really simple component that renders its children if the prop named `value` has a truthy value
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <RenderIfValueIsTruthy value={variable}>
|
|
8
|
+
* <Component value={variable} />
|
|
9
|
+
* </RenderIfValueIsTruthy>
|
|
10
|
+
* ````
|
|
11
|
+
*
|
|
12
|
+
* @param props.value The value to check whether or not its truthy
|
|
13
|
+
* @param props.fallback What to render if the value is not truthy. If not specified, nothing will be rendered
|
|
14
|
+
* @param props.children The components to render if the `value` is truthy
|
|
15
|
+
*/
|
|
16
|
+
export declare const RenderIfValueIsTruthy: React.FC<PropsWithChildren<{
|
|
17
|
+
value: unknown;
|
|
18
|
+
fallback?: React.ReactNode;
|
|
19
|
+
}>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A really simple component that renders its children if the prop named `value` has a truthy value
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <RenderIfValueIsTruthy value={variable}>
|
|
8
|
+
* <Component value={variable} />
|
|
9
|
+
* </RenderIfValueIsTruthy>
|
|
10
|
+
* ````
|
|
11
|
+
*
|
|
12
|
+
* @param props.value The value to check whether or not its truthy
|
|
13
|
+
* @param props.fallback What to render if the value is not truthy. If not specified, nothing will be rendered
|
|
14
|
+
* @param props.children The components to render if the `value` is truthy
|
|
15
|
+
*/ export const RenderIfValueIsTruthy = ({ children, value, fallback })=>{
|
|
16
|
+
if (Boolean(value)) {
|
|
17
|
+
return /*#__PURE__*/ React.createElement(React.Fragment, null, children);
|
|
18
|
+
}
|
|
19
|
+
return fallback ? /*#__PURE__*/ React.createElement(React.Fragment, null, fallback) : null;
|
|
20
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface UserHasAccessProps {
|
|
3
|
+
privilege: string | string[];
|
|
4
|
+
fallback?: React.ReactNode;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A React component that renders its children only if the current user exists and has the privilege(s)
|
|
9
|
+
* specified by the `privilege` prop. This can be used not to render certain components when the user
|
|
10
|
+
* doesn't have the permission to use this.
|
|
11
|
+
*
|
|
12
|
+
* Note that for top-level extensions (i.e., the component that's the root of the extension), you don't
|
|
13
|
+
* need to use this component. Instead, when registering the extension, declare the required privileges
|
|
14
|
+
* as part of the extension registration. This is for use deeper in extensions or other components where
|
|
15
|
+
* a separate permission check might be needed.
|
|
16
|
+
*
|
|
17
|
+
* This can also be used to hide components when the current user is not logged in.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* <Form>
|
|
22
|
+
* <UserHasAccess privilege='Form Finallizer'>
|
|
23
|
+
* <Checkbox id="finalize-form" value={formFinalized} onChange={handleChange} />
|
|
24
|
+
* </UserHasAccess>
|
|
25
|
+
* </Form>
|
|
26
|
+
* ````
|
|
27
|
+
*
|
|
28
|
+
* @param props.privilege Either a string for a single required privilege or an array of strings for a
|
|
29
|
+
* set of required privileges. Note that sets of required privileges must all be matched.
|
|
30
|
+
* @param props.fallback What to render if the user does not have access or if the user is not currently
|
|
31
|
+
* logged in. If not provided, nothing will be rendered
|
|
32
|
+
* @param props.children The children to be rendered only if the user is logged in and has the required
|
|
33
|
+
* privileges.
|
|
34
|
+
*/
|
|
35
|
+
export declare const UserHasAccess: React.FC<UserHasAccessProps>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** @module @category API */ import { getCurrentUser, userHasAccess } from "@openmrs/esm-api";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A React component that renders its children only if the current user exists and has the privilege(s)
|
|
5
|
+
* specified by the `privilege` prop. This can be used not to render certain components when the user
|
|
6
|
+
* doesn't have the permission to use this.
|
|
7
|
+
*
|
|
8
|
+
* Note that for top-level extensions (i.e., the component that's the root of the extension), you don't
|
|
9
|
+
* need to use this component. Instead, when registering the extension, declare the required privileges
|
|
10
|
+
* as part of the extension registration. This is for use deeper in extensions or other components where
|
|
11
|
+
* a separate permission check might be needed.
|
|
12
|
+
*
|
|
13
|
+
* This can also be used to hide components when the current user is not logged in.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* <Form>
|
|
18
|
+
* <UserHasAccess privilege='Form Finallizer'>
|
|
19
|
+
* <Checkbox id="finalize-form" value={formFinalized} onChange={handleChange} />
|
|
20
|
+
* </UserHasAccess>
|
|
21
|
+
* </Form>
|
|
22
|
+
* ````
|
|
23
|
+
*
|
|
24
|
+
* @param props.privilege Either a string for a single required privilege or an array of strings for a
|
|
25
|
+
* set of required privileges. Note that sets of required privileges must all be matched.
|
|
26
|
+
* @param props.fallback What to render if the user does not have access or if the user is not currently
|
|
27
|
+
* logged in. If not provided, nothing will be rendered
|
|
28
|
+
* @param props.children The children to be rendered only if the user is logged in and has the required
|
|
29
|
+
* privileges.
|
|
30
|
+
*/ export const UserHasAccess = ({ privilege, fallback, children })=>{
|
|
31
|
+
const [user, setUser] = useState(null);
|
|
32
|
+
useEffect(()=>{
|
|
33
|
+
const subscription = getCurrentUser({
|
|
34
|
+
includeAuthStatus: false
|
|
35
|
+
}).subscribe(setUser);
|
|
36
|
+
return ()=>subscription.unsubscribe();
|
|
37
|
+
}, []);
|
|
38
|
+
if (user && userHasAccess(privilege, user)) {
|
|
39
|
+
return /*#__PURE__*/ React.createElement(React.Fragment, null, children);
|
|
40
|
+
} else {
|
|
41
|
+
return fallback ? /*#__PURE__*/ React.createElement(React.Fragment, null, fallback) : null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @module @category Framework */
|
|
2
|
+
import type { ComponentType } from 'react';
|
|
3
|
+
import singleSpaReact from 'single-spa-react';
|
|
4
|
+
import type { ComponentDecoratorOptions } from './openmrsComponentDecorator';
|
|
5
|
+
export declare function getLifecycle<T>(Component: ComponentType<T>, options: ComponentDecoratorOptions): singleSpaReact.ReactAppOrParcel<T>;
|
|
6
|
+
export declare function getAsyncLifecycle<T>(lazy: () => Promise<{
|
|
7
|
+
default: ComponentType<T>;
|
|
8
|
+
}>, options: ComponentDecoratorOptions): () => Promise<singleSpaReact.ReactAppOrParcel<T>>;
|
|
9
|
+
export declare function getSyncLifecycle<T>(Component: ComponentType<T>, options: ComponentDecoratorOptions): () => Promise<singleSpaReact.ReactAppOrParcel<T>>;
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use getAsyncLifecycle instead.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getAsyncExtensionLifecycle: typeof getAsyncLifecycle;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @module @category Framework */ import React from "react";
|
|
2
|
+
import ReactDOMClient from "react-dom/client";
|
|
3
|
+
import singleSpaReact from "single-spa-react";
|
|
4
|
+
import { openmrsComponentDecorator } from "./openmrsComponentDecorator.js";
|
|
5
|
+
export function getLifecycle(Component, options) {
|
|
6
|
+
return singleSpaReact({
|
|
7
|
+
React,
|
|
8
|
+
ReactDOMClient,
|
|
9
|
+
rootComponent: openmrsComponentDecorator(options)(Component)
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function getAsyncLifecycle(lazy, options) {
|
|
13
|
+
return ()=>lazy().then(({ default: Component })=>getLifecycle(Component, options));
|
|
14
|
+
}
|
|
15
|
+
export function getSyncLifecycle(Component, options) {
|
|
16
|
+
return ()=>Promise.resolve(getLifecycle(Component, options));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Use getAsyncLifecycle instead.
|
|
20
|
+
*/ export const getAsyncExtensionLifecycle = getAsyncLifecycle;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export * from './ComponentContext';
|
|
2
|
+
export * from './ConfigurableLink';
|
|
3
|
+
export * from './Extension';
|
|
4
|
+
export * from './ExtensionSlot';
|
|
5
|
+
export * from './OpenmrsContext';
|
|
6
|
+
export * from './RenderIfValueIsTruthy';
|
|
7
|
+
export * from './UserHasAccess';
|
|
8
|
+
export * from './getLifecycle';
|
|
9
|
+
export * from './openmrsComponentDecorator';
|
|
10
|
+
export * from './useAbortController';
|
|
11
|
+
export * from './useAppContext';
|
|
12
|
+
export * from './useAssignedExtensionIds';
|
|
13
|
+
export * from './useAssignedExtensions';
|
|
14
|
+
export * from './useAttachments';
|
|
15
|
+
export * from './useBodyScrollLock';
|
|
16
|
+
export * from './useConfig';
|
|
17
|
+
export * from './useConnectedExtensions';
|
|
18
|
+
export * from './useConnectivity';
|
|
19
|
+
export * from './useDebounce';
|
|
20
|
+
export * from './useDefineAppContext';
|
|
21
|
+
export * from './useEmrConfiguration';
|
|
22
|
+
export * from './useExtensionInternalStore';
|
|
23
|
+
export * from './useExtensionSlot';
|
|
24
|
+
export * from './useExtensionSlotMeta';
|
|
25
|
+
export * from './useExtensionSlotStore';
|
|
26
|
+
export * from './useExtensionStore';
|
|
27
|
+
export * from './useFeatureFlag';
|
|
28
|
+
export * from './useForceUpdate';
|
|
29
|
+
export * from './useFhirFetchAll';
|
|
30
|
+
export * from './useFhirInfinite';
|
|
31
|
+
export * from './useFhirPagination';
|
|
32
|
+
export * from './useLayoutType';
|
|
33
|
+
export * from './useLocations';
|
|
34
|
+
export * from './useOnClickOutside';
|
|
35
|
+
export * from './useOnVisible';
|
|
36
|
+
export * from './useOpenmrsSWR';
|
|
37
|
+
export * from './usePagination';
|
|
38
|
+
export * from './usePatient';
|
|
39
|
+
export * from './usePrimaryIdentifierResource';
|
|
40
|
+
export * from './useRenderableExtensions';
|
|
41
|
+
export * from './useStore';
|
|
42
|
+
export * from './useVisit';
|
|
43
|
+
export * from './useVisitContextStore';
|
|
44
|
+
export * from './useVisitTypes';
|
|
45
|
+
export { useOpenmrsFetchAll, type UseServerFetchAllOptions } from './useOpenmrsFetchAll';
|
|
46
|
+
export { useOpenmrsInfinite, type UseServerInfiniteOptions } from './useOpenmrsInfinite';
|
|
47
|
+
export { useOpenmrsPagination, type UseServerPaginationOptions } from './useOpenmrsPagination';
|
|
48
|
+
export { useSession } from './useSession';
|