@welshare/questionnaire 0.2.1 → 0.2.3
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/README.md +118 -0
- package/dist/esm/components/legal-consent-form.d.ts +123 -0
- package/dist/esm/components/legal-consent-form.d.ts.map +1 -0
- package/dist/esm/components/legal-consent-form.js +97 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/types/fhir.d.ts +19 -115
- package/dist/esm/types/fhir.d.ts.map +1 -1
- package/dist/esm/types/fhir.js +13 -4
- package/dist/styles.css +153 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -168,6 +168,124 @@ function MyComponent() {
|
|
|
168
168
|
}
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
+
### LegalConsentForm
|
|
172
|
+
|
|
173
|
+
A self-contained form component for collecting user consent before data submission. This component handles:
|
|
174
|
+
|
|
175
|
+
1. **Terms & Conditions and Privacy Policy** (required) - User must accept to proceed
|
|
176
|
+
2. **Study invitation notifications** (optional) - User can opt in to receive study invitations
|
|
177
|
+
|
|
178
|
+
**Usage Pattern:**
|
|
179
|
+
This component provides the content for a consent dialog. Applications should wrap this component in their own Dialog/Modal component (e.g., from shadcn/ui, Radix UI, MUI, etc.), similar to how BmiForm is used with input helpers.
|
|
180
|
+
|
|
181
|
+
**Props:**
|
|
182
|
+
|
|
183
|
+
- `initialValues?: Partial<LegalConsentResult>` - Initial consent state (for restoring previous preferences)
|
|
184
|
+
- `documentLinks?: LegalDocumentLinks` - Custom URLs for Terms & Privacy documents
|
|
185
|
+
- `onConfirm?: (result: LegalConsentResult) => void` - Called when user confirms (only if terms accepted)
|
|
186
|
+
- `onCancel?: () => void` - Called when user cancels/declines
|
|
187
|
+
- `onChange?: (result: LegalConsentResult) => void` - Called on any checkbox change (real-time updates)
|
|
188
|
+
- `renderCheckbox?: (props: LegalCheckboxProps) => ReactNode` - Custom checkbox component renderer
|
|
189
|
+
- `confirmButtonLabel?: string` - Custom confirm button text (default: "Confirm & Continue")
|
|
190
|
+
- `cancelButtonLabel?: string` - Custom cancel button text (default: "Cancel")
|
|
191
|
+
- `className?: string` - Additional CSS classes
|
|
192
|
+
|
|
193
|
+
**Result Object:**
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
interface LegalConsentResult {
|
|
197
|
+
termsAccepted: boolean; // Required - T&Cs and Privacy Policy
|
|
198
|
+
studyConsentAccepted: boolean; // Optional - Study invitations
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Example with Dialog:**
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
import { useState } from "react";
|
|
206
|
+
import {
|
|
207
|
+
LegalConsentForm,
|
|
208
|
+
type LegalConsentResult,
|
|
209
|
+
} from "@welshare/questionnaire";
|
|
210
|
+
import {
|
|
211
|
+
Dialog,
|
|
212
|
+
DialogContent,
|
|
213
|
+
DialogHeader,
|
|
214
|
+
DialogTitle,
|
|
215
|
+
} from "@/components/ui/dialog";
|
|
216
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
217
|
+
import "@welshare/questionnaire/tokens.css";
|
|
218
|
+
import "@welshare/questionnaire/styles.css";
|
|
219
|
+
|
|
220
|
+
function DataSubmissionFlow() {
|
|
221
|
+
const [showConsent, setShowConsent] = useState(false);
|
|
222
|
+
|
|
223
|
+
const handleSubmitData = () => {
|
|
224
|
+
// Show consent dialog before submitting data
|
|
225
|
+
setShowConsent(true);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const handleConsent = (result: LegalConsentResult) => {
|
|
229
|
+
console.log("Terms accepted:", result.termsAccepted);
|
|
230
|
+
console.log("Study consent:", result.studyConsentAccepted);
|
|
231
|
+
|
|
232
|
+
// Proceed with data submission
|
|
233
|
+
submitDataToServer(result);
|
|
234
|
+
setShowConsent(false);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<>
|
|
239
|
+
<button onClick={handleSubmitData}>Submit My Data</button>
|
|
240
|
+
|
|
241
|
+
<Dialog open={showConsent} onOpenChange={setShowConsent}>
|
|
242
|
+
<DialogContent>
|
|
243
|
+
<DialogHeader>
|
|
244
|
+
<DialogTitle>Health Data Storage Consent</DialogTitle>
|
|
245
|
+
</DialogHeader>
|
|
246
|
+
<LegalConsentForm
|
|
247
|
+
renderCheckbox={({ id, checked, onCheckedChange, className }) => (
|
|
248
|
+
<Checkbox
|
|
249
|
+
id={id}
|
|
250
|
+
checked={checked}
|
|
251
|
+
onCheckedChange={onCheckedChange}
|
|
252
|
+
className={className}
|
|
253
|
+
/>
|
|
254
|
+
)}
|
|
255
|
+
onConfirm={handleConsent}
|
|
256
|
+
onCancel={() => setShowConsent(false)}
|
|
257
|
+
/>
|
|
258
|
+
</DialogContent>
|
|
259
|
+
</Dialog>
|
|
260
|
+
</>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Custom Document Links:**
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
<LegalConsentForm
|
|
269
|
+
documentLinks={{
|
|
270
|
+
termsUrl: "https://myapp.com/terms",
|
|
271
|
+
privacyUrl: "https://myapp.com/privacy",
|
|
272
|
+
}}
|
|
273
|
+
onConfirm={handleConsent}
|
|
274
|
+
onCancel={handleCancel}
|
|
275
|
+
/>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**CSS Classes for Styling:**
|
|
279
|
+
|
|
280
|
+
- `.wq-legal-consent-form` - Main form container
|
|
281
|
+
- `.wq-legal-consent-section` - Individual consent section
|
|
282
|
+
- `.wq-legal-consent-heading` - Section headings
|
|
283
|
+
- `.wq-legal-consent-checkbox-row` - Checkbox + label row
|
|
284
|
+
- `.wq-legal-consent-link` - Links to legal documents
|
|
285
|
+
- `.wq-legal-consent-info-box` - Information box (study consent details)
|
|
286
|
+
- `.wq-legal-consent-actions` - Button container
|
|
287
|
+
- `.wq-button`, `.wq-button-primary`, `.wq-button-outline` - Button styles
|
|
288
|
+
|
|
171
289
|
### Utilities
|
|
172
290
|
|
|
173
291
|
**Questionnaire Utilities:**
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for legal document links
|
|
4
|
+
*/
|
|
5
|
+
export interface LegalDocumentLinks {
|
|
6
|
+
/** URL to Terms & Conditions document */
|
|
7
|
+
termsUrl?: string;
|
|
8
|
+
/** URL to Privacy Policy document */
|
|
9
|
+
privacyUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Result object returned on submission
|
|
13
|
+
*/
|
|
14
|
+
export interface LegalConsentResult {
|
|
15
|
+
/** Whether user accepted terms & conditions and privacy policy */
|
|
16
|
+
termsAccepted: boolean;
|
|
17
|
+
/** Whether user opted in to receive study invitations */
|
|
18
|
+
studyConsentAccepted: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Props for rendering a checkbox in the legal consent form
|
|
22
|
+
*/
|
|
23
|
+
export interface LegalCheckboxProps {
|
|
24
|
+
/** Unique identifier for the checkbox */
|
|
25
|
+
id: string;
|
|
26
|
+
/** Whether the checkbox is checked */
|
|
27
|
+
checked: boolean;
|
|
28
|
+
/** Callback when the checkbox value changes */
|
|
29
|
+
onCheckedChange: (checked: boolean) => void;
|
|
30
|
+
/** Additional CSS class name */
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface LegalConsentFormProps {
|
|
34
|
+
/**
|
|
35
|
+
* Initial consent values.
|
|
36
|
+
* Useful for restoring previous consent state.
|
|
37
|
+
*/
|
|
38
|
+
initialValues?: Partial<LegalConsentResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Custom URLs for legal documents.
|
|
41
|
+
* Defaults to Welshare's standard T&Cs and Privacy Policy.
|
|
42
|
+
*/
|
|
43
|
+
documentLinks?: LegalDocumentLinks;
|
|
44
|
+
/**
|
|
45
|
+
* Callback fired when user confirms the consent.
|
|
46
|
+
* Only fires if required consents are accepted.
|
|
47
|
+
*/
|
|
48
|
+
onConfirm?: (result: LegalConsentResult) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Callback fired when user cancels/declines.
|
|
51
|
+
*/
|
|
52
|
+
onCancel?: () => void;
|
|
53
|
+
/**
|
|
54
|
+
* Callback fired whenever any consent value changes.
|
|
55
|
+
* Use this for real-time updates without requiring explicit submission.
|
|
56
|
+
*/
|
|
57
|
+
onChange?: (result: LegalConsentResult) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Custom checkbox renderer.
|
|
60
|
+
* Applications *can* optionally provide their own checkbox component renderer
|
|
61
|
+
* If not provided, uses native HTML checkboxes.
|
|
62
|
+
*/
|
|
63
|
+
renderCheckbox?: (props: LegalCheckboxProps) => ReactNode;
|
|
64
|
+
/**
|
|
65
|
+
* Label for the confirm button.
|
|
66
|
+
* Defaults to "Confirm & Continue".
|
|
67
|
+
*/
|
|
68
|
+
confirmButtonLabel?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Label for the cancel button.
|
|
71
|
+
* Defaults to "Cancel".
|
|
72
|
+
*/
|
|
73
|
+
cancelButtonLabel?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Additional CSS class names
|
|
76
|
+
*/
|
|
77
|
+
className?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Legal Consent Form Component
|
|
81
|
+
*
|
|
82
|
+
* A self-contained form component for collecting user consent for:
|
|
83
|
+
* 1. Terms & Conditions and Privacy Policy (required)
|
|
84
|
+
* 2. Study invitation notifications (optional)
|
|
85
|
+
*
|
|
86
|
+
* **Usage Pattern:**
|
|
87
|
+
* This component provides the content for a consent dialog. Applications
|
|
88
|
+
* should wrap this component in their own Dialog/Modal component, similar
|
|
89
|
+
* to how BmiForm is used.
|
|
90
|
+
*
|
|
91
|
+
* **Customization:**
|
|
92
|
+
* - Provide custom checkbox renderer via `renderCheckbox` prop
|
|
93
|
+
* - Customize document URLs via `documentLinks` prop
|
|
94
|
+
* - Style with CSS classes using `className` prop
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```tsx
|
|
98
|
+
* <Dialog open={showConsent} onOpenChange={setShowConsent}>
|
|
99
|
+
* <DialogContent>
|
|
100
|
+
* <DialogHeader>
|
|
101
|
+
* <DialogTitle>Health Data Storage Consent</DialogTitle>
|
|
102
|
+
* </DialogHeader>
|
|
103
|
+
* <LegalConsentForm
|
|
104
|
+
* renderCheckbox={({ id, checked, onCheckedChange, className }) => (
|
|
105
|
+
* <Checkbox
|
|
106
|
+
* id={id}
|
|
107
|
+
* checked={checked}
|
|
108
|
+
* onCheckedChange={onCheckedChange}
|
|
109
|
+
* className={className}
|
|
110
|
+
* />
|
|
111
|
+
* )}
|
|
112
|
+
* onConfirm={(result) => {
|
|
113
|
+
* handleConsent(result);
|
|
114
|
+
* setShowConsent(false);
|
|
115
|
+
* }}
|
|
116
|
+
* onCancel={() => setShowConsent(false)}
|
|
117
|
+
* />
|
|
118
|
+
* </DialogContent>
|
|
119
|
+
* </Dialog>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare const LegalConsentForm: ({ initialValues, documentLinks, onConfirm, onCancel, onChange, renderCheckbox, confirmButtonLabel, cancelButtonLabel, className, }: LegalConsentFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
123
|
+
//# sourceMappingURL=legal-consent-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legal-consent-form.d.ts","sourceRoot":"","sources":["../../../src/components/legal-consent-form.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,aAAa,EAAE,OAAO,CAAC;IACvB,yDAAyD;IACzD,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAE5C;;;OAGG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAEjD;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAEtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC;IAE1D;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,gBAAgB,GAAI,oIAU9B,qBAAqB,4CAmLvB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback } from "react";
|
|
3
|
+
const DEFAULT_TERMS_URL = "https://www.welshare.health/terms-hpmp";
|
|
4
|
+
const DEFAULT_PRIVACY_URL = "https://www.welshare.health/privacy-hpmp";
|
|
5
|
+
/**
|
|
6
|
+
* Legal Consent Form Component
|
|
7
|
+
*
|
|
8
|
+
* A self-contained form component for collecting user consent for:
|
|
9
|
+
* 1. Terms & Conditions and Privacy Policy (required)
|
|
10
|
+
* 2. Study invitation notifications (optional)
|
|
11
|
+
*
|
|
12
|
+
* **Usage Pattern:**
|
|
13
|
+
* This component provides the content for a consent dialog. Applications
|
|
14
|
+
* should wrap this component in their own Dialog/Modal component, similar
|
|
15
|
+
* to how BmiForm is used.
|
|
16
|
+
*
|
|
17
|
+
* **Customization:**
|
|
18
|
+
* - Provide custom checkbox renderer via `renderCheckbox` prop
|
|
19
|
+
* - Customize document URLs via `documentLinks` prop
|
|
20
|
+
* - Style with CSS classes using `className` prop
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <Dialog open={showConsent} onOpenChange={setShowConsent}>
|
|
25
|
+
* <DialogContent>
|
|
26
|
+
* <DialogHeader>
|
|
27
|
+
* <DialogTitle>Health Data Storage Consent</DialogTitle>
|
|
28
|
+
* </DialogHeader>
|
|
29
|
+
* <LegalConsentForm
|
|
30
|
+
* renderCheckbox={({ id, checked, onCheckedChange, className }) => (
|
|
31
|
+
* <Checkbox
|
|
32
|
+
* id={id}
|
|
33
|
+
* checked={checked}
|
|
34
|
+
* onCheckedChange={onCheckedChange}
|
|
35
|
+
* className={className}
|
|
36
|
+
* />
|
|
37
|
+
* )}
|
|
38
|
+
* onConfirm={(result) => {
|
|
39
|
+
* handleConsent(result);
|
|
40
|
+
* setShowConsent(false);
|
|
41
|
+
* }}
|
|
42
|
+
* onCancel={() => setShowConsent(false)}
|
|
43
|
+
* />
|
|
44
|
+
* </DialogContent>
|
|
45
|
+
* </Dialog>
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const LegalConsentForm = ({ initialValues, documentLinks, onConfirm, onCancel, onChange, renderCheckbox, confirmButtonLabel = "Confirm & Continue", cancelButtonLabel = "Cancel", className = "", }) => {
|
|
49
|
+
// Consent state
|
|
50
|
+
const [termsAccepted, setTermsAccepted] = useState(initialValues?.termsAccepted ?? false);
|
|
51
|
+
const [studyConsentAccepted, setStudyConsentAccepted] = useState(initialValues?.studyConsentAccepted ?? false);
|
|
52
|
+
// Resolve document URLs
|
|
53
|
+
const termsUrl = documentLinks?.termsUrl ?? DEFAULT_TERMS_URL;
|
|
54
|
+
const privacyUrl = documentLinks?.privacyUrl ?? DEFAULT_PRIVACY_URL;
|
|
55
|
+
// Handle terms acceptance change
|
|
56
|
+
const handleTermsChange = useCallback((checked) => {
|
|
57
|
+
setTermsAccepted(checked);
|
|
58
|
+
onChange?.({
|
|
59
|
+
termsAccepted: checked,
|
|
60
|
+
studyConsentAccepted,
|
|
61
|
+
});
|
|
62
|
+
}, [onChange, studyConsentAccepted]);
|
|
63
|
+
// Handle study consent change
|
|
64
|
+
const handleStudyConsentChange = useCallback((checked) => {
|
|
65
|
+
setStudyConsentAccepted(checked);
|
|
66
|
+
onChange?.({
|
|
67
|
+
termsAccepted,
|
|
68
|
+
studyConsentAccepted: checked,
|
|
69
|
+
});
|
|
70
|
+
}, [onChange, termsAccepted]);
|
|
71
|
+
// Handle confirm
|
|
72
|
+
const handleConfirm = useCallback(() => {
|
|
73
|
+
if (termsAccepted && onConfirm) {
|
|
74
|
+
onConfirm({
|
|
75
|
+
termsAccepted,
|
|
76
|
+
studyConsentAccepted,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}, [termsAccepted, studyConsentAccepted, onConfirm]);
|
|
80
|
+
// Handle cancel
|
|
81
|
+
const handleCancel = useCallback(() => {
|
|
82
|
+
onCancel?.();
|
|
83
|
+
}, [onCancel]);
|
|
84
|
+
// Render checkbox - use custom renderer or fallback to native
|
|
85
|
+
const renderCheckboxElement = (id, checked, onCheckedChange) => {
|
|
86
|
+
if (renderCheckbox) {
|
|
87
|
+
return renderCheckbox({
|
|
88
|
+
id,
|
|
89
|
+
checked,
|
|
90
|
+
onCheckedChange,
|
|
91
|
+
className: "wq-legal-checkbox",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return (_jsx("input", { type: "checkbox", id: id, checked: checked, onChange: (e) => onCheckedChange(e.target.checked), className: "wq-legal-checkbox" }));
|
|
95
|
+
};
|
|
96
|
+
return (_jsxs("div", { className: `wq-legal-consent-form ${className}`, children: [_jsxs("div", { className: "wq-legal-consent-sections", children: [_jsxs("div", { className: "wq-legal-consent-section", children: [_jsx("h3", { className: "wq-legal-consent-heading", children: "1. Agree to T&Cs & Privacy Policy" }), _jsxs("div", { className: "wq-legal-consent-checkbox-row", children: [renderCheckboxElement("terms", termsAccepted, handleTermsChange), _jsxs("label", { htmlFor: "terms", className: "wq-legal-consent-label", children: ["I agree to the", " ", _jsx("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", className: "wq-legal-consent-link", children: "Terms & Conditions" }), " ", "and", " ", _jsx("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", className: "wq-legal-consent-link", children: "Privacy Policy" }), "."] })] })] }), _jsxs("div", { className: "wq-legal-consent-section", children: [_jsx("h3", { className: "wq-legal-consent-heading", children: "2. Consent for study invitations / recruitment notifications (optional)" }), _jsx("p", { className: "wq-legal-consent-subtitle", children: "Receive study invitations (optional)" }), _jsx("p", { className: "wq-legal-consent-description", children: "If you opt in, you may be contacted about relevant studies. We will not share your contact or identity with a sponsor unless you explicitly accept a specific invitation." }), _jsxs("div", { className: "wq-legal-consent-info-box", children: [_jsx("p", { className: "wq-legal-consent-info-title", children: "What happens if I opt in?" }), _jsxs("ul", { className: "wq-legal-consent-info-list", children: [_jsx("li", { children: "You may receive emails/in-app messages about studies that match your profile." }), _jsx("li", { children: "If you click \"I'm interested\", we'll show you the details and ask for separate consent to share your contact or to pre-screen you \u2014 you control this per study." }), _jsx("li", { children: "You can opt out of invitations at any time." })] })] }), _jsxs("div", { className: "wq-legal-consent-checkbox-row", children: [renderCheckboxElement("study-consent", studyConsentAccepted, handleStudyConsentChange), _jsx("label", { htmlFor: "study-consent", className: "wq-legal-consent-label", children: "I consent to receive study invitations from Welshare. I can opt out anytime." })] })] })] }), _jsxs("div", { className: "wq-legal-consent-actions", children: [_jsx("button", { type: "button", className: "wq-button wq-button-outline", onClick: handleCancel, children: cancelButtonLabel }), _jsx("button", { type: "button", className: "wq-button wq-button-primary", onClick: handleConfirm, disabled: !termsAccepted, children: confirmButtonLabel })] })] }));
|
|
97
|
+
};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { QuestionRenderer } from "./components/question-renderer.js";
|
|
|
2
2
|
export type { QuestionRendererProps } from "./components/question-renderer.js";
|
|
3
3
|
export { BmiForm } from "./components/bmi-form.js";
|
|
4
4
|
export type { BmiFormProps } from "./components/bmi-form.js";
|
|
5
|
+
export { LegalConsentForm } from "./components/legal-consent-form.js";
|
|
6
|
+
export type { LegalConsentFormProps, LegalConsentResult, LegalCheckboxProps, LegalDocumentLinks, } from "./components/legal-consent-form.js";
|
|
5
7
|
export { QuestionnaireProvider, useQuestionnaire, type QuestionnaireContextType, type QuestionnaireProviderProps, } from "./contexts/questionnaire-context.js";
|
|
6
8
|
export type { Attachment, Coding, Extension, Questionnaire, QuestionnaireItem, QuestionnaireItemAnswerOption, QuestionnaireResponse, QuestionnaireResponseAnswer, QuestionnaireResponseItem, } from "./types/fhir.js";
|
|
7
9
|
export type { RadioInputProps, CheckboxInputProps, InputHelperConfig, HelperTriggerProps, } from "./types/index.js";
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,GAChC,MAAM,qCAAqC,CAAC;AAG7C,YAAY,EACV,UAAU,EACV,MAAM,EACN,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,6BAA6B,EAC7B,qBAAqB,EACrB,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Components
|
|
2
2
|
export { QuestionRenderer } from "./components/question-renderer.js";
|
|
3
3
|
export { BmiForm } from "./components/bmi-form.js";
|
|
4
|
+
export { LegalConsentForm } from "./components/legal-consent-form.js";
|
|
4
5
|
// Contexts
|
|
5
6
|
export { QuestionnaireProvider, useQuestionnaire, } from "./contexts/questionnaire-context.js";
|
|
6
7
|
// Utils
|
package/dist/esm/types/fhir.d.ts
CHANGED
|
@@ -1,85 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
valueInteger?: number;
|
|
21
|
-
valueDate?: string;
|
|
22
|
-
valueTime?: string;
|
|
23
|
-
valueString?: string;
|
|
24
|
-
initialSelected?: boolean;
|
|
25
|
-
extension?: Extension[];
|
|
26
|
-
};
|
|
27
|
-
export type Extension = {
|
|
28
|
-
url: string;
|
|
29
|
-
valueBoolean?: boolean;
|
|
30
|
-
valueString?: string;
|
|
31
|
-
valueInteger?: number;
|
|
32
|
-
valueCodeableConcept?: {
|
|
33
|
-
coding?: Coding[];
|
|
34
|
-
text?: string;
|
|
35
|
-
};
|
|
36
|
-
valueExpression?: {
|
|
37
|
-
language?: string;
|
|
38
|
-
expression?: string;
|
|
39
|
-
};
|
|
40
|
-
valueAttachment?: Attachment;
|
|
41
|
-
extension?: Extension[];
|
|
42
|
-
};
|
|
43
|
-
export type QuestionnaireItem = {
|
|
44
|
-
linkId: string;
|
|
45
|
-
definition?: string;
|
|
46
|
-
code?: Coding[];
|
|
47
|
-
prefix?: string;
|
|
48
|
-
text?: string;
|
|
49
|
-
type: "group" | "display" | "boolean" | "decimal" | "integer" | "date" | "dateTime" | "time" | "string" | "text" | "url" | "choice" | "open-choice" | "coding" | "attachment" | "reference" | "quantity";
|
|
50
|
-
enableWhen?: Array<{
|
|
51
|
-
question: string;
|
|
52
|
-
operator: string;
|
|
53
|
-
answerBoolean?: boolean;
|
|
54
|
-
answerDecimal?: number;
|
|
55
|
-
answerInteger?: number;
|
|
56
|
-
answerDate?: string;
|
|
57
|
-
answerDateTime?: string;
|
|
58
|
-
answerTime?: string;
|
|
59
|
-
answerString?: string;
|
|
60
|
-
answerCoding?: Coding;
|
|
61
|
-
}>;
|
|
62
|
-
enableBehavior?: string;
|
|
63
|
-
required?: boolean;
|
|
64
|
-
repeats?: boolean;
|
|
65
|
-
readOnly?: boolean;
|
|
66
|
-
maxLength?: number;
|
|
67
|
-
maxAnswers?: number;
|
|
68
|
-
answerValueSet?: string;
|
|
69
|
-
answerOption?: QuestionnaireItemAnswerOption[];
|
|
70
|
-
initial?: Array<{
|
|
71
|
-
valueBoolean?: boolean;
|
|
72
|
-
valueDecimal?: number;
|
|
73
|
-
valueInteger?: number;
|
|
74
|
-
valueDate?: string;
|
|
75
|
-
valueDateTime?: string;
|
|
76
|
-
valueTime?: string;
|
|
77
|
-
valueString?: string;
|
|
78
|
-
valueCoding?: Coding;
|
|
79
|
-
}>;
|
|
80
|
-
extension?: Extension[];
|
|
81
|
-
item?: QuestionnaireItem[];
|
|
82
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* FHIR Types for Questionnaire and QuestionnaireResponse
|
|
3
|
+
*
|
|
4
|
+
* These types are re-exported from @welshare/sdk/types to avoid duplication
|
|
5
|
+
* and ensure consistency across the monorepo.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This file uses `export type` to ensure no JavaScript runtime
|
|
8
|
+
* dependencies are pulled in from the SDK. Only TypeScript type definitions
|
|
9
|
+
* are imported.
|
|
10
|
+
*
|
|
11
|
+
* Note: FHIR R4 uses 'choice' and 'open-choice', while FHIR R5 uses 'coding'.
|
|
12
|
+
* Both are supported for backwards compatibility.
|
|
13
|
+
*/
|
|
14
|
+
export type { SimpleCoding as Coding, SimpleAttachment as Attachment, Extension, QuestionnaireItemAnswerOption, QuestionnaireItemType, QuestionnaireItem, QuestionnaireResponseAnswer, QuestionnaireResponseItem, SimpleQuestionnaireResponse as QuestionnaireResponse, } from "@welshare/sdk/types";
|
|
15
|
+
import type { QuestionnaireItem } from "@welshare/sdk/types";
|
|
16
|
+
/**
|
|
17
|
+
* Questionnaire type for rendering
|
|
18
|
+
* Based on FHIR R4/R5 Specifications with simplified item structure
|
|
19
|
+
*/
|
|
83
20
|
export type Questionnaire = {
|
|
84
21
|
resourceType: "Questionnaire";
|
|
85
22
|
id: string;
|
|
@@ -102,37 +39,4 @@ export type Questionnaire = {
|
|
|
102
39
|
description?: string;
|
|
103
40
|
item?: QuestionnaireItem[];
|
|
104
41
|
};
|
|
105
|
-
export type QuestionnaireResponseAnswer = {
|
|
106
|
-
valueBoolean?: boolean;
|
|
107
|
-
valueDecimal?: number;
|
|
108
|
-
valueInteger?: number;
|
|
109
|
-
valueDate?: string;
|
|
110
|
-
valueDateTime?: string;
|
|
111
|
-
valueTime?: string;
|
|
112
|
-
valueString?: string;
|
|
113
|
-
valueUri?: string;
|
|
114
|
-
valueCoding?: Coding;
|
|
115
|
-
valueQuantity?: {
|
|
116
|
-
value?: number;
|
|
117
|
-
unit?: string;
|
|
118
|
-
system?: string;
|
|
119
|
-
code?: string;
|
|
120
|
-
};
|
|
121
|
-
item?: QuestionnaireResponseItem[];
|
|
122
|
-
};
|
|
123
|
-
export type QuestionnaireResponseItem = {
|
|
124
|
-
linkId: string;
|
|
125
|
-
definition?: string;
|
|
126
|
-
text?: string;
|
|
127
|
-
answer?: QuestionnaireResponseAnswer[];
|
|
128
|
-
item?: QuestionnaireResponseItem[];
|
|
129
|
-
};
|
|
130
|
-
export type QuestionnaireResponse = {
|
|
131
|
-
resourceType: "QuestionnaireResponse";
|
|
132
|
-
id?: string;
|
|
133
|
-
questionnaire?: string;
|
|
134
|
-
status: "in-progress" | "completed" | "amended" | "entered-in-error" | "stopped";
|
|
135
|
-
authored?: string;
|
|
136
|
-
item?: QuestionnaireResponseItem[];
|
|
137
|
-
};
|
|
138
42
|
//# sourceMappingURL=fhir.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fhir.d.ts","sourceRoot":"","sources":["../../../src/types/fhir.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fhir.d.ts","sourceRoot":"","sources":["../../../src/types/fhir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,YAAY,EACV,YAAY,IAAI,MAAM,EACtB,gBAAgB,IAAI,UAAU,EAC9B,SAAS,EACT,6BAA6B,EAC7B,qBAAqB,EACrB,iBAAiB,EACjB,2BAA2B,EAC3B,yBAAyB,EACzB,2BAA2B,IAAI,qBAAqB,GACrD,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,EAAE,eAAe,CAAC;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,GAAG,CAAC,EAAE,KAAK,CAAC;YACV,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ,CAAC;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAC5B,CAAC"}
|
package/dist/esm/types/fhir.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* FHIR Types for Questionnaire and QuestionnaireResponse
|
|
3
|
+
*
|
|
4
|
+
* These types are re-exported from @welshare/sdk/types to avoid duplication
|
|
5
|
+
* and ensure consistency across the monorepo.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This file uses `export type` to ensure no JavaScript runtime
|
|
8
|
+
* dependencies are pulled in from the SDK. Only TypeScript type definitions
|
|
9
|
+
* are imported.
|
|
10
|
+
*
|
|
11
|
+
* Note: FHIR R4 uses 'choice' and 'open-choice', while FHIR R5 uses 'coding'.
|
|
12
|
+
* Both are supported for backwards compatibility.
|
|
13
|
+
*/
|
|
5
14
|
export {};
|
package/dist/styles.css
CHANGED
|
@@ -573,3 +573,156 @@
|
|
|
573
573
|
margin: 0;
|
|
574
574
|
line-height: 1.5;
|
|
575
575
|
}
|
|
576
|
+
|
|
577
|
+
/* === Legal Consent Form === */
|
|
578
|
+
.wq-legal-consent-form {
|
|
579
|
+
display: flex;
|
|
580
|
+
flex-direction: column;
|
|
581
|
+
gap: var(--wq-space-lg);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.wq-legal-consent-sections {
|
|
585
|
+
display: flex;
|
|
586
|
+
flex-direction: column;
|
|
587
|
+
gap: var(--wq-space-xl, 1.5rem);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.wq-legal-consent-section {
|
|
591
|
+
display: flex;
|
|
592
|
+
flex-direction: column;
|
|
593
|
+
gap: var(--wq-space-md);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.wq-legal-consent-heading {
|
|
597
|
+
font-size: var(--wq-font-size-base);
|
|
598
|
+
font-weight: var(--wq-font-weight-medium);
|
|
599
|
+
color: var(--wq-color-text-primary);
|
|
600
|
+
margin: 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.wq-legal-consent-subtitle {
|
|
604
|
+
font-size: var(--wq-font-size-sm);
|
|
605
|
+
font-weight: var(--wq-font-weight-medium);
|
|
606
|
+
color: var(--wq-color-text-primary);
|
|
607
|
+
margin: 0;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.wq-legal-consent-description {
|
|
611
|
+
font-size: var(--wq-font-size-sm);
|
|
612
|
+
color: var(--wq-color-text-secondary);
|
|
613
|
+
margin: 0;
|
|
614
|
+
line-height: 1.5;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.wq-legal-consent-checkbox-row {
|
|
618
|
+
display: flex;
|
|
619
|
+
align-items: flex-start;
|
|
620
|
+
gap: var(--wq-space-md);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.wq-legal-checkbox {
|
|
624
|
+
margin-top: 2px;
|
|
625
|
+
flex-shrink: 0;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.wq-legal-consent-label {
|
|
629
|
+
font-size: var(--wq-font-size-sm);
|
|
630
|
+
color: var(--wq-color-text-primary);
|
|
631
|
+
line-height: 1.5;
|
|
632
|
+
cursor: pointer;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.wq-legal-consent-link {
|
|
636
|
+
color: var(--wq-color-primary);
|
|
637
|
+
text-decoration: underline;
|
|
638
|
+
transition: text-decoration var(--wq-transition-fast);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.wq-legal-consent-link:hover {
|
|
642
|
+
text-decoration: none;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.wq-legal-consent-info-box {
|
|
646
|
+
padding: var(--wq-space-md);
|
|
647
|
+
background-color: var(--wq-color-background);
|
|
648
|
+
border-radius: var(--wq-radius-md);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.wq-legal-consent-info-title {
|
|
652
|
+
font-size: var(--wq-font-size-sm);
|
|
653
|
+
font-weight: var(--wq-font-weight-medium);
|
|
654
|
+
color: var(--wq-color-text-primary);
|
|
655
|
+
margin: 0 0 var(--wq-space-sm) 0;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.wq-legal-consent-info-list {
|
|
659
|
+
margin: 0;
|
|
660
|
+
padding-left: 0;
|
|
661
|
+
list-style: none;
|
|
662
|
+
display: flex;
|
|
663
|
+
flex-direction: column;
|
|
664
|
+
gap: var(--wq-space-xs);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.wq-legal-consent-info-list li {
|
|
668
|
+
font-size: var(--wq-font-size-sm);
|
|
669
|
+
color: var(--wq-color-text-secondary);
|
|
670
|
+
line-height: 1.5;
|
|
671
|
+
padding-left: var(--wq-space-md);
|
|
672
|
+
position: relative;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.wq-legal-consent-info-list li::before {
|
|
676
|
+
content: "•";
|
|
677
|
+
position: absolute;
|
|
678
|
+
left: 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.wq-legal-consent-actions {
|
|
682
|
+
display: flex;
|
|
683
|
+
gap: var(--wq-space-md);
|
|
684
|
+
padding-top: var(--wq-space-md);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/* === Buttons === */
|
|
688
|
+
.wq-button {
|
|
689
|
+
display: inline-flex;
|
|
690
|
+
align-items: center;
|
|
691
|
+
justify-content: center;
|
|
692
|
+
padding: var(--wq-input-padding-y) var(--wq-input-padding-x);
|
|
693
|
+
border-radius: var(--wq-radius-md);
|
|
694
|
+
font-size: var(--wq-font-size-base);
|
|
695
|
+
font-weight: var(--wq-font-weight-medium);
|
|
696
|
+
font-family: inherit;
|
|
697
|
+
cursor: pointer;
|
|
698
|
+
transition: all var(--wq-transition-fast);
|
|
699
|
+
flex: 1;
|
|
700
|
+
min-height: var(--wq-input-height);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.wq-button:disabled {
|
|
704
|
+
opacity: 0.5;
|
|
705
|
+
cursor: not-allowed;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
.wq-button-primary {
|
|
709
|
+
background-color: var(--wq-color-primary);
|
|
710
|
+
color: white;
|
|
711
|
+
border: var(--wq-border-width) solid var(--wq-color-primary);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.wq-button-primary:hover:not(:disabled) {
|
|
715
|
+
background-color: var(--wq-color-primary-hover);
|
|
716
|
+
border-color: var(--wq-color-primary-hover);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.wq-button-outline {
|
|
720
|
+
background-color: transparent;
|
|
721
|
+
color: var(--wq-color-text-primary);
|
|
722
|
+
border: var(--wq-border-width) solid var(--wq-color-border);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.wq-button-outline:hover:not(:disabled) {
|
|
726
|
+
background-color: var(--wq-color-background);
|
|
727
|
+
border-color: var(--wq-color-border-hover);
|
|
728
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@welshare/questionnaire",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "FHIR Questionnaire components for React with state management and validation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -38,8 +38,9 @@
|
|
|
38
38
|
"storybook": "^10.1.11",
|
|
39
39
|
"@storybook/react": "^10.1.11",
|
|
40
40
|
"@storybook/react-vite": "^10.1.11",
|
|
41
|
-
"@
|
|
42
|
-
"@workspace/typescript-config": "0.0.0"
|
|
41
|
+
"@welshare/sdk": "0.3.3",
|
|
42
|
+
"@workspace/typescript-config": "0.0.0",
|
|
43
|
+
"@workspace/eslint-config": "0.0.0"
|
|
43
44
|
},
|
|
44
45
|
"tshy": {
|
|
45
46
|
"dialects": [
|