@welshare/questionnaire 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +173 -0
  3. package/dist/esm/components/debug-section.d.ts +44 -0
  4. package/dist/esm/components/debug-section.d.ts.map +1 -0
  5. package/dist/esm/components/debug-section.js +28 -0
  6. package/dist/esm/components/question-renderer.d.ts +80 -0
  7. package/dist/esm/components/question-renderer.d.ts.map +1 -0
  8. package/dist/esm/components/question-renderer.js +159 -0
  9. package/dist/esm/components/questions/boolean-question.d.ts +15 -0
  10. package/dist/esm/components/questions/boolean-question.d.ts.map +1 -0
  11. package/dist/esm/components/questions/boolean-question.js +19 -0
  12. package/dist/esm/components/questions/choice-question.d.ts +19 -0
  13. package/dist/esm/components/questions/choice-question.d.ts.map +1 -0
  14. package/dist/esm/components/questions/choice-question.js +23 -0
  15. package/dist/esm/components/questions/decimal-question.d.ts +12 -0
  16. package/dist/esm/components/questions/decimal-question.d.ts.map +1 -0
  17. package/dist/esm/components/questions/decimal-question.js +7 -0
  18. package/dist/esm/components/questions/integer-question.d.ts +18 -0
  19. package/dist/esm/components/questions/integer-question.d.ts.map +1 -0
  20. package/dist/esm/components/questions/integer-question.js +24 -0
  21. package/dist/esm/components/questions/multiple-choice-question.d.ts +20 -0
  22. package/dist/esm/components/questions/multiple-choice-question.d.ts.map +1 -0
  23. package/dist/esm/components/questions/multiple-choice-question.js +39 -0
  24. package/dist/esm/components/questions/string-question.d.ts +12 -0
  25. package/dist/esm/components/questions/string-question.d.ts.map +1 -0
  26. package/dist/esm/components/questions/string-question.js +7 -0
  27. package/dist/esm/contexts/questionnaire-context.d.ts +41 -0
  28. package/dist/esm/contexts/questionnaire-context.d.ts.map +1 -0
  29. package/dist/esm/contexts/questionnaire-context.js +350 -0
  30. package/dist/esm/index.d.ts +7 -0
  31. package/dist/esm/index.d.ts.map +1 -0
  32. package/dist/esm/index.js +6 -0
  33. package/dist/esm/lib/questionnaire-utils.d.ts +29 -0
  34. package/dist/esm/lib/questionnaire-utils.d.ts.map +1 -0
  35. package/dist/esm/lib/questionnaire-utils.js +80 -0
  36. package/dist/esm/package.json +3 -0
  37. package/dist/esm/types/fhir.d.ts +117 -0
  38. package/dist/esm/types/fhir.d.ts.map +1 -0
  39. package/dist/esm/types/fhir.js +3 -0
  40. package/dist/esm/types/index.d.ts +51 -0
  41. package/dist/esm/types/index.d.ts.map +1 -0
  42. package/dist/esm/types/index.js +1 -0
  43. package/dist/node_modules/@welshare/questionnaire/.tshy/build.json +8 -0
  44. package/dist/node_modules/@welshare/questionnaire/.tshy/esm.json +16 -0
  45. package/dist/node_modules/@welshare/questionnaire/LICENSE +7 -0
  46. package/dist/node_modules/@welshare/questionnaire/README.md +173 -0
  47. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/debug-section.d.ts +44 -0
  48. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/debug-section.d.ts.map +1 -0
  49. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/debug-section.js +28 -0
  50. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/question-renderer.d.ts +80 -0
  51. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/question-renderer.d.ts.map +1 -0
  52. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/question-renderer.js +159 -0
  53. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/boolean-question.d.ts +15 -0
  54. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/boolean-question.d.ts.map +1 -0
  55. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/boolean-question.js +19 -0
  56. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/choice-question.d.ts +19 -0
  57. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/choice-question.d.ts.map +1 -0
  58. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/choice-question.js +23 -0
  59. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/decimal-question.d.ts +12 -0
  60. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/decimal-question.d.ts.map +1 -0
  61. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/decimal-question.js +7 -0
  62. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/integer-question.d.ts +18 -0
  63. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/integer-question.d.ts.map +1 -0
  64. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/integer-question.js +24 -0
  65. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/multiple-choice-question.d.ts +20 -0
  66. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/multiple-choice-question.d.ts.map +1 -0
  67. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/multiple-choice-question.js +39 -0
  68. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/string-question.d.ts +12 -0
  69. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/string-question.d.ts.map +1 -0
  70. package/dist/node_modules/@welshare/questionnaire/dist/esm/components/questions/string-question.js +7 -0
  71. package/dist/node_modules/@welshare/questionnaire/dist/esm/contexts/questionnaire-context.d.ts +41 -0
  72. package/dist/node_modules/@welshare/questionnaire/dist/esm/contexts/questionnaire-context.d.ts.map +1 -0
  73. package/dist/node_modules/@welshare/questionnaire/dist/esm/contexts/questionnaire-context.js +350 -0
  74. package/dist/node_modules/@welshare/questionnaire/dist/esm/index.d.ts +7 -0
  75. package/dist/node_modules/@welshare/questionnaire/dist/esm/index.d.ts.map +1 -0
  76. package/dist/node_modules/@welshare/questionnaire/dist/esm/index.js +6 -0
  77. package/dist/node_modules/@welshare/questionnaire/dist/esm/lib/questionnaire-utils.d.ts +29 -0
  78. package/dist/node_modules/@welshare/questionnaire/dist/esm/lib/questionnaire-utils.d.ts.map +1 -0
  79. package/dist/node_modules/@welshare/questionnaire/dist/esm/lib/questionnaire-utils.js +80 -0
  80. package/dist/node_modules/@welshare/questionnaire/dist/esm/package.json +3 -0
  81. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/fhir.d.ts +117 -0
  82. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/fhir.d.ts.map +1 -0
  83. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/fhir.js +3 -0
  84. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/index.d.ts +51 -0
  85. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/index.d.ts.map +1 -0
  86. package/dist/node_modules/@welshare/questionnaire/dist/esm/types/index.js +1 -0
  87. package/dist/node_modules/@welshare/questionnaire/dist/styles.css +467 -0
  88. package/dist/node_modules/@welshare/questionnaire/dist/tokens.css +130 -0
  89. package/dist/node_modules/@welshare/questionnaire/package.json +85 -0
  90. package/dist/node_modules/@welshare/questionnaire/src/components/debug-section.tsx +116 -0
  91. package/dist/node_modules/@welshare/questionnaire/src/components/question-renderer.tsx +368 -0
  92. package/dist/node_modules/@welshare/questionnaire/src/components/questionnaire-styles.css +467 -0
  93. package/dist/node_modules/@welshare/questionnaire/src/components/questionnaire-tokens.css +130 -0
  94. package/dist/node_modules/@welshare/questionnaire/src/components/questions/boolean-question.tsx +72 -0
  95. package/dist/node_modules/@welshare/questionnaire/src/components/questions/choice-question.tsx +68 -0
  96. package/dist/node_modules/@welshare/questionnaire/src/components/questions/decimal-question.tsx +32 -0
  97. package/dist/node_modules/@welshare/questionnaire/src/components/questions/integer-question.tsx +87 -0
  98. package/dist/node_modules/@welshare/questionnaire/src/components/questions/multiple-choice-question.tsx +119 -0
  99. package/dist/node_modules/@welshare/questionnaire/src/components/questions/string-question.tsx +31 -0
  100. package/dist/node_modules/@welshare/questionnaire/src/contexts/questionnaire-context.tsx +499 -0
  101. package/dist/node_modules/@welshare/questionnaire/src/index.ts +40 -0
  102. package/dist/node_modules/@welshare/questionnaire/src/lib/__tests__/questionnaire-utils.test.ts +578 -0
  103. package/dist/node_modules/@welshare/questionnaire/src/lib/questionnaire-utils.ts +99 -0
  104. package/dist/node_modules/@welshare/questionnaire/src/types/fhir.ts +126 -0
  105. package/dist/node_modules/@welshare/questionnaire/src/types/index.ts +44 -0
  106. package/dist/node_modules/@welshare/questionnaire/tsconfig.json +16 -0
  107. package/dist/styles.css +467 -0
  108. package/dist/tokens.css +130 -0
  109. package/package.json +84 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright © 2026 Welshare Health UG (haftungsbeschränkt)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # @welshare/questionnaire
