@openmrs/esm-extensions 8.0.1-pre.3439 → 8.0.1-pre.3457
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/.turbo/turbo-build.log +1 -1
- package/dist/extensions.d.ts +11 -2
- package/dist/extensions.js +55 -23
- package/dist/left-nav.d.ts +4 -1
- package/dist/left-nav.js +3 -2
- package/dist/store.d.ts +4 -0
- package/dist/store.js +1 -0
- package/package.json +7 -7
- package/src/extensions.ts +76 -24
- package/src/left-nav.ts +5 -2
- package/src/store.ts +6 -0
package/.turbo/turbo-build.log
CHANGED
package/dist/extensions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @module @category Extension */
|
|
2
|
-
import { type AssignedExtension, type ExtensionRegistration, type
|
|
2
|
+
import { type AssignedExtension, type ExtensionInternalStore, type ExtensionRegistration, type ExtensionSlotCustomState } from './store';
|
|
3
3
|
/**
|
|
4
4
|
* Given an extension ID, which is a string uniquely identifying
|
|
5
5
|
* an instance of an extension within an extension slot, this
|
|
@@ -63,9 +63,18 @@ export declare function getAssignedExtensions(slotName: string): Array<AssignedE
|
|
|
63
63
|
*
|
|
64
64
|
* @param moduleName The name of the module that contains the extension slot
|
|
65
65
|
* @param slotName The extension slot name that is actually used
|
|
66
|
+
* @param state Optional custom state for the slot, which will be stored in the extension store.
|
|
66
67
|
* @internal
|
|
67
68
|
*/
|
|
68
|
-
export declare const registerExtensionSlot: (moduleName: string, slotName: string) => void;
|
|
69
|
+
export declare const registerExtensionSlot: (moduleName: string, slotName: string, state?: ExtensionSlotCustomState) => void;
|
|
70
|
+
/**
|
|
71
|
+
* Used by extension slots to update the copy of the state for the extension slot
|
|
72
|
+
*
|
|
73
|
+
* @param slotName The name of the slot with state to update
|
|
74
|
+
* @param state A copy of the new state
|
|
75
|
+
* @param partial Whether this should be applied as a partial
|
|
76
|
+
*/
|
|
77
|
+
export declare function updateExtensionSlotState(slotName: string, state: ExtensionSlotCustomState, partial?: boolean): void;
|
|
69
78
|
/**
|
|
70
79
|
* @internal
|
|
71
80
|
* Just for testing.
|
package/dist/extensions.js
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* - assigned (computed from attached and configured)
|
|
7
7
|
* - connected (computed from assigned using connectivity and online / offline)
|
|
8
8
|
*/ import { sessionStore, userHasAccess } from "@openmrs/esm-api";
|
|
9
|
-
import {
|
|
9
|
+
import { getExtensionConfigFromExtensionSlotStore, getExtensionConfigFromStore, getExtensionSlotConfig, getExtensionSlotConfigFromStore, getExtensionSlotsConfigStore, getExtensionsConfigStore } from "@openmrs/esm-config";
|
|
10
10
|
import { evaluateAsBoolean } from "@openmrs/esm-expression-evaluator";
|
|
11
11
|
import { featureFlagsStore } from "@openmrs/esm-feature-flags";
|
|
12
12
|
import { subscribeConnectivityChanged } from "@openmrs/esm-globals";
|
|
13
13
|
import { isOnline as isOnlineFn } from "@openmrs/esm-utils";
|
|
14
14
|
import { isEqual, merge } from "lodash-es";
|
|
15
15
|
import { checkStatusFor } from "./helpers.js";
|
|
16
|
-
import {
|
|
16
|
+
import { getExtensionInternalStore, getExtensionStore, updateInternalExtensionStore } from "./store.js";
|
|
17
17
|
const extensionInternalStore = getExtensionInternalStore();
|
|
18
18
|
const extensionStore = getExtensionStore();
|
|
19
19
|
const slotsConfigStore = getExtensionSlotsConfigStore();
|
|
@@ -57,12 +57,13 @@ function updateOutputStoreToCurrent() {
|
|
|
57
57
|
}
|
|
58
58
|
updateOutputStoreToCurrent();
|
|
59
59
|
subscribeConnectivityChanged(updateOutputStoreToCurrent);
|
|
60
|
-
function createNewExtensionSlotInfo(slotName, moduleName) {
|
|
60
|
+
function createNewExtensionSlotInfo(slotName, moduleName, state) {
|
|
61
61
|
return {
|
|
62
62
|
moduleName,
|
|
63
63
|
name: slotName,
|
|
64
64
|
attachedIds: [],
|
|
65
|
-
config: null
|
|
65
|
+
config: null,
|
|
66
|
+
state
|
|
66
67
|
};
|
|
67
68
|
}
|
|
68
69
|
/**
|
|
@@ -220,6 +221,14 @@ function getAssignedExtensionsFromSlotData(slotName, internalState, config, exte
|
|
|
220
221
|
const attachedIds = internalState.slots[slotName].attachedIds;
|
|
221
222
|
const assignedIds = calculateAssignedIds(config, attachedIds);
|
|
222
223
|
const extensions = [];
|
|
224
|
+
// Create context once for all extensions in this slot
|
|
225
|
+
const slotState = internalState.slots[slotName]?.state;
|
|
226
|
+
const expressionContext = slotState && typeof slotState === 'object' ? {
|
|
227
|
+
session,
|
|
228
|
+
...slotState
|
|
229
|
+
} : {
|
|
230
|
+
session
|
|
231
|
+
};
|
|
223
232
|
for (let id of assignedIds){
|
|
224
233
|
const { config: rawExtensionConfig } = getExtensionConfigFromStore(extensionConfigStoreState, slotName, id);
|
|
225
234
|
const rawExtensionSlotExtensionConfig = getExtensionConfigFromExtensionSlotStore(config, slotName, id);
|
|
@@ -237,16 +246,14 @@ function getAssignedExtensionsFromSlotData(slotName, internalState, config, exte
|
|
|
237
246
|
continue;
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
|
-
const displayConditionExpression = extensionConfig?.['Display conditions']?.expression
|
|
241
|
-
if (displayConditionExpression !==
|
|
249
|
+
const displayConditionExpression = extensionConfig?.['Display conditions']?.expression || extension.displayExpression;
|
|
250
|
+
if (displayConditionExpression !== undefined && typeof displayConditionExpression === 'string' && displayConditionExpression.trim().length > 0) {
|
|
242
251
|
try {
|
|
243
|
-
if (!evaluateAsBoolean(displayConditionExpression, {
|
|
244
|
-
session
|
|
245
|
-
})) {
|
|
252
|
+
if (!evaluateAsBoolean(displayConditionExpression, expressionContext)) {
|
|
246
253
|
continue;
|
|
247
254
|
}
|
|
248
255
|
} catch (e) {
|
|
249
|
-
console.error(`Error while evaluating expression ${displayConditionExpression}`, e);
|
|
256
|
+
console.error(`Error while evaluating expression '${displayConditionExpression}' for extension ${name} in slot ${slotName}`, e);
|
|
250
257
|
continue;
|
|
251
258
|
}
|
|
252
259
|
}
|
|
@@ -310,38 +317,63 @@ function calculateAssignedIds(config, attachedIds) {
|
|
|
310
317
|
*
|
|
311
318
|
* @param moduleName The name of the module that contains the extension slot
|
|
312
319
|
* @param slotName The extension slot name that is actually used
|
|
320
|
+
* @param state Optional custom state for the slot, which will be stored in the extension store.
|
|
313
321
|
* @internal
|
|
314
|
-
*/ export const registerExtensionSlot = (moduleName, slotName)=>extensionInternalStore.setState((
|
|
315
|
-
const existingModuleName =
|
|
322
|
+
*/ export const registerExtensionSlot = (moduleName, slotName, state)=>extensionInternalStore.setState((currentState)=>{
|
|
323
|
+
const existingModuleName = currentState.slots[slotName]?.moduleName;
|
|
316
324
|
if (existingModuleName && existingModuleName != moduleName) {
|
|
317
325
|
console.warn(`An extension slot with the name '${slotName}' already exists. Refusing to register the same slot name twice (in "registerExtensionSlot"). The existing one is from module ${existingModuleName}.`);
|
|
318
|
-
return
|
|
326
|
+
return currentState;
|
|
319
327
|
}
|
|
320
328
|
if (existingModuleName && existingModuleName == moduleName) {
|
|
321
329
|
// Re-rendering an existing slot
|
|
322
|
-
return
|
|
330
|
+
return currentState;
|
|
323
331
|
}
|
|
324
|
-
if (
|
|
332
|
+
if (currentState.slots[slotName]) {
|
|
325
333
|
return {
|
|
326
|
-
...
|
|
334
|
+
...currentState,
|
|
327
335
|
slots: {
|
|
328
|
-
...
|
|
336
|
+
...currentState.slots,
|
|
329
337
|
[slotName]: {
|
|
330
|
-
...
|
|
331
|
-
moduleName
|
|
338
|
+
...currentState.slots[slotName],
|
|
339
|
+
moduleName,
|
|
340
|
+
state
|
|
332
341
|
}
|
|
333
342
|
}
|
|
334
343
|
};
|
|
335
344
|
}
|
|
336
|
-
const slot = createNewExtensionSlotInfo(slotName, moduleName);
|
|
345
|
+
const slot = createNewExtensionSlotInfo(slotName, moduleName, state);
|
|
337
346
|
return {
|
|
338
|
-
...
|
|
347
|
+
...currentState,
|
|
339
348
|
slots: {
|
|
340
|
-
...
|
|
341
|
-
[slotName]:
|
|
349
|
+
...currentState.slots,
|
|
350
|
+
[slotName]: {
|
|
351
|
+
...slot
|
|
352
|
+
}
|
|
342
353
|
}
|
|
343
354
|
};
|
|
344
355
|
});
|
|
356
|
+
/**
|
|
357
|
+
* Used by extension slots to update the copy of the state for the extension slot
|
|
358
|
+
*
|
|
359
|
+
* @param slotName The name of the slot with state to update
|
|
360
|
+
* @param state A copy of the new state
|
|
361
|
+
* @param partial Whether this should be applied as a partial
|
|
362
|
+
*/ export function updateExtensionSlotState(slotName, state, partial = false) {
|
|
363
|
+
extensionInternalStore.setState((currentState)=>{
|
|
364
|
+
const newState = partial ? merge(currentState.slots[slotName].state, state) : state;
|
|
365
|
+
return {
|
|
366
|
+
...currentState,
|
|
367
|
+
slots: {
|
|
368
|
+
...currentState.slots,
|
|
369
|
+
[slotName]: {
|
|
370
|
+
...currentState.slots[slotName],
|
|
371
|
+
state: newState
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
});
|
|
376
|
+
}
|
|
345
377
|
/**
|
|
346
378
|
* @internal
|
|
347
379
|
* Just for testing.
|
package/dist/left-nav.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ComponentConfig } from './types';
|
|
2
|
+
import { type ExtensionSlotState } from './store';
|
|
2
3
|
type LeftNavMode = 'normal' | 'collapsed' | 'hidden';
|
|
3
4
|
export interface LeftNavStore {
|
|
4
5
|
slotName: string | null;
|
|
5
6
|
basePath: string;
|
|
6
7
|
mode: LeftNavMode;
|
|
7
8
|
componentContext?: ComponentConfig;
|
|
9
|
+
state?: ExtensionSlotState;
|
|
8
10
|
}
|
|
9
11
|
/** @internal */
|
|
10
12
|
export declare const leftNavStore: import("zustand").StoreApi<LeftNavStore>;
|
|
@@ -18,13 +20,14 @@ export interface SetLeftNavParams {
|
|
|
18
20
|
*/
|
|
19
21
|
mode?: LeftNavMode;
|
|
20
22
|
componentContext?: ComponentConfig;
|
|
23
|
+
state?: ExtensionSlotState;
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
23
26
|
* Sets the current left nav context. Must be paired with {@link unsetLeftNav}.
|
|
24
27
|
*
|
|
25
28
|
* @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
|
|
26
29
|
*/
|
|
27
|
-
export declare function setLeftNav({ name, basePath, mode, componentContext }: SetLeftNavParams): void;
|
|
30
|
+
export declare function setLeftNav({ name, basePath, mode, componentContext, state }: SetLeftNavParams): void;
|
|
28
31
|
/**
|
|
29
32
|
* Unsets the left nav context if the current context is for the supplied name.
|
|
30
33
|
*
|
package/dist/left-nav.js
CHANGED
|
@@ -8,12 +8,13 @@ import { createGlobalStore } from "@openmrs/esm-state";
|
|
|
8
8
|
* Sets the current left nav context. Must be paired with {@link unsetLeftNav}.
|
|
9
9
|
*
|
|
10
10
|
* @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
|
|
11
|
-
*/ export function setLeftNav({ name, basePath, mode, componentContext }) {
|
|
11
|
+
*/ export function setLeftNav({ name, basePath, mode, componentContext, state }) {
|
|
12
12
|
leftNavStore.setState({
|
|
13
13
|
slotName: name,
|
|
14
14
|
basePath,
|
|
15
15
|
mode: mode ?? 'normal',
|
|
16
|
-
componentContext
|
|
16
|
+
componentContext,
|
|
17
|
+
state
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
/**
|
package/dist/store.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface ExtensionRegistration {
|
|
|
13
13
|
readonly offline?: boolean;
|
|
14
14
|
readonly privileges?: string | Array<string>;
|
|
15
15
|
readonly featureFlag?: string;
|
|
16
|
+
readonly displayExpression?: string;
|
|
16
17
|
}
|
|
17
18
|
export interface ExtensionInfo extends ExtensionRegistration {
|
|
18
19
|
/**
|
|
@@ -31,6 +32,7 @@ export interface ExtensionInternalStore {
|
|
|
31
32
|
/** Extensions indexed by name */
|
|
32
33
|
extensions: Record<string, ExtensionInfo>;
|
|
33
34
|
}
|
|
35
|
+
export type ExtensionSlotCustomState = Record<string | symbol | number, unknown> | undefined | null;
|
|
34
36
|
export interface ExtensionSlotInfo {
|
|
35
37
|
/**
|
|
36
38
|
* The module in which the extension slot exists. Undefined if the slot
|
|
@@ -48,6 +50,7 @@ export interface ExtensionSlotInfo {
|
|
|
48
50
|
attachedIds: Array<string>;
|
|
49
51
|
/** The configuration provided for this slot. `null` if not yet loaded. */
|
|
50
52
|
config: Omit<ExtensionSlotConfig, 'configuration'> | null;
|
|
53
|
+
state?: ExtensionSlotCustomState;
|
|
51
54
|
}
|
|
52
55
|
export interface ExtensionStore {
|
|
53
56
|
slots: Record<string, ExtensionSlotState>;
|
|
@@ -55,6 +58,7 @@ export interface ExtensionStore {
|
|
|
55
58
|
export interface ExtensionSlotState {
|
|
56
59
|
moduleName?: string;
|
|
57
60
|
assignedExtensions: Array<AssignedExtension>;
|
|
61
|
+
state?: ExtensionSlotCustomState;
|
|
58
62
|
}
|
|
59
63
|
export interface AssignedExtension {
|
|
60
64
|
readonly id: string;
|
package/dist/store.js
CHANGED
|
@@ -15,6 +15,7 @@ const extensionInternalStore = createGlobalStore('extensionsInternal', {
|
|
|
15
15
|
extensions: {}
|
|
16
16
|
});
|
|
17
17
|
/** @internal */ export function updateInternalExtensionStore(updater) {
|
|
18
|
+
// This is a function that updates the internal extension store.
|
|
18
19
|
const state = extensionInternalStore.getState();
|
|
19
20
|
const newState = updater(state);
|
|
20
21
|
if (newState !== state) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-extensions",
|
|
3
|
-
"version": "8.0.1-pre.
|
|
3
|
+
"version": "8.0.1-pre.3457",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Coordinates extensions and extension points in the OpenMRS Frontend",
|
|
6
6
|
"type": "module",
|
|
@@ -64,12 +64,12 @@
|
|
|
64
64
|
"single-spa": "6.x"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@openmrs/esm-api": "8.0.1-pre.
|
|
68
|
-
"@openmrs/esm-config": "8.0.1-pre.
|
|
69
|
-
"@openmrs/esm-expression-evaluator": "8.0.1-pre.
|
|
70
|
-
"@openmrs/esm-feature-flags": "8.0.1-pre.
|
|
71
|
-
"@openmrs/esm-state": "8.0.1-pre.
|
|
72
|
-
"@openmrs/esm-utils": "8.0.1-pre.
|
|
67
|
+
"@openmrs/esm-api": "8.0.1-pre.3457",
|
|
68
|
+
"@openmrs/esm-config": "8.0.1-pre.3457",
|
|
69
|
+
"@openmrs/esm-expression-evaluator": "8.0.1-pre.3457",
|
|
70
|
+
"@openmrs/esm-feature-flags": "8.0.1-pre.3457",
|
|
71
|
+
"@openmrs/esm-state": "8.0.1-pre.3457",
|
|
72
|
+
"@openmrs/esm-utils": "8.0.1-pre.3457",
|
|
73
73
|
"@swc/cli": "^0.7.7",
|
|
74
74
|
"@swc/core": "^1.11.29",
|
|
75
75
|
"concurrently": "^9.1.2",
|
package/src/extensions.ts
CHANGED
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
import { type Session, type SessionStore, sessionStore, userHasAccess } from '@openmrs/esm-api';
|
|
12
12
|
import {
|
|
13
|
-
type ExtensionsConfigStore,
|
|
14
13
|
type ExtensionSlotConfig,
|
|
15
14
|
type ExtensionSlotsConfigStore,
|
|
15
|
+
type ExtensionsConfigStore,
|
|
16
|
+
getExtensionConfigFromExtensionSlotStore,
|
|
16
17
|
getExtensionConfigFromStore,
|
|
17
|
-
getExtensionsConfigStore,
|
|
18
18
|
getExtensionSlotConfig,
|
|
19
|
-
getExtensionConfigFromExtensionSlotStore,
|
|
20
19
|
getExtensionSlotConfigFromStore,
|
|
21
20
|
getExtensionSlotsConfigStore,
|
|
21
|
+
getExtensionsConfigStore,
|
|
22
22
|
} from '@openmrs/esm-config';
|
|
23
23
|
import { evaluateAsBoolean } from '@openmrs/esm-expression-evaluator';
|
|
24
24
|
import { type FeatureFlagsStore, featureFlagsStore } from '@openmrs/esm-feature-flags';
|
|
@@ -28,12 +28,13 @@ import { isEqual, merge } from 'lodash-es';
|
|
|
28
28
|
import { checkStatusFor } from './helpers';
|
|
29
29
|
import {
|
|
30
30
|
type AssignedExtension,
|
|
31
|
+
type ExtensionInternalStore,
|
|
31
32
|
type ExtensionRegistration,
|
|
33
|
+
type ExtensionSlotCustomState,
|
|
32
34
|
type ExtensionSlotInfo,
|
|
33
|
-
type ExtensionInternalStore,
|
|
34
35
|
type ExtensionSlotState,
|
|
35
|
-
getExtensionStore,
|
|
36
36
|
getExtensionInternalStore,
|
|
37
|
+
getExtensionStore,
|
|
37
38
|
updateInternalExtensionStore,
|
|
38
39
|
} from './store';
|
|
39
40
|
|
|
@@ -139,12 +140,17 @@ function updateOutputStoreToCurrent() {
|
|
|
139
140
|
updateOutputStoreToCurrent();
|
|
140
141
|
subscribeConnectivityChanged(updateOutputStoreToCurrent);
|
|
141
142
|
|
|
142
|
-
function createNewExtensionSlotInfo(
|
|
143
|
+
function createNewExtensionSlotInfo(
|
|
144
|
+
slotName: string,
|
|
145
|
+
moduleName?: string,
|
|
146
|
+
state?: ExtensionSlotCustomState,
|
|
147
|
+
): ExtensionSlotInfo {
|
|
143
148
|
return {
|
|
144
149
|
moduleName,
|
|
145
150
|
name: slotName,
|
|
146
151
|
attachedIds: [],
|
|
147
152
|
config: null,
|
|
153
|
+
state,
|
|
148
154
|
};
|
|
149
155
|
}
|
|
150
156
|
|
|
@@ -333,6 +339,10 @@ function getAssignedExtensionsFromSlotData(
|
|
|
333
339
|
const assignedIds = calculateAssignedIds(config, attachedIds);
|
|
334
340
|
const extensions: Array<AssignedExtension> = [];
|
|
335
341
|
|
|
342
|
+
// Create context once for all extensions in this slot
|
|
343
|
+
const slotState = internalState.slots[slotName]?.state;
|
|
344
|
+
const expressionContext = slotState && typeof slotState === 'object' ? { session, ...slotState } : { session };
|
|
345
|
+
|
|
336
346
|
for (let id of assignedIds) {
|
|
337
347
|
const { config: rawExtensionConfig } = getExtensionConfigFromStore(extensionConfigStoreState, slotName, id);
|
|
338
348
|
const rawExtensionSlotExtensionConfig = getExtensionConfigFromExtensionSlotStore(config, slotName, id);
|
|
@@ -357,15 +367,23 @@ function getAssignedExtensionsFromSlotData(
|
|
|
357
367
|
}
|
|
358
368
|
}
|
|
359
369
|
|
|
360
|
-
const displayConditionExpression =
|
|
361
|
-
|
|
370
|
+
const displayConditionExpression =
|
|
371
|
+
extensionConfig?.['Display conditions']?.expression || extension.displayExpression;
|
|
372
|
+
|
|
373
|
+
if (
|
|
374
|
+
displayConditionExpression !== undefined &&
|
|
375
|
+
typeof displayConditionExpression === 'string' &&
|
|
376
|
+
displayConditionExpression.trim().length > 0
|
|
377
|
+
) {
|
|
362
378
|
try {
|
|
363
|
-
if (!evaluateAsBoolean(displayConditionExpression,
|
|
379
|
+
if (!evaluateAsBoolean(displayConditionExpression, expressionContext)) {
|
|
364
380
|
continue;
|
|
365
381
|
}
|
|
366
382
|
} catch (e) {
|
|
367
|
-
console.error(
|
|
368
|
-
|
|
383
|
+
console.error(
|
|
384
|
+
`Error while evaluating expression '${displayConditionExpression}' for extension ${name} in slot ${slotName}`,
|
|
385
|
+
e,
|
|
386
|
+
);
|
|
369
387
|
continue;
|
|
370
388
|
}
|
|
371
389
|
}
|
|
@@ -449,43 +467,77 @@ function calculateAssignedIds(config: ExtensionSlotConfig, attachedIds: Array<st
|
|
|
449
467
|
*
|
|
450
468
|
* @param moduleName The name of the module that contains the extension slot
|
|
451
469
|
* @param slotName The extension slot name that is actually used
|
|
470
|
+
* @param state Optional custom state for the slot, which will be stored in the extension store.
|
|
452
471
|
* @internal
|
|
453
472
|
*/
|
|
454
|
-
export const registerExtensionSlot: (moduleName: string, slotName: string) => void = (
|
|
455
|
-
|
|
456
|
-
|
|
473
|
+
export const registerExtensionSlot: (moduleName: string, slotName: string, state?: ExtensionSlotCustomState) => void = (
|
|
474
|
+
moduleName,
|
|
475
|
+
slotName,
|
|
476
|
+
state,
|
|
477
|
+
) =>
|
|
478
|
+
extensionInternalStore.setState((currentState) => {
|
|
479
|
+
const existingModuleName = currentState.slots[slotName]?.moduleName;
|
|
457
480
|
if (existingModuleName && existingModuleName != moduleName) {
|
|
458
481
|
console.warn(
|
|
459
482
|
`An extension slot with the name '${slotName}' already exists. Refusing to register the same slot name twice (in "registerExtensionSlot"). The existing one is from module ${existingModuleName}.`,
|
|
460
483
|
);
|
|
461
|
-
return
|
|
484
|
+
return currentState;
|
|
462
485
|
}
|
|
486
|
+
|
|
463
487
|
if (existingModuleName && existingModuleName == moduleName) {
|
|
464
488
|
// Re-rendering an existing slot
|
|
465
|
-
return
|
|
489
|
+
return currentState;
|
|
466
490
|
}
|
|
467
|
-
|
|
491
|
+
|
|
492
|
+
if (currentState.slots[slotName]) {
|
|
468
493
|
return {
|
|
469
|
-
...
|
|
494
|
+
...currentState,
|
|
470
495
|
slots: {
|
|
471
|
-
...
|
|
496
|
+
...currentState.slots,
|
|
472
497
|
[slotName]: {
|
|
473
|
-
...
|
|
498
|
+
...currentState.slots[slotName],
|
|
474
499
|
moduleName,
|
|
500
|
+
state,
|
|
475
501
|
},
|
|
476
502
|
},
|
|
477
503
|
};
|
|
478
504
|
}
|
|
479
|
-
|
|
505
|
+
|
|
506
|
+
const slot = createNewExtensionSlotInfo(slotName, moduleName, state);
|
|
480
507
|
return {
|
|
481
|
-
...
|
|
508
|
+
...currentState,
|
|
482
509
|
slots: {
|
|
483
|
-
...
|
|
484
|
-
[slotName]:
|
|
510
|
+
...currentState.slots,
|
|
511
|
+
[slotName]: {
|
|
512
|
+
...slot,
|
|
513
|
+
},
|
|
485
514
|
},
|
|
486
515
|
};
|
|
487
516
|
});
|
|
488
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Used by extension slots to update the copy of the state for the extension slot
|
|
520
|
+
*
|
|
521
|
+
* @param slotName The name of the slot with state to update
|
|
522
|
+
* @param state A copy of the new state
|
|
523
|
+
* @param partial Whether this should be applied as a partial
|
|
524
|
+
*/
|
|
525
|
+
export function updateExtensionSlotState(slotName: string, state: ExtensionSlotCustomState, partial: boolean = false) {
|
|
526
|
+
extensionInternalStore.setState((currentState) => {
|
|
527
|
+
const newState = partial ? merge(currentState.slots[slotName].state, state) : state;
|
|
528
|
+
return {
|
|
529
|
+
...currentState,
|
|
530
|
+
slots: {
|
|
531
|
+
...currentState.slots,
|
|
532
|
+
[slotName]: {
|
|
533
|
+
...currentState.slots[slotName],
|
|
534
|
+
state: newState,
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
};
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
489
541
|
/**
|
|
490
542
|
* @internal
|
|
491
543
|
* Just for testing.
|
package/src/left-nav.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {} from '@openmrs/esm-globals';
|
|
2
2
|
import { createGlobalStore } from '@openmrs/esm-state';
|
|
3
3
|
import { type ComponentConfig } from './types';
|
|
4
|
+
import { type ExtensionSlotState } from './store';
|
|
4
5
|
|
|
5
6
|
type LeftNavMode = 'normal' | 'collapsed' | 'hidden';
|
|
6
7
|
export interface LeftNavStore {
|
|
@@ -8,6 +9,7 @@ export interface LeftNavStore {
|
|
|
8
9
|
basePath: string;
|
|
9
10
|
mode: LeftNavMode;
|
|
10
11
|
componentContext?: ComponentConfig;
|
|
12
|
+
state?: ExtensionSlotState;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/** @internal */
|
|
@@ -27,6 +29,7 @@ export interface SetLeftNavParams {
|
|
|
27
29
|
*/
|
|
28
30
|
mode?: LeftNavMode;
|
|
29
31
|
componentContext?: ComponentConfig;
|
|
32
|
+
state?: ExtensionSlotState;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -34,8 +37,8 @@ export interface SetLeftNavParams {
|
|
|
34
37
|
*
|
|
35
38
|
* @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
|
|
36
39
|
*/
|
|
37
|
-
export function setLeftNav({ name, basePath, mode, componentContext }: SetLeftNavParams) {
|
|
38
|
-
leftNavStore.setState({ slotName: name, basePath, mode: mode ?? 'normal', componentContext });
|
|
40
|
+
export function setLeftNav({ name, basePath, mode, componentContext, state }: SetLeftNavParams) {
|
|
41
|
+
leftNavStore.setState({ slotName: name, basePath, mode: mode ?? 'normal', componentContext, state });
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
package/src/store.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface ExtensionRegistration {
|
|
|
19
19
|
readonly offline?: boolean;
|
|
20
20
|
readonly privileges?: string | Array<string>;
|
|
21
21
|
readonly featureFlag?: string;
|
|
22
|
+
readonly displayExpression?: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export interface ExtensionInfo extends ExtensionRegistration {
|
|
@@ -41,6 +42,8 @@ export interface ExtensionInternalStore {
|
|
|
41
42
|
extensions: Record<string, ExtensionInfo>;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
export type ExtensionSlotCustomState = Record<string | symbol | number, unknown> | undefined | null;
|
|
46
|
+
|
|
44
47
|
export interface ExtensionSlotInfo {
|
|
45
48
|
/**
|
|
46
49
|
* The module in which the extension slot exists. Undefined if the slot
|
|
@@ -58,6 +61,7 @@ export interface ExtensionSlotInfo {
|
|
|
58
61
|
attachedIds: Array<string>;
|
|
59
62
|
/** The configuration provided for this slot. `null` if not yet loaded. */
|
|
60
63
|
config: Omit<ExtensionSlotConfig, 'configuration'> | null;
|
|
64
|
+
state?: ExtensionSlotCustomState;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export interface ExtensionStore {
|
|
@@ -67,6 +71,7 @@ export interface ExtensionStore {
|
|
|
67
71
|
export interface ExtensionSlotState {
|
|
68
72
|
moduleName?: string;
|
|
69
73
|
assignedExtensions: Array<AssignedExtension>;
|
|
74
|
+
state?: ExtensionSlotCustomState;
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
export interface AssignedExtension {
|
|
@@ -110,6 +115,7 @@ export const getExtensionInternalStore = () =>
|
|
|
110
115
|
|
|
111
116
|
/** @internal */
|
|
112
117
|
export function updateInternalExtensionStore(updater: (state: ExtensionInternalStore) => ExtensionInternalStore) {
|
|
118
|
+
// This is a function that updates the internal extension store.
|
|
113
119
|
const state = extensionInternalStore.getState();
|
|
114
120
|
const newState = updater(state);
|
|
115
121
|
|