@welshare/questionnaire 0.2.1 → 0.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/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
+ };
@@ -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";
@@ -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;AAG7D,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"}
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/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.1",
3
+ "version": "0.2.2",
4
4
  "description": "FHIR Questionnaire components for React with state management and validation",
5
5
  "keywords": [
6
6
  "react",