@aehrc/smart-forms-renderer 1.2.0 → 1.2.2
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/lib/components/FormComponents/ItemParts/FadingCheckIcon.d.ts +7 -0
- package/lib/components/FormComponents/ItemParts/FadingCheckIcon.js +26 -0
- package/lib/components/FormComponents/ItemParts/FadingCheckIcon.js.map +1 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelText.d.ts +8 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelText.js +63 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelText.js.map +1 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.d.ts +8 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js +53 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js.map +1 -0
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js +4 -3
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/DeleteItemButton.d.ts +10 -0
- package/lib/components/FormComponents/RepeatGroup/DeleteItemButton.js +30 -0
- package/lib/components/FormComponents/RepeatGroup/DeleteItemButton.js.map +1 -0
- package/lib/components/FormComponents/SingleItem/SingleItem.js +15 -2
- package/lib/components/FormComponents/SingleItem/SingleItem.js.map +1 -1
- package/lib/components/FormComponents/SingleItem/SingleNestedItems.js +8 -8
- package/lib/components/FormComponents/SingleItem/SingleNestedItems.js.map +1 -1
- package/lib/components/Iconify/Iconify.d.ts +10 -0
- package/lib/components/Iconify/Iconify.js +26 -0
- package/lib/components/Iconify/Iconify.js.map +1 -0
- package/lib/components/Renderer/FormBodyPage.d.ts +9 -0
- package/lib/components/Renderer/FormBodyPage.js +43 -0
- package/lib/components/Renderer/FormBodyPage.js.map +1 -0
- package/lib/components/Renderer/FormTitle.d.ts +7 -0
- package/lib/components/Renderer/FormTitle.js +30 -0
- package/lib/components/Renderer/FormTitle.js.map +1 -0
- package/lib/components/Renderer/FormTopLevelPage.d.ts +9 -0
- package/lib/components/Renderer/FormTopLevelPage.js +29 -0
- package/lib/components/Renderer/FormTopLevelPage.js.map +1 -0
- package/lib/components/Tabs/FormBodyTabListWrapper.js +1 -1
- package/lib/components/Tabs/FormBodyTabListWrapper.js.map +1 -1
- package/lib/hooks/useBooleanCalculatedExpression.d.ts +12 -0
- package/lib/hooks/useBooleanCalculatedExpression.js +53 -0
- package/lib/hooks/useBooleanCalculatedExpression.js.map +1 -0
- package/lib/hooks/useDecimalCalculatedExpression.d.ts +13 -0
- package/lib/hooks/useDecimalCalculatedExpression.js +59 -0
- package/lib/hooks/useDecimalCalculatedExpression.js.map +1 -0
- package/lib/hooks/useIntegerCalculatedExpression.d.ts +12 -0
- package/lib/hooks/useIntegerCalculatedExpression.js +56 -0
- package/lib/hooks/useIntegerCalculatedExpression.js.map +1 -0
- package/lib/hooks/useQuantityCalculatedExpression.d.ts +14 -0
- package/lib/hooks/useQuantityCalculatedExpression.js +107 -0
- package/lib/hooks/useQuantityCalculatedExpression.js.map +1 -0
- package/lib/hooks/useStringCalculatedExpression.d.ts +12 -0
- package/lib/hooks/useStringCalculatedExpression.js +58 -0
- package/lib/hooks/useStringCalculatedExpression.js.map +1 -0
- package/lib/stores/rendererConfigStore.d.ts +1 -1
- package/lib/stories/storybookWrappers/InitialiseFormWrapperForStorybook.d.ts +3 -2
- package/lib/stories/storybookWrappers/InitialiseFormWrapperForStorybook.js +27 -6
- package/lib/stories/storybookWrappers/InitialiseFormWrapperForStorybook.js.map +1 -1
- package/lib/stories/storybookWrappers/index.js +1 -1
- package/lib/theme/Theme.d.ts +44 -0
- package/lib/theme/Theme.js +43 -0
- package/lib/theme/Theme.js.map +1 -0
- package/lib/theme/customGlobalStyles.d.ts +2 -0
- package/lib/theme/customGlobalStyles.js +61 -0
- package/lib/theme/customGlobalStyles.js.map +1 -0
- package/lib/theme/overrides/Accordion.d.ts +14 -0
- package/lib/theme/overrides/Accordion.js +32 -0
- package/lib/theme/overrides/Accordion.js.map +1 -0
- package/lib/theme/overrides/Autocomplete.d.ts +10 -0
- package/lib/theme/overrides/Autocomplete.js +28 -0
- package/lib/theme/overrides/Autocomplete.js.map +1 -0
- package/lib/theme/overrides/Button.d.ts +33 -0
- package/lib/theme/overrides/Button.js +52 -0
- package/lib/theme/overrides/Button.js.map +1 -0
- package/lib/theme/overrides/Card.d.ts +35 -0
- package/lib/theme/overrides/Card.js +49 -0
- package/lib/theme/overrides/Card.js.map +1 -0
- package/lib/theme/overrides/Input.d.ts +64 -0
- package/lib/theme/overrides/Input.js +81 -0
- package/lib/theme/overrides/Input.js.map +1 -0
- package/lib/theme/overrides/Overrides.d.ts +3 -0
- package/lib/theme/overrides/Overrides.js +29 -0
- package/lib/theme/overrides/Overrides.js.map +1 -0
- package/lib/theme/overrides/Paper.d.ts +12 -0
- package/lib/theme/overrides/Paper.js +31 -0
- package/lib/theme/overrides/Paper.js.map +1 -0
- package/lib/theme/overrides/SpeedDial.d.ts +16 -0
- package/lib/theme/overrides/SpeedDial.js +34 -0
- package/lib/theme/overrides/SpeedDial.js.map +1 -0
- package/lib/theme/overrides/Table.d.ts +12 -0
- package/lib/theme/overrides/Table.js +30 -0
- package/lib/theme/overrides/Table.js.map +1 -0
- package/lib/theme/palette.d.ts +27 -0
- package/lib/theme/palette.js +63 -0
- package/lib/theme/palette.js.map +1 -0
- package/lib/theme/shadows.d.ts +2 -0
- package/lib/theme/shadows.js +52 -0
- package/lib/theme/shadows.js.map +1 -0
- package/lib/utils/calculatedExpression.js +11 -0
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/dayjsExtend.d.ts +1 -0
- package/lib/utils/dayjsExtend.js +22 -0
- package/lib/utils/dayjsExtend.js.map +1 -0
- package/lib/utils/initialise.js +8 -1
- package/lib/utils/initialise.js.map +1 -1
- package/lib/utils/itemControl.d.ts +103 -0
- package/lib/utils/itemControl.js +350 -0
- package/lib/utils/itemControl.js.map +1 -0
- package/lib/utils/mapItem.d.ts +1 -1
- package/lib/utils/mapItem.js +2 -0
- package/lib/utils/mapItem.js.map +1 -1
- package/lib/utils/qrItem.d.ts +13 -0
- package/lib/utils/qrItem.js +76 -0
- package/lib/utils/qrItem.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/addAdditionalVariables.d.ts +2 -0
- package/lib/utils/questionnaireStoreUtils/addAdditionalVariables.js +43 -0
- package/lib/utils/questionnaireStoreUtils/addAdditionalVariables.js.map +1 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.d.ts +3 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js +101 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -0
- package/lib/utils/questionnaireStoreUtils/extractOtherExtensions.js +4 -2
- package/lib/utils/questionnaireStoreUtils/extractOtherExtensions.js.map +1 -1
- package/lib/utils/validateQuestionnaire.d.ts +66 -0
- package/lib/utils/validateQuestionnaire.js +559 -0
- package/lib/utils/validateQuestionnaire.js.map +1 -0
- package/package.json +1 -1
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx +4 -3
- package/src/components/FormComponents/SingleItem/SingleItem.tsx +16 -2
- package/src/components/FormComponents/SingleItem/SingleNestedItems.tsx +8 -9
- package/src/components/Tabs/FormBodyTabListWrapper.tsx +1 -1
- package/src/stores/rendererConfigStore.ts +1 -1
- package/src/utils/calculatedExpression.ts +11 -0
- package/src/utils/initialise.ts +8 -1
- package/src/utils/mapItem.ts +3 -1
- package/src/utils/qrItem.ts +91 -0
- package/src/utils/questionnaireStoreUtils/extractOtherExtensions.ts +4 -2
- package/lib/interfaces/itemPath.interface.d.ts +0 -31
- package/lib/interfaces/itemPath.interface.js +0 -2
- package/lib/interfaces/itemPath.interface.js.map +0 -1
- package/lib/utils/itemPath.d.ts +0 -57
- package/lib/utils/itemPath.js +0 -75
- package/lib/utils/itemPath.js.map +0 -1
|
@@ -19,12 +19,11 @@ import React, { useMemo } from 'react';
|
|
|
19
19
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
20
20
|
import { getQrItemsIndex, mapQItemsIndex } from '../../../utils/mapItem';
|
|
21
21
|
import GroupItemSwitcher from '../GroupItem/GroupItemSwitcher';
|
|
22
|
-
import {
|
|
22
|
+
import { createEmptyRepeatNestedItems, updateQrNestedItems } from '../../../utils/qrItem';
|
|
23
23
|
import type {
|
|
24
24
|
PropsWithParentIsReadOnlyAttribute,
|
|
25
25
|
PropsWithQrItemChangeHandler
|
|
26
26
|
} from '../../../interfaces/renderProps.interface';
|
|
27
|
-
import type { QrRepeatGroup } from '../../../interfaces/repeatGroup.interface';
|
|
28
27
|
import Box from '@mui/material/Box';
|
|
29
28
|
|
|
30
29
|
interface SingleNestedItemsProps
|
|
@@ -41,27 +40,27 @@ function SingleNestedItems(props: SingleNestedItemsProps) {
|
|
|
41
40
|
const qItemsIndexMap: Record<string, number> = useMemo(() => mapQItemsIndex(qItem), [qItem]);
|
|
42
41
|
|
|
43
42
|
const qItems = qItem.item;
|
|
44
|
-
const qrGroup = qrItem && qrItem.
|
|
45
|
-
const
|
|
43
|
+
const qrGroup = qrItem && qrItem.answer ? qrItem : createEmptyRepeatNestedItems(qItem);
|
|
44
|
+
const qrAnswers = qrGroup.answer;
|
|
46
45
|
|
|
47
46
|
// Event Handlers
|
|
48
47
|
function handleQrItemChange(newQrItem: QuestionnaireResponseItem) {
|
|
49
48
|
const updatedQrGroup: QuestionnaireResponseItem = { ...qrGroup };
|
|
50
|
-
|
|
49
|
+
updateQrNestedItems(newQrItem, updatedQrGroup, qItemsIndexMap);
|
|
51
50
|
onQrItemChange(updatedQrGroup);
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
function handleQrRepeatGroupChange(
|
|
53
|
+
function handleQrRepeatGroupChange() {
|
|
55
54
|
const updatedQrGroup: QuestionnaireResponseItem = { ...qrGroup };
|
|
56
|
-
|
|
55
|
+
updateQrNestedItems(null, updatedQrGroup, qItemsIndexMap);
|
|
57
56
|
onQrItemChange(updatedQrGroup);
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
if (!qItems || !
|
|
59
|
+
if (!qItems || !qrAnswers) {
|
|
61
60
|
return <>Unable to load group, something has gone terribly wrong.</>;
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
const qrItemsByIndex = getQrItemsIndex(qItems,
|
|
63
|
+
const qrItemsByIndex = getQrItemsIndex(qItems, qrAnswers[0]?.item, qItemsIndexMap);
|
|
65
64
|
|
|
66
65
|
// TODO - Add support for horizontal "row" layout
|
|
67
66
|
return (
|
|
@@ -45,7 +45,7 @@ const FormBodyTabListWrapper = memo(function FormBodyTabListWrapper(
|
|
|
45
45
|
return (
|
|
46
46
|
<Card sx={{ p: 0.75, mb: 2 }}>
|
|
47
47
|
<Box sx={{ flexGrow: 1 }}>
|
|
48
|
-
<aside aria-label="
|
|
48
|
+
<aside aria-label="Form sections">
|
|
49
49
|
<PrimarySelectableList
|
|
50
50
|
dense
|
|
51
51
|
disablePadding
|
|
@@ -74,7 +74,7 @@ import type { Breakpoints } from '@mui/material';
|
|
|
74
74
|
* @property disableTabButtons - If `true`, hides navigation buttons for tabs.
|
|
75
75
|
* - Default: `false`
|
|
76
76
|
*
|
|
77
|
-
* @property disableHeadingFocusOnTabSwitch - If `true`, the first heading
|
|
77
|
+
* @property disableHeadingFocusOnTabSwitch - If `true`, disables automatic focus on the first heading when switching tabs.
|
|
78
78
|
* - Default: `false`
|
|
79
79
|
*
|
|
80
80
|
* @property readOnlyVisualStyle - If `true`, item.readOnly will result in form fields having MUI disabled property and styles (recommended from usability perspective). If `false`, item.readOnly will result in form fields having HTML readonly property (less stable, but recommended from accessibility perspective).
|
|
@@ -705,6 +705,17 @@ function parseValueToAnswer(
|
|
|
705
705
|
if (matchingAnswer) {
|
|
706
706
|
return matchingAnswer;
|
|
707
707
|
}
|
|
708
|
+
//If no matching answer is found, then we return the value as it is
|
|
709
|
+
else {
|
|
710
|
+
// if it is an object so that Calculated Expressions can retain the selected choice option.
|
|
711
|
+
if (typeof value === 'object') {
|
|
712
|
+
return { valueCoding: value };
|
|
713
|
+
}
|
|
714
|
+
// if it is a string from the choice field, return as it is
|
|
715
|
+
else if (typeof value === 'string') {
|
|
716
|
+
return { valueString: value };
|
|
717
|
+
}
|
|
718
|
+
}
|
|
708
719
|
}
|
|
709
720
|
}
|
|
710
721
|
|
package/src/utils/initialise.ts
CHANGED
|
@@ -168,7 +168,14 @@ function readInitialValuesRecursive(
|
|
|
168
168
|
text: qItem.text
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
-
|
|
171
|
+
|
|
172
|
+
if (qItem.item && qItem.type !== 'group') {
|
|
173
|
+
qrItem.answer?.forEach((answer) => {
|
|
174
|
+
answer.item = initialValues.map((obj) => ({ ...obj }));
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
qrItem.item = initialValues.map((obj) => ({ ...obj }));
|
|
178
|
+
}
|
|
172
179
|
}
|
|
173
180
|
|
|
174
181
|
return qrItem ? [qrItem] : null;
|
package/src/utils/mapItem.ts
CHANGED
|
@@ -30,12 +30,14 @@ import { isRepeatItemAndNotCheckbox } from './qItem';
|
|
|
30
30
|
*/
|
|
31
31
|
export function getQrItemsIndex(
|
|
32
32
|
qItems: QuestionnaireItem[],
|
|
33
|
-
qrItems: QuestionnaireResponseItem[],
|
|
33
|
+
qrItems: QuestionnaireResponseItem[] | undefined,
|
|
34
34
|
qItemsIndexMap: Record<string, number>
|
|
35
35
|
): (QuestionnaireResponseItem | QuestionnaireResponseItem[] | undefined)[] {
|
|
36
36
|
// Generate a <linkId, QrItem OR QrItems> dictionary
|
|
37
37
|
const qrItemsCollected: Record<string, QuestionnaireResponseItem | QuestionnaireResponseItem[]> =
|
|
38
38
|
{};
|
|
39
|
+
|
|
40
|
+
if (!qrItems) return [];
|
|
39
41
|
for (const qrItem of qrItems) {
|
|
40
42
|
const linkId = qrItem.linkId;
|
|
41
43
|
|
package/src/utils/qrItem.ts
CHANGED
|
@@ -67,6 +67,20 @@ export function createEmptyQrGroup(qItem: QuestionnaireItem): QuestionnaireRespo
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Create an empty repeat qrItem from a given repeat qItem
|
|
72
|
+
*
|
|
73
|
+
* @author Clinton Gillespie
|
|
74
|
+
*/
|
|
75
|
+
export function createEmptyRepeatNestedItems(qItem: QuestionnaireItem): QuestionnaireResponseItem {
|
|
76
|
+
return {
|
|
77
|
+
linkId: qItem.linkId,
|
|
78
|
+
text: qItem.text,
|
|
79
|
+
answer: [],
|
|
80
|
+
item: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
70
84
|
/**
|
|
71
85
|
* Create an empty qrItem from a given qItem, optionally with an answer key
|
|
72
86
|
*
|
|
@@ -221,3 +235,80 @@ export function updateQrItemsInGroup(
|
|
|
221
235
|
}
|
|
222
236
|
}
|
|
223
237
|
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Updates the QuestionnaireResponseItem group by adding/removing a new/modified child QuestionnaireResponseItem into/from a qrGroup
|
|
241
|
+
* Specifically handles nested repeat items where each item has an answer array rather than nested items
|
|
242
|
+
*
|
|
243
|
+
* @author Sean Fong
|
|
244
|
+
*/
|
|
245
|
+
export function updateQrNestedItems(
|
|
246
|
+
newQrItem: QuestionnaireResponseItem | null,
|
|
247
|
+
questionnaireResponseOrQrItem: QuestionnaireResponseItem,
|
|
248
|
+
qItemsIndexMap: Record<string, number>
|
|
249
|
+
): void {
|
|
250
|
+
if (!newQrItem) return;
|
|
251
|
+
if (!questionnaireResponseOrQrItem.answer) {
|
|
252
|
+
questionnaireResponseOrQrItem.answer = [];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let qrAnswerItems = questionnaireResponseOrQrItem.answer;
|
|
256
|
+
if (!qrAnswerItems) {
|
|
257
|
+
qrAnswerItems = [];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (newQrItem && newQrItem.linkId in qItemsIndexMap) {
|
|
261
|
+
if (qrAnswerItems.length === 0) {
|
|
262
|
+
// Only add if the item has answers (nested items use answer array, not item array)
|
|
263
|
+
if (newQrItem.answer?.length) {
|
|
264
|
+
qrAnswerItems.push({ item: [newQrItem] });
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Find the index where newQrItem should be inserted based on qItemsIndexMap
|
|
271
|
+
const newItemSequenceIndex = qItemsIndexMap[newQrItem.linkId];
|
|
272
|
+
|
|
273
|
+
// Find the last answer object (group) that exists
|
|
274
|
+
const lastAnswerGroup = qrAnswerItems[qrAnswerItems.length - 1];
|
|
275
|
+
|
|
276
|
+
// If no answer group exists yet, create one
|
|
277
|
+
if (!lastAnswerGroup) {
|
|
278
|
+
const newGroup = {
|
|
279
|
+
item: [newQrItem]
|
|
280
|
+
};
|
|
281
|
+
qrAnswerItems.push(newGroup);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// If the last answer group doesn't have an item array, create one
|
|
286
|
+
if (!lastAnswerGroup.item) {
|
|
287
|
+
lastAnswerGroup.item = [newQrItem];
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check if newQrItem already exists in the last group's item array
|
|
292
|
+
const existingItemIndex = lastAnswerGroup.item.findIndex(
|
|
293
|
+
(item) => item.linkId === newQrItem.linkId
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (existingItemIndex !== -1) {
|
|
297
|
+
// Item exists - overwrite it
|
|
298
|
+
lastAnswerGroup.item[existingItemIndex] = newQrItem;
|
|
299
|
+
} else {
|
|
300
|
+
// Item doesn't exist - find correct position to insert based on sequence
|
|
301
|
+
let insertIndex = lastAnswerGroup.item.length;
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < lastAnswerGroup.item.length; i++) {
|
|
304
|
+
const currentItemSequenceIndex = qItemsIndexMap[lastAnswerGroup.item[i].linkId];
|
|
305
|
+
if (newItemSequenceIndex < currentItemSequenceIndex) {
|
|
306
|
+
insertIndex = i;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Insert at the correct position
|
|
311
|
+
lastAnswerGroup.item.splice(insertIndex, 0, newQrItem);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -54,6 +54,7 @@ import type { InitialExpression } from '../../interfaces/initialExpression.inter
|
|
|
54
54
|
import { addBindingParametersToValueSetUrl, getBindingParameters } from '../parameterisedValueSets';
|
|
55
55
|
import type { AnswerOptionsToggleExpression } from '../../interfaces/answerOptionsToggleExpression.interface';
|
|
56
56
|
import { getItemTerminologyServerToUse } from '../preferredTerminologyServer';
|
|
57
|
+
import { isSpecificItemControl } from '../extensions';
|
|
57
58
|
|
|
58
59
|
interface ReturnParamsRecursive {
|
|
59
60
|
variables: Variables;
|
|
@@ -282,9 +283,10 @@ async function extractExtensionsFromItemRecursive(
|
|
|
282
283
|
|
|
283
284
|
// Only continue to process if answerValueSetUrl is not a reference, because we have already processed it earlier
|
|
284
285
|
if (!initialValueSetUrl.startsWith('#')) {
|
|
285
|
-
// Get valueSet promise to be resolved
|
|
286
|
+
// Get valueSet promise to be resolved if it is not an autocomplete item, or it hasn't been added before
|
|
287
|
+
// We exclude autocomplete items here because they usually contain large valueSets that cannot be expanded fully
|
|
286
288
|
// Note: this entry uses valueSetUrlWithParams as the key
|
|
287
|
-
if (!valueSetPromises[initialValueSetUrl]) {
|
|
289
|
+
if (!isSpecificItemControl(item, 'autocomplete') && !valueSetPromises[initialValueSetUrl]) {
|
|
288
290
|
valueSetPromises[valueSetUrlWithParams] = {
|
|
289
291
|
promise: getValueSetPromise(valueSetUrlWithParams, terminologyServerUrl)
|
|
290
292
|
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Represents a single step in the path to an item within a nested QuestionnaireResponse.
|
|
3
|
-
* Each step is identified by its `linkId`, and optionally a `repeatIndex` if the item
|
|
4
|
-
* occurs within a repeating group.
|
|
5
|
-
*/
|
|
6
|
-
export interface ItemPathSegment {
|
|
7
|
-
/** The `linkId` of the QuestionnaireResponseItem at this path segment. */
|
|
8
|
-
linkId: string;
|
|
9
|
-
/**
|
|
10
|
-
* The index of the repeated item if this segment occurs within a repeating group.
|
|
11
|
-
* Omitted if the item is not part of a repeating group.
|
|
12
|
-
*/
|
|
13
|
-
repeatIndex?: number;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Represents a full path from the root of a QuestionnaireResponse to a deeply nested item.
|
|
17
|
-
* Each segment corresponds to one level of the item hierarchy.
|
|
18
|
-
*
|
|
19
|
-
* Example:
|
|
20
|
-
* [
|
|
21
|
-
* { linkId: 'sectionA' },
|
|
22
|
-
* { linkId: 'groupB', repeatIndex: 1 },
|
|
23
|
-
* { linkId: 'questionC' }
|
|
24
|
-
* ]
|
|
25
|
-
*
|
|
26
|
-
* This path navigates through `sectionA`, into the second instance of `groupB`,
|
|
27
|
-
* and finally to `questionC`.
|
|
28
|
-
*
|
|
29
|
-
* @author Sean Fong
|
|
30
|
-
*/
|
|
31
|
-
export type ItemPath = ItemPathSegment[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itemPath.interface.js","sourceRoot":"","sources":["../../src/interfaces/itemPath.interface.ts"],"names":[],"mappings":""}
|
package/lib/utils/itemPath.d.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import type { ItemPath } from '../interfaces/itemPath.interface';
|
|
2
|
-
/**
|
|
3
|
-
* Creates an `ItemPath` containing a single segment for the given `linkId`.
|
|
4
|
-
* This is useful when you want to construct a path to a specific item without referencing its ancestry,
|
|
5
|
-
* such as when working with top-level items or when only the item's identity is relevant.
|
|
6
|
-
*
|
|
7
|
-
* @param linkId - The `linkId` of the item.
|
|
8
|
-
* @param repeatIndex - (Optional) If the item is part of a repeating group, specify its index.
|
|
9
|
-
* @returns An `ItemPath` containing one segment.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const path = createSingleItemPath('question-1');
|
|
14
|
-
* // Output: [{ linkId: 'question-1' }]
|
|
15
|
-
*
|
|
16
|
-
* const pathWithRepeat = createSingleItemPath('question-1', 2);
|
|
17
|
-
* // Output: [{ linkId: 'question-1', repeatIndex: 2 }]
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export declare function createSingleItemPath(linkId: string, repeatIndex?: number): ItemPath;
|
|
21
|
-
/**
|
|
22
|
-
* Creates a new `ItemPath` by extending the given path with a new segment for the provided `linkId`.
|
|
23
|
-
* This version does NOT include a `repeatIndex`. If repeat handling is needed,
|
|
24
|
-
* use `appendRepeatIndexToLastSegment` after extending.
|
|
25
|
-
*
|
|
26
|
-
* @param currentPath - The existing item path.
|
|
27
|
-
* @param linkId - The `linkId` to use in the new path segment.
|
|
28
|
-
* @returns A new `ItemPath` with the added segment.
|
|
29
|
-
*/
|
|
30
|
-
export declare function extendItemPath(currentPath: linkId: string): ItemPath;
|
|
31
|
-
/**
|
|
32
|
-
* Returns a new `ItemPath` with a `repeatIndex` applied to the last segment.
|
|
33
|
-
* Useful for denoting which repetition of a group is being accessed.
|
|
34
|
-
*
|
|
35
|
-
* @param path - The item path to modify.
|
|
36
|
-
* @param repeatIndex - The index to assign to the final segment.
|
|
37
|
-
* @returns A new `ItemPath` with the last segment modified to include `repeatIndex`.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* const basePath = [{ linkId: 'groupA' }, { linkId: 'groupB' }];
|
|
41
|
-
* appendRepeatIndexToLastSegment(basePath, 2);
|
|
42
|
-
* // → [{ linkId: 'groupA' }, { linkId: 'groupB', repeatIndex: 2 }]
|
|
43
|
-
*/
|
|
44
|
-
export declare function appendRepeatIndexToLastSegment(path: repeatIndex: number): ItemPath;
|
|
45
|
-
/**
|
|
46
|
-
* Converts an `ItemPath` to a FHIRPath-compatible string.
|
|
47
|
-
*
|
|
48
|
-
* For example, the path:
|
|
49
|
-
* [
|
|
50
|
-
* { linkId: 'groupA' },
|
|
51
|
-
* { linkId: 'repeatingGroup', repeatIndex: 1 },
|
|
52
|
-
* { linkId: 'questionB' }
|
|
53
|
-
* ]
|
|
54
|
-
*
|
|
55
|
-
* Returns: "item.where(linkId='groupA').item.where(linkId='repeatingGroup')[1].item.where(linkId='questionB')"
|
|
56
|
-
*/
|
|
57
|
-
export declare function itemPathToFhirPathString(path: ItemPath): string;
|
package/lib/utils/itemPath.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates an `ItemPath` containing a single segment for the given `linkId`.
|
|
3
|
-
* This is useful when you want to construct a path to a specific item without referencing its ancestry,
|
|
4
|
-
* such as when working with top-level items or when only the item's identity is relevant.
|
|
5
|
-
*
|
|
6
|
-
* @param linkId - The `linkId` of the item.
|
|
7
|
-
* @param repeatIndex - (Optional) If the item is part of a repeating group, specify its index.
|
|
8
|
-
* @returns An `ItemPath` containing one segment.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* const path = createSingleItemPath('question-1');
|
|
13
|
-
* // Output: [{ linkId: 'question-1' }]
|
|
14
|
-
*
|
|
15
|
-
* const pathWithRepeat = createSingleItemPath('question-1', 2);
|
|
16
|
-
* // Output: [{ linkId: 'question-1', repeatIndex: 2 }]
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function createSingleItemPath(linkId, repeatIndex) {
|
|
20
|
-
return repeatIndex !== undefined ? [{ linkId, repeatIndex }] : [{ linkId }];
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Creates a new `ItemPath` by extending the given path with a new segment for the provided `linkId`.
|
|
24
|
-
* This version does NOT include a `repeatIndex`. If repeat handling is needed,
|
|
25
|
-
* use `appendRepeatIndexToLastSegment` after extending.
|
|
26
|
-
*
|
|
27
|
-
* @param currentPath - The existing item path.
|
|
28
|
-
* @param linkId - The `linkId` to use in the new path segment.
|
|
29
|
-
* @returns A new `ItemPath` with the added segment.
|
|
30
|
-
*/
|
|
31
|
-
export function extendItemPath(currentPath, linkId) {
|
|
32
|
-
return [...currentPath, { linkId }];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Returns a new `ItemPath` with a `repeatIndex` applied to the last segment.
|
|
36
|
-
* Useful for denoting which repetition of a group is being accessed.
|
|
37
|
-
*
|
|
38
|
-
* @param path - The item path to modify.
|
|
39
|
-
* @param repeatIndex - The index to assign to the final segment.
|
|
40
|
-
* @returns A new `ItemPath` with the last segment modified to include `repeatIndex`.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* const basePath = [{ linkId: 'groupA' }, { linkId: 'groupB' }];
|
|
44
|
-
* appendRepeatIndexToLastSegment(basePath, 2);
|
|
45
|
-
* // → [{ linkId: 'groupA' }, { linkId: 'groupB', repeatIndex: 2 }]
|
|
46
|
-
*/
|
|
47
|
-
export function appendRepeatIndexToLastSegment(path, repeatIndex) {
|
|
48
|
-
if (path.length === 0)
|
|
49
|
-
return [];
|
|
50
|
-
const newPath = [...path];
|
|
51
|
-
newPath[path.length - 1] = Object.assign(Object.assign({}, newPath[path.length - 1]), { repeatIndex });
|
|
52
|
-
return newPath;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Converts an `ItemPath` to a FHIRPath-compatible string.
|
|
56
|
-
*
|
|
57
|
-
* For example, the path:
|
|
58
|
-
* [
|
|
59
|
-
* { linkId: 'groupA' },
|
|
60
|
-
* { linkId: 'repeatingGroup', repeatIndex: 1 },
|
|
61
|
-
* { linkId: 'questionB' }
|
|
62
|
-
* ]
|
|
63
|
-
*
|
|
64
|
-
* Returns: "item.where(linkId='groupA').item.where(linkId='repeatingGroup')[1].item.where(linkId='questionB')"
|
|
65
|
-
*/
|
|
66
|
-
export function itemPathToFhirPathString(path) {
|
|
67
|
-
return path
|
|
68
|
-
.map((segment, index) => {
|
|
69
|
-
const base = `where(linkId='${segment.linkId}')`;
|
|
70
|
-
const repeat = segment.repeatIndex !== undefined ? `[${segment.repeatIndex}]` : '';
|
|
71
|
-
return (index === 0 ? 'item.' : '') + base + repeat;
|
|
72
|
-
})
|
|
73
|
-
.join('.item.');
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=itemPath.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"itemPath.js","sourceRoot":"","sources":["../../src/utils/itemPath.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,WAAoB;IACvE,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,WAAqB,EAAE,MAAc;IAClE,OAAO,CAAC,GAAG,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAc,EAAE,WAAmB;IAChF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,mCAAQ,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,KAAE,WAAW,GAAE,CAAC;IACxE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAc;IACrD,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACtB,MAAM,IAAI,GAAG,iBAAiB,OAAO,CAAC,MAAM,IAAI,CAAC;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;IACtD,CAAC,CAAC;SACD,IAAI,CAAC,QAAQ,CAAC,CAAC;AACpB,CAAC"}
|