2
+
3
+ FHIR R4 Questionnaire components for React with state management, validation, and theming.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @welshare/questionnaire
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { useState, useEffect } from 'react';
15
+ import {
16
+ QuestionnaireProvider,
17
+ useQuestionnaire,
18
+ QuestionRenderer,
19
+ getVisiblePages,
20
+ type Questionnaire,
21
+ } from '@welshare/questionnaire';
22
+ import '@welshare/questionnaire/tokens.css';
23
+ import '@welshare/questionnaire/styles.css';
24
+
25
+ function QuestionnairePage() {
26
+ const { questionnaire, isPageValid } = useQuestionnaire();
27
+ const pages = getVisiblePages(questionnaire);
28
+ const [currentPageIndex, setCurrentPageIndex] = useState(0);
29
+ const currentPage = pages[currentPageIndex];
30
+
31
+ return (
32
+ <div>
33
+ <h1>{questionnaire.title}</h1>
34
+ {currentPage?.item?.map((item) => (
35
+ <QuestionRenderer key={item.linkId} item={item} />
36
+ ))}
37
+ <button
38
+ onClick={() => setCurrentPageIndex(currentPageIndex + 1)}
39
+ disabled={!isPageValid(currentPage?.item || [])}
40
+ >
41
+ Next
42
+ </button>
43
+ </div>
44
+ );
45
+ }
46
+
47
+ function App() {
48
+ const [questionnaire, setQuestionnaire] = useState<Questionnaire | null>(null);
49
+
50
+ useEffect(() => {
51
+ fetch('/api/questionnaire/your-id')
52
+ .then(res => res.json())
53
+ .then(setQuestionnaire);
54
+ }, []);
55
+
56
+ if (!questionnaire) return <div>Loading...</div>;
57
+
58
+ return (
59
+ <QuestionnaireProvider questionnaire={questionnaire}>
60
+ <QuestionnairePage />
61
+ </QuestionnaireProvider>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## API
67
+
68
+ ### QuestionnaireProvider
69
+
70
+ **Props:**
71
+ - `questionnaire: Questionnaire` - FHIR Questionnaire object
72
+ - `questionnaireId?: string` - Optional ID override (defaults to `questionnaire.id`)
73
+ - `useNestedStructure?: boolean` - Nested or flat response structure (default: `true`)
74
+
75
+ ### useQuestionnaire Hook
76
+
77
+ ```tsx
78
+ const {
79
+ questionnaire, response,
80
+ updateAnswer, updateMultipleAnswers,
81
+ getAnswer, getAnswers,
82
+ isPageValid, getRequiredQuestions, getUnansweredRequiredQuestions,
83
+ markValidationErrors, clearValidationErrors, hasValidationError,
84
+ debugMode, toggleDebugMode,
85
+ } = useQuestionnaire();
86
+ ```
87
+
88
+ ### QuestionRenderer
89
+
90
+ **Props:**
91
+ - `item: QuestionnaireItem` - Questionnaire item to render
92
+ - `className?: string` - Container CSS classes
93
+ - `inputClassName?: string` - Input CSS classes
94
+ - `choiceClassName?: string` - Choice option CSS classes
95
+ - `renderRadioInput?: (props: RadioInputProps) => ReactNode` - Custom radio renderer
96
+ - `renderCheckboxInput?: (props: CheckboxInputProps) => ReactNode` - Custom checkbox renderer
97
+
98
+ **Supported Types:** `choice`, `boolean`, `integer`, `decimal`, `string`, `text`
99
+
100
+ ### Utilities
101
+
102
+ - `getVisiblePages(questionnaire)` - Get visible page groups
103
+ - `calculateProgress(currentIndex, total)` - Calculate progress percentage
104
+ - `getAllQuestionsFromPage(pageItem)` - Get all questions from a page
105
+
106
+ ## Theming
107
+
108
+ Override CSS custom properties:
109
+
110
+ ```css
111
+ :root {
112
+ --wq-color-primary-500: #3b82f6;
113
+ --wq-space-lg: 1.5rem;
114
+ --wq-font-size-base: 1rem;
115
+ }
116
+ ```
117
+
118
+ ### Custom Input Renderers
119
+
120
+ ```tsx
121
+ <QuestionRenderer
122
+ item={item}
123
+ renderRadioInput={(props) => (
124
+ <input type="radio" checked={props.checked} onChange={props.onChange} />
125
+ )}
126
+ renderCheckboxInput={(props) => (
127
+ <input type="checkbox" checked={props.checked} onChange={props.onChange} />
128
+ )}
129
+ />
130
+ ```
131
+
132
+ **Renderer Props:** `linkId`, `valueCoding`, `valueInteger`, `checked`, `disabled`, `onChange`, `label`, `index`
133
+
134
+ ## FHIR Extensions
135
+
136
+ ### Hidden Questions
137
+ ```json
138
+ {
139
+ "extension": [{
140
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden",
141
+ "valueBoolean": true
142
+ }]
143
+ }
144
+ ```
145
+
146
+ ### Slider Controls
147
+ ```json
148
+ {
149
+ "extension": [{
150
+ "url": "http://codes.welshare.app/StructureDefinition/questionnaire-slider-control",
151
+ "extension": [
152
+ { "url": "minValue", "valueInteger": 0 },
153
+ { "url": "maxValue", "valueInteger": 100 },
154
+ { "url": "step", "valueInteger": 1 },
155
+ { "url": "unit", "valueString": "minutes" }
156
+ ]
157
+ }]
158
+ }
159
+ ```
160
+
161
+ ### Exclusive Options
162
+ ```json
163
+ {
164
+ "extension": [{
165
+ "url": "http://codes.welshare.app/StructureDefinition/questionnaire-exclusive-option",
166
+ "valueString": "none-of-the-above-code"
167
+ }]
168
+ }
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT © Welshare UG (haftungsbeschränkt)
@@ -0,0 +1,44 @@
1
+ import type { QuestionnaireItem, QuestionnaireResponseAnswer, Coding } from "../types/fhir.js";
2
+ export interface DebugSectionProps {
3
+ /**
4
+ * The questionnaire item to display debug information for
5
+ */
6
+ item: QuestionnaireItem;
7
+ /**
8
+ * The current answer (for single-answer questions)
9
+ */
10
+ currentAnswer?: QuestionnaireResponseAnswer;
11
+ /**
12
+ * The current answers (for multi-answer questions)
13
+ */
14
+ currentAnswers?: QuestionnaireResponseAnswer[];
15
+ /**
16
+ * Optional LOINC code to display
17
+ */
18
+ loincCode?: Coding;
19
+ /**
20
+ * Additional CSS class names
21
+ */
22
+ className?: string;
23
+ }
24
+ /**
25
+ * DebugSection component displays debug information for a questionnaire item.
26
+ *
27
+ * This component is designed to be reusable and can be moved to the shared package.
28
+ * It displays:
29
+ * - LOINC code link (if available)
30
+ * - Collapsible FHIR question definition
31
+ * - Collapsible selected answer(s) in FHIR format
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * <DebugSection
36
+ * item={questionItem}
37
+ * currentAnswer={answer}
38
+ * currentAnswers={answers}
39
+ * loincCode={loincCode}
40
+ * />
41
+ * ```
42
+ */
43
+ export declare const DebugSection: ({ item, currentAnswer, currentAnswers, loincCode, className, }: DebugSectionProps) => import("react/jsx-runtime").JSX.Element;
44
+ //# sourceMappingURL=debug-section.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-section.d.ts","sourceRoot":"","sources":["../../../src/components/debug-section.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,2BAA2B,EAC3B,MAAM,EACP,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAC;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C;;OAEG;IACH,cAAc,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAC/C;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,GAAI,gEAM1B,iBAAiB,4CA4DnB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ /**
4
+ * DebugSection component displays debug information for a questionnaire item.
5
+ *
6
+ * This component is designed to be reusable and can be moved to the shared package.
7
+ * It displays:
8
+ * - LOINC code link (if available)
9
+ * - Collapsible FHIR question definition
10
+ * - Collapsible selected answer(s) in FHIR format
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <DebugSection
15
+ * item={questionItem}
16
+ * currentAnswer={answer}
17
+ * currentAnswers={answers}
18
+ * loincCode={loincCode}
19
+ * />
20
+ * ```
21
+ */
22
+ export const DebugSection = ({ item, currentAnswer, currentAnswers = [], loincCode, className = "", }) => {
23
+ const [isQuestionDefOpen, setIsQuestionDefOpen] = useState(false);
24
+ const [isAnswersOpen, setIsAnswersOpen] = useState(false);
25
+ return (_jsxs("div", { className: `wq-debug-section ${className}`, children: [loincCode && loincCode.code && (_jsx("div", { className: "wq-loinc-link-section", children: _jsxs("a", { href: `https://loinc.org/${loincCode.code}`, target: "_blank", rel: "noopener noreferrer", className: "wq-loinc-link", children: ["\uD83D\uDCCB LOINC Specification: ", loincCode.code] }) })), _jsxs("div", { className: "wq-debug-collapsible", children: [_jsxs("button", { className: "wq-debug-collapsible-header", onClick: () => setIsQuestionDefOpen(!isQuestionDefOpen), type: "button", children: [_jsx("span", { className: `chevron ${isQuestionDefOpen ? "open" : ""}`, children: "\u25B6" }), _jsx("span", { className: "wq-debug-title", children: "FHIR Question Definition" })] }), isQuestionDefOpen && (_jsx("pre", { className: "wq-debug-json", children: JSON.stringify(item, null, 2) }))] }), (currentAnswer || currentAnswers.length > 0) && (_jsxs("div", { className: "wq-debug-collapsible", children: [_jsxs("button", { className: "wq-debug-collapsible-header", onClick: () => setIsAnswersOpen(!isAnswersOpen), type: "button", children: [_jsx("span", { className: `chevron ${isAnswersOpen ? "open" : ""}`, children: "\u25B6" }), _jsx("span", { className: "wq-debug-title", children: "Selected Answer(s) (FHIR Format)" })] }), isAnswersOpen && (_jsx("pre", { className: "wq-debug-json", children: item.repeats
26
+ ? JSON.stringify(currentAnswers, null, 2)
27
+ : JSON.stringify(currentAnswer, null, 2) }))] }))] }));
28
+ };
@@ -0,0 +1,80 @@
1
+ import { type ReactNode } from "react";
2
+ import type { QuestionnaireItem } from "../types/fhir.js";
3
+ import { RadioInputProps, CheckboxInputProps } from "../types/index.js";
4
+ export interface QuestionRendererProps {
5
+ item: QuestionnaireItem;
6
+ /**
7
+ * Additional class names for the question container
8
+ * Will be appended to the default wq-question-container class
9
+ */
10
+ className?: string;
11
+ /**
12
+ * Additional class names for input elements
13
+ * Will be appended to the default wq-question-input class
14
+ */
15
+ inputClassName?: string;
16
+ /**
17
+ * Additional class names for choice options
18
+ * Will be appended to the default wq-choice-option class
19
+ */
20
+ choiceClassName?: string;
21
+ /**
22
+ * Custom renderer for radio button inputs.
23
+ * When provided, this function will be called to render each radio option,
24
+ * giving clients full control over styling and structure.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <QuestionRenderer
29
+ * item={item}
30
+ * renderRadioInput={(props) => (
31
+ * <div className="custom-radio-wrapper">
32
+ * <input
33
+ * type="radio"
34
+ * checked={props.checked}
35
+ * onChange={props.onChange}
36
+ * className="custom-radio"
37
+ * />
38
+ * <label>{props.label}</label>
39
+ * </div>
40
+ * )}
41
+ * />
42
+ * ```
43
+ */
44
+ renderRadioInput?: (props: RadioInputProps) => ReactNode;
45
+ /**
46
+ * Custom renderer for checkbox inputs.
47
+ * When provided, this function will be called to render each checkbox option,
48
+ * giving clients full control over styling and structure.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * <QuestionRenderer
53
+ * item={item}
54
+ * renderCheckboxInput={(props) => (
55
+ * <div className="custom-checkbox-wrapper">
56
+ * <input
57
+ * type="checkbox"
58
+ * checked={props.checked}
59
+ * onChange={props.onChange}
60
+ * className="custom-checkbox"
61
+ * />
62
+ * <label>{props.label}</label>
63
+ * </div>
64
+ * )}
65
+ * />
66
+ * ```
67
+ */
68
+ renderCheckboxInput?: (props: CheckboxInputProps) => ReactNode;
69
+ }
70
+ /**
71
+ * Wrapper component that combines the library's QuestionRenderer with debug functionality.
72
+ *
73
+ * This component:
74
+ * - Renders the library's QuestionRenderer with custom radio/checkbox renderers
75
+ * - Adds DebugSection below it when debug mode is enabled
76
+ *
77
+ * Uses custom input renderers to maintain the app's design system styling.
78
+ */
79
+ export declare const QuestionRenderer: (rendererProps: QuestionRendererProps) => import("react/jsx-runtime").JSX.Element;
80
+ //# sourceMappingURL=question-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/question-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAQ1D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;IACzD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC;CAChE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,eAAe,qBAAqB,4CAyBpE,CAAC"}
@@ -0,0 +1,159 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useQuestionnaire } from "../contexts/questionnaire-context.js";
3
+ import { DebugSection } from "./debug-section.js";
4
+ import { ChoiceQuestion } from "./questions/choice-question.js";
5
+ import { MultipleChoiceQuestion } from "./questions/multiple-choice-question.js";
6
+ import { IntegerQuestion } from "./questions/integer-question.js";
7
+ import { DecimalQuestion } from "./questions/decimal-question.js";
8
+ import { StringQuestion } from "./questions/string-question.js";
9
+ import { BooleanQuestion } from "./questions/boolean-question.js";
10
+ /**
11
+ * Wrapper component that combines the library's QuestionRenderer with debug functionality.
12
+ *
13
+ * This component:
14
+ * - Renders the library's QuestionRenderer with custom radio/checkbox renderers
15
+ * - Adds DebugSection below it when debug mode is enabled
16
+ *
17
+ * Uses custom input renderers to maintain the app's design system styling.
18
+ */
19
+ export const QuestionRenderer = (rendererProps) => {
20
+ const { getAnswer, getAnswers, debugMode } = useQuestionnaire();
21
+ const { item } = rendererProps;
22
+ const currentAnswer = getAnswer(item.linkId);
23
+ const currentAnswers = getAnswers(item.linkId);
24
+ // Find LOINC code if present
25
+ const loincCode = item.code?.find((coding) => coding.system === "http://loinc.org");
26
+ return (_jsxs("div", { children: [_jsx(QuestionRendererInternal, { ...rendererProps }), debugMode && (_jsx(DebugSection, { item: item, currentAnswer: currentAnswer, currentAnswers: currentAnswers, loincCode: loincCode }))] }));
27
+ };
28
+ /**
29
+ * QuestionRenderer component renders different types of questionnaire items
30
+ * Supports: choice, integer, decimal, string, text, boolean
31
+ * Features:
32
+ * - Single and multiple choice (radio/checkbox)
33
+ * - Slider controls for integer inputs
34
+ * - Exclusive option support for multi-select
35
+ * - Auto-populated (hidden) fields
36
+ * - Validation error display
37
+ */
38
+ const QuestionRendererInternal = ({ item, className = "", inputClassName = "", choiceClassName = "", renderRadioInput, renderCheckboxInput, }) => {
39
+ const { updateAnswer, updateMultipleAnswers, getAnswer, getAnswers, hasValidationError, } = useQuestionnaire();
40
+ const currentAnswer = getAnswer(item.linkId);
41
+ const currentAnswers = getAnswers(item.linkId);
42
+ const hasError = hasValidationError(item.linkId);
43
+ // Check if this field should be hidden (auto-populated)
44
+ const isAutoPopulated = item.extension?.some((ext) => ext.url ===
45
+ "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden" &&
46
+ ext.valueBoolean === true);
47
+ // Don't render auto-populated fields
48
+ if (isAutoPopulated) {
49
+ return null;
50
+ }
51
+ const handleChoiceChange = (valueCoding, valueInteger) => {
52
+ updateAnswer(item.linkId, { valueCoding, valueInteger });
53
+ };
54
+ const handleMultipleChoiceToggle = (valueCoding, valueInteger) => {
55
+ const isSelected = currentAnswers.some((answer) => answer.valueCoding?.code === valueCoding.code);
56
+ // Check if there's an exclusive option extension
57
+ const exclusiveOptionExt = item.extension?.find((ext) => ext.url ===
58
+ "http://codes.welshare.app/StructureDefinition/questionnaire-exclusive-option");
59
+ const exclusiveOptionCode = exclusiveOptionExt?.valueString;
60
+ let newAnswers;
61
+ if (isSelected) {
62
+ // Remove the answer
63
+ newAnswers = currentAnswers.filter((answer) => answer.valueCoding?.code !== valueCoding.code);
64
+ }
65
+ else {
66
+ // Check if we're selecting the exclusive option
67
+ if (exclusiveOptionCode && valueCoding.code === exclusiveOptionCode) {
68
+ // Clear all other answers and only keep this one
69
+ newAnswers = [{ valueCoding, valueInteger }];
70
+ }
71
+ else {
72
+ // Check if exclusive option is currently selected
73
+ const exclusiveSelected = currentAnswers.some((answer) => answer.valueCoding?.code === exclusiveOptionCode);
74
+ if (exclusiveSelected) {
75
+ // Remove exclusive option and add new selection
76
+ newAnswers = currentAnswers.filter((answer) => answer.valueCoding?.code !== exclusiveOptionCode);
77
+ newAnswers = [...newAnswers, { valueCoding, valueInteger }];
78
+ }
79
+ else {
80
+ // Normal behavior: Add the answer (respecting maxAnswers limit)
81
+ const maxAnswers = item.maxAnswers || Number.MAX_SAFE_INTEGER;
82
+ if (currentAnswers.length < maxAnswers) {
83
+ newAnswers = [...currentAnswers, { valueCoding, valueInteger }];
84
+ }
85
+ else {
86
+ // Already at max, don't add
87
+ return;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ updateMultipleAnswers(item.linkId, newAnswers);
93
+ };
94
+ const handleIntegerChange = (value) => {
95
+ // Allow clearing the controlled input
96
+ if (value === "") {
97
+ updateAnswer(item.linkId, {});
98
+ return;
99
+ }
100
+ const numValue = parseInt(value);
101
+ if (!isNaN(numValue)) {
102
+ updateAnswer(item.linkId, { valueInteger: numValue });
103
+ }
104
+ };
105
+ const handleDecimalChange = (value) => {
106
+ // Allow clearing the controlled input
107
+ if (value === "") {
108
+ updateAnswer(item.linkId, {});
109
+ return;
110
+ }
111
+ const numValue = parseFloat(value);
112
+ if (!isNaN(numValue)) {
113
+ updateAnswer(item.linkId, { valueDecimal: numValue });
114
+ }
115
+ };
116
+ const handleStringChange = (value) => {
117
+ updateAnswer(item.linkId, { valueString: value });
118
+ };
119
+ const handleBooleanChange = (value) => {
120
+ updateAnswer(item.linkId, { valueBoolean: value });
121
+ };
122
+ // Check if this field should use a slider control
123
+ const getSliderConfig = () => {
124
+ const sliderExt = item.extension?.find((ext) => ext.url ===
125
+ "http://codes.welshare.app/StructureDefinition/questionnaire-slider-control");
126
+ if (!sliderExt?.extension)
127
+ return null;
128
+ const minValue = sliderExt.extension.find((e) => e.url === "minValue")?.valueInteger ?? 0;
129
+ const maxValue = sliderExt.extension.find((e) => e.url === "maxValue")?.valueInteger ??
130
+ 100;
131
+ const step = sliderExt.extension.find((e) => e.url === "step")?.valueInteger ?? 1;
132
+ const unit = sliderExt.extension.find((e) => e.url === "unit")?.valueString ?? "";
133
+ return { minValue, maxValue, step, unit };
134
+ };
135
+ const sliderConfig = getSliderConfig();
136
+ const renderQuestion = () => {
137
+ switch (item.type) {
138
+ case "choice":
139
+ // Multi-select with checkboxes when repeats is true
140
+ if (item.repeats) {
141
+ return (_jsx(MultipleChoiceQuestion, { item: item, currentAnswers: currentAnswers, onMultipleChoiceToggle: handleMultipleChoiceToggle, choiceClassName: choiceClassName, renderCheckboxInput: renderCheckboxInput }));
142
+ }
143
+ // Single-select with radio buttons (default)
144
+ return (_jsx(ChoiceQuestion, { item: item, currentAnswer: currentAnswer, onChoiceChange: handleChoiceChange, choiceClassName: choiceClassName, renderRadioInput: renderRadioInput }));
145
+ case "integer":
146
+ return (_jsx(IntegerQuestion, { item: item, currentAnswer: currentAnswer, onIntegerChange: handleIntegerChange, inputClassName: inputClassName, sliderConfig: sliderConfig }));
147
+ case "decimal":
148
+ return (_jsx(DecimalQuestion, { item: item, currentAnswer: currentAnswer, onDecimalChange: handleDecimalChange, inputClassName: inputClassName }));
149
+ case "string":
150
+ case "text":
151
+ return (_jsx(StringQuestion, { item: item, currentAnswer: currentAnswer, onStringChange: handleStringChange, inputClassName: inputClassName }));
152
+ case "boolean":
153
+ return (_jsx(BooleanQuestion, { item: item, currentAnswer: currentAnswer, onBooleanChange: handleBooleanChange, choiceClassName: choiceClassName, renderRadioInput: renderRadioInput }));
154
+ default:
155
+ return (_jsxs("div", { className: "wq-unsupported-type", children: ["Unsupported question type: ", item.type] }));
156
+ }
157
+ };
158
+ return (_jsxs("div", { className: `wq-question-container ${hasError ? "wq-has-error" : ""} ${className}`, children: [_jsxs("div", { className: "wq-question-text", children: [item.text, item.required && _jsx("span", { className: "wq-required-indicator", children: "*" })] }), renderQuestion()] }));
159
+ };
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { QuestionnaireItem, QuestionnaireResponseAnswer } from '../../types/fhir.js';
3
+ import { RadioInputProps } from '@/types/index.js';
4
+ export interface BooleanQuestionProps {
5
+ item: QuestionnaireItem;
6
+ currentAnswer?: QuestionnaireResponseAnswer;
7
+ onBooleanChange: (value: boolean) => void;
8
+ choiceClassName?: string;
9
+ renderRadioInput?: (props: RadioInputProps) => ReactNode;
10
+ }
11
+ /**
12
+ * Renders a boolean question with Yes/No radio buttons
13
+ */
14
+ export declare const BooleanQuestion: ({ item, currentAnswer, onBooleanChange, choiceClassName, renderRadioInput }: BooleanQuestionProps) => import("react/jsx-runtime").JSX.Element;
15
+ //# sourceMappingURL=boolean-question.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boolean-question.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/boolean-question.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,iBAAiB,CAAC;IACxB,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CAC1D;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,6EAM7B,oBAAoB,4CAgDtB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Renders a boolean question with Yes/No radio buttons
4
+ */
5
+ export const BooleanQuestion = ({ item, currentAnswer, onBooleanChange, choiceClassName = '', renderRadioInput }) => {
6
+ return (_jsx("div", { className: `wq-question-choice ${choiceClassName}`, children: renderRadioInput ? (_jsxs(_Fragment, { children: [renderRadioInput({
7
+ linkId: item.linkId,
8
+ checked: currentAnswer?.valueBoolean === true,
9
+ onChange: () => onBooleanChange(true),
10
+ label: 'Yes',
11
+ index: 0
12
+ }), renderRadioInput({
13
+ linkId: item.linkId,
14
+ checked: currentAnswer?.valueBoolean === false,
15
+ onChange: () => onBooleanChange(false),
16
+ label: 'No',
17
+ index: 1
18
+ })] })) : (_jsxs(_Fragment, { children: [_jsxs("label", { className: `wq-choice-option ${currentAnswer?.valueBoolean === true ? 'wq-selected' : ''}`, children: [_jsx("input", { type: "radio", name: item.linkId, checked: currentAnswer?.valueBoolean === true, onChange: () => onBooleanChange(true), "data-wq-input": "radio", "data-wq-selected": currentAnswer?.valueBoolean === true }), _jsx("span", { className: "wq-choice-label", children: "Yes" })] }), _jsxs("label", { className: `wq-choice-option ${currentAnswer?.valueBoolean === false ? 'wq-selected' : ''}`, children: [_jsx("input", { type: "radio", name: item.linkId, checked: currentAnswer?.valueBoolean === false, onChange: () => onBooleanChange(false), "data-wq-input": "radio", "data-wq-selected": currentAnswer?.valueBoolean === false }), _jsx("span", { className: "wq-choice-label", children: "No" })] })] })) }));
19
+ };
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { QuestionnaireItem, QuestionnaireResponseAnswer } from '../../types/fhir.js';
3
+ import { RadioInputProps } from '@/types/index.js';
4
+ export interface ChoiceQuestionProps {
5
+ item: QuestionnaireItem;
6
+ currentAnswer?: QuestionnaireResponseAnswer;
7
+ onChoiceChange: (valueCoding: {
8
+ system?: string;
9
+ code?: string;
10
+ display?: string;
11
+ }, valueInteger?: number) => void;
12
+ choiceClassName?: string;
13
+ renderRadioInput?: (props: RadioInputProps) => ReactNode;
14
+ }
15
+ /**
16
+ * Renders a single-select choice question with radio buttons
17
+ */
18
+ export declare const ChoiceQuestion: ({ item, currentAnswer, onChoiceChange, choiceClassName, renderRadioInput }: ChoiceQuestionProps) => import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=choice-question.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"choice-question.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/choice-question.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C,cAAc,EAAE,CACd,WAAW,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EACjE,YAAY,CAAC,EAAE,MAAM,KAClB,IAAI,CAAC;IACV,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CAC1D;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,4EAM5B,mBAAmB,4CAyCrB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Renders a single-select choice question with radio buttons
4
+ */
5
+ export const ChoiceQuestion = ({ item, currentAnswer, onChoiceChange, choiceClassName = '', renderRadioInput }) => {
6
+ return (_jsx("div", { className: `wq-question-choice ${choiceClassName}`, children: item.answerOption?.map((option, index) => {
7
+ const isSelected = currentAnswer?.valueCoding?.code === option.valueCoding?.code;
8
+ // Use custom renderer if provided
9
+ if (renderRadioInput) {
10
+ return (_jsx("div", { children: renderRadioInput({
11
+ linkId: item.linkId,
12
+ valueCoding: option.valueCoding,
13
+ valueInteger: option.valueInteger,
14
+ checked: isSelected,
15
+ onChange: () => onChoiceChange(option.valueCoding || {}, option.valueInteger),
16
+ label: option.valueCoding?.display || '',
17
+ index
18
+ }) }, index));
19
+ }
20
+ // Default rendering
21
+ return (_jsxs("label", { className: `wq-choice-option ${isSelected ? 'wq-selected' : ''}`, children: [_jsx("input", { type: "radio", name: item.linkId, value: option.valueCoding?.code, checked: isSelected, onChange: () => onChoiceChange(option.valueCoding || {}, option.valueInteger), "data-wq-input": "radio", "data-wq-selected": isSelected }), _jsx("span", { className: "wq-choice-label", children: option.valueCoding?.display })] }, index));
22
+ }) }));
23
+ };
@@ -0,0 +1,12 @@
1
+ import type { QuestionnaireItem, QuestionnaireResponseAnswer } from "../../types/fhir.js";
2
+ export interface DecimalQuestionProps {
3
+ item: QuestionnaireItem;
4
+ currentAnswer?: QuestionnaireResponseAnswer;
5
+ onDecimalChange: (value: string) => void;
6
+ inputClassName?: string;
7
+ }
8
+ /**
9
+ * Renders a decimal question with number input
10
+ */
11
+ export declare const DecimalQuestion: ({ item, currentAnswer, onDecimalChange, inputClassName, }: DecimalQuestionProps) => import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=decimal-question.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decimal-question.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/decimal-question.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,iBAAiB,CAAC;IACxB,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,2DAK7B,oBAAoB,4CAWtB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Renders a decimal question with number input
4
+ */
5
+ export const DecimalQuestion = ({ item, currentAnswer, onDecimalChange, inputClassName = "", }) => {
6
+ return (_jsx("input", { type: "number", className: `wq-question-input ${inputClassName}`, value: currentAnswer?.valueDecimal ?? "", onChange: (e) => onDecimalChange(e.target.value), placeholder: "Enter a number", step: "0.1" }));
7
+ };
@@ -0,0 +1,18 @@
1
+ import type { QuestionnaireItem, QuestionnaireResponseAnswer } from "../../types/fhir.js";
2
+ export interface IntegerQuestionProps {
3
+ item: QuestionnaireItem;
4
+ currentAnswer?: QuestionnaireResponseAnswer;
5
+ onIntegerChange: (value: string) => void;
6
+ inputClassName?: string;
7
+ sliderConfig?: {
8
+ minValue: number;
9
+ maxValue: number;
10
+ step: number;
11
+ unit: string;
12
+ } | null;
13
+ }
14
+ /**
15
+ * Renders an integer question with optional slider control
16
+ */
17
+ export declare const IntegerQuestion: ({ item, currentAnswer, onIntegerChange, inputClassName, sliderConfig, }: IntegerQuestionProps) => import("react/jsx-runtime").JSX.Element;
18
+ //# sourceMappingURL=integer-question.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integer-question.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/integer-question.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,iBAAiB,CAAC;IACxB,aAAa,CAAC,EAAE,2BAA2B,CAAC;IAC5C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;CACV;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,yEAM7B,oBAAoB,4CA2DtB,CAAC"}