@qaiddev/quests-embed 1.0.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.
- package/LICENSE +21 -0
- package/README.md +462 -0
- package/dist/embed.d.ts +71 -0
- package/dist/index.d.ts +36 -0
- package/dist/inputs.d.ts +34 -0
- package/dist/qaid-quests.js +752 -0
- package/dist/qaid-quests.js.map +1 -0
- package/dist/qaid-quests.umd.cjs +2 -0
- package/dist/qaid-quests.umd.cjs.map +1 -0
- package/dist/styles.d.ts +22 -0
- package/dist/types.d.ts +225 -0
- package/dist/visibility.d.ts +15 -0
- package/package.json +58 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Questionnaire schema — defines the form itself.
|
|
3
|
+
* Loaded from JSON, either inline or remote.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Predicate that gates a question's visibility based on prior answers.
|
|
7
|
+
* Predicates may only reference questions that appear EARLIER in the
|
|
8
|
+
* `questions` array; forward references are rejected at validation time.
|
|
9
|
+
*
|
|
10
|
+
* Composition: `allOf` / `anyOf` accept arrays of nested rules.
|
|
11
|
+
* Atom forms target a single prior question via `questionId`.
|
|
12
|
+
*/
|
|
13
|
+
export type VisibilityRule = {
|
|
14
|
+
allOf: VisibilityRule[];
|
|
15
|
+
} | {
|
|
16
|
+
anyOf: VisibilityRule[];
|
|
17
|
+
} | {
|
|
18
|
+
questionId: string;
|
|
19
|
+
equals: string | number;
|
|
20
|
+
} | {
|
|
21
|
+
questionId: string;
|
|
22
|
+
notEquals: string | number;
|
|
23
|
+
} | {
|
|
24
|
+
questionId: string;
|
|
25
|
+
in: Array<string | number>;
|
|
26
|
+
} | {
|
|
27
|
+
questionId: string;
|
|
28
|
+
answered: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Common fields on every question */
|
|
31
|
+
interface QuestionBase {
|
|
32
|
+
/** Stable id used as the answer key */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Question label shown above the input */
|
|
35
|
+
label: string;
|
|
36
|
+
/** Optional helper text shown below the label */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** Whether an answer is required to advance */
|
|
39
|
+
required?: boolean;
|
|
40
|
+
/** Predicate gating visibility; absent means always visible. */
|
|
41
|
+
visibleIf?: VisibilityRule;
|
|
42
|
+
}
|
|
43
|
+
export interface TextQuestion extends QuestionBase {
|
|
44
|
+
type: "text";
|
|
45
|
+
/** Input placeholder */
|
|
46
|
+
placeholder?: string;
|
|
47
|
+
/** Use a multi-line textarea */
|
|
48
|
+
multiline?: boolean;
|
|
49
|
+
/** Max length */
|
|
50
|
+
maxLength?: number;
|
|
51
|
+
/** Min length (only enforced when required) */
|
|
52
|
+
minLength?: number;
|
|
53
|
+
/** Native input type for short fields ("text" | "email" | "tel" | "url"). Default "text" */
|
|
54
|
+
inputType?: "text" | "email" | "tel" | "url";
|
|
55
|
+
}
|
|
56
|
+
export interface CurrencyQuestion extends QuestionBase {
|
|
57
|
+
type: "currency";
|
|
58
|
+
/** ISO currency code, e.g. "USD". Default "USD" */
|
|
59
|
+
currency?: string;
|
|
60
|
+
/** Minimum value */
|
|
61
|
+
min?: number;
|
|
62
|
+
/** Maximum value */
|
|
63
|
+
max?: number;
|
|
64
|
+
/** Locale for number formatting. Default browser locale */
|
|
65
|
+
locale?: string;
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface RangeQuestion extends QuestionBase {
|
|
69
|
+
type: "range";
|
|
70
|
+
min: number;
|
|
71
|
+
max: number;
|
|
72
|
+
/** Step size. Default 1 */
|
|
73
|
+
step?: number;
|
|
74
|
+
/** Initial value when no answer yet. Default min */
|
|
75
|
+
defaultValue?: number;
|
|
76
|
+
/** Suffix shown next to the value (e.g. "/10", "%") */
|
|
77
|
+
unit?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface DateQuestion extends QuestionBase {
|
|
80
|
+
type: "date";
|
|
81
|
+
/** ISO date string for min */
|
|
82
|
+
min?: string;
|
|
83
|
+
/** ISO date string for max */
|
|
84
|
+
max?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface MultipleChoiceOption {
|
|
87
|
+
value: string;
|
|
88
|
+
label: string;
|
|
89
|
+
/** Optional description shown below the option label */
|
|
90
|
+
description?: string;
|
|
91
|
+
/** Optional image URL. Rendered as a 1:1 thumbnail next to or above
|
|
92
|
+
* the label, depending on the question's `imageAlignment`. */
|
|
93
|
+
image?: string;
|
|
94
|
+
}
|
|
95
|
+
export interface MultipleChoiceQuestion extends QuestionBase {
|
|
96
|
+
type: "multiple-choice";
|
|
97
|
+
options: MultipleChoiceOption[];
|
|
98
|
+
/** Allow selecting multiple options. Answer becomes string[]. Default false */
|
|
99
|
+
multiple?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Layout for option images (only meaningful when at least one option
|
|
102
|
+
* has `image` set).
|
|
103
|
+
* - "horizontal" (default): image to the left of the label
|
|
104
|
+
* - "vertical": image above the label, centered
|
|
105
|
+
*/
|
|
106
|
+
imageAlignment?: "horizontal" | "vertical";
|
|
107
|
+
}
|
|
108
|
+
export type Question = TextQuestion | CurrencyQuestion | RangeQuestion | DateQuestion | MultipleChoiceQuestion;
|
|
109
|
+
/** Map of answers keyed by question id */
|
|
110
|
+
export type AnswerValue = string | number | string[] | null;
|
|
111
|
+
export type Answers = Record<string, AnswerValue>;
|
|
112
|
+
/** Top-level questionnaire definition (the JSON loaded from configUrl) */
|
|
113
|
+
export interface Questionnaire {
|
|
114
|
+
/** Stable identifier for this questionnaire */
|
|
115
|
+
id?: string;
|
|
116
|
+
/** Title shown at the top of the form */
|
|
117
|
+
title?: string;
|
|
118
|
+
/** Optional description shown under the title */
|
|
119
|
+
description?: string;
|
|
120
|
+
/** Text shown when the form is finished. Default "Thank you!" */
|
|
121
|
+
thankYouTitle?: string;
|
|
122
|
+
/** Subtitle shown when the form is finished */
|
|
123
|
+
thankYouMessage?: string;
|
|
124
|
+
/** Submit button label on the last step. Default "Submit" */
|
|
125
|
+
submitLabel?: string;
|
|
126
|
+
/** Next button label. Default "Next" */
|
|
127
|
+
nextLabel?: string;
|
|
128
|
+
/** Back button label. Default "Back" */
|
|
129
|
+
backLabel?: string;
|
|
130
|
+
questions: Question[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Configuration for the QaidQuests embed.
|
|
134
|
+
* Mirrors the theming surface of @qaiddev/thumbs-embed so themes
|
|
135
|
+
* (CSS variable values + injected CSS) work in both embeds.
|
|
136
|
+
*/
|
|
137
|
+
export interface QuestsConfig {
|
|
138
|
+
/** Required: API endpoint URL for storing answers */
|
|
139
|
+
endpoint: string;
|
|
140
|
+
/** API key for authenticating with the service */
|
|
141
|
+
apiKey?: string;
|
|
142
|
+
/** Inline questionnaire definition (takes precedence over configUrl) */
|
|
143
|
+
questionnaire?: Questionnaire;
|
|
144
|
+
/** URL to fetch the questionnaire JSON from */
|
|
145
|
+
configUrl?: string;
|
|
146
|
+
/** CSS selector for user-provided container. If absent, opens as a modal */
|
|
147
|
+
container?: string;
|
|
148
|
+
/** z-index for modal-mode embed. Default: 50 */
|
|
149
|
+
zIndex?: number;
|
|
150
|
+
/** Custom theme colors — same names as thumbs-embed for reuse */
|
|
151
|
+
colors?: {
|
|
152
|
+
/** Primary accent (used for progress / focus / submit) */
|
|
153
|
+
positive?: string;
|
|
154
|
+
/** Error / destructive */
|
|
155
|
+
negative?: string;
|
|
156
|
+
/** Selection / highlight */
|
|
157
|
+
marker?: string;
|
|
158
|
+
};
|
|
159
|
+
/** Modal width in pixels. Default: 480 */
|
|
160
|
+
modalWidth?: number;
|
|
161
|
+
/** Backdrop opacity (0-1). Default: 0.4 */
|
|
162
|
+
backdropOpacity?: number;
|
|
163
|
+
/** Font family. Default: system-ui, -apple-system, sans-serif */
|
|
164
|
+
fontFamily?: string;
|
|
165
|
+
/** Base font size in pixels. Default: 16 */
|
|
166
|
+
fontSize?: number;
|
|
167
|
+
/** Custom CSS injected into the shadow root for theming */
|
|
168
|
+
css?: string;
|
|
169
|
+
/** When true, advances automatically on selection for multiple-choice (single) and range. Default: false */
|
|
170
|
+
autoAdvance?: boolean;
|
|
171
|
+
/** Debounce in ms for autosave on text/currency. Default: 500 */
|
|
172
|
+
saveDebounceMs?: number;
|
|
173
|
+
/** Auto-focus the input on each step. Default: true. Set false in preview/embedded contexts that shouldn't steal focus. */
|
|
174
|
+
autoFocus?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Where to render the step counter ("1 / 5") and progress bar.
|
|
177
|
+
* - "top" (default): inline at the top of the card, above the question
|
|
178
|
+
* - "bottom": inline in the footer next to the Next button — useful
|
|
179
|
+
* for compact / above-the-fold layouts where you want the question
|
|
180
|
+
* to sit as high as possible.
|
|
181
|
+
*/
|
|
182
|
+
progressPosition?: "top" | "bottom";
|
|
183
|
+
}
|
|
184
|
+
export interface ResolvedQuestsConfig {
|
|
185
|
+
endpoint: string;
|
|
186
|
+
apiKey: string;
|
|
187
|
+
container: string;
|
|
188
|
+
zIndex: number;
|
|
189
|
+
colors: {
|
|
190
|
+
positive: string;
|
|
191
|
+
negative: string;
|
|
192
|
+
marker: string;
|
|
193
|
+
};
|
|
194
|
+
modalWidth: number;
|
|
195
|
+
backdropOpacity: number;
|
|
196
|
+
fontFamily: string;
|
|
197
|
+
fontSize: number;
|
|
198
|
+
css: string;
|
|
199
|
+
autoAdvance: boolean;
|
|
200
|
+
saveDebounceMs: number;
|
|
201
|
+
autoFocus: boolean;
|
|
202
|
+
progressPosition: "top" | "bottom";
|
|
203
|
+
}
|
|
204
|
+
/** Initial payload sent to create the response */
|
|
205
|
+
export interface CreateResponsePayload {
|
|
206
|
+
apiKey?: string;
|
|
207
|
+
questId?: string;
|
|
208
|
+
pageUrl: string;
|
|
209
|
+
visitorId: string;
|
|
210
|
+
userAgent?: string;
|
|
211
|
+
}
|
|
212
|
+
export interface CreateResponseResult {
|
|
213
|
+
/** Server-assigned id used for subsequent answer updates */
|
|
214
|
+
id: string | number;
|
|
215
|
+
}
|
|
216
|
+
/** Sent when an answer changes */
|
|
217
|
+
export interface UpdateAnswerPayload {
|
|
218
|
+
questionId: string;
|
|
219
|
+
value: AnswerValue;
|
|
220
|
+
}
|
|
221
|
+
/** Sent when the form is submitted */
|
|
222
|
+
export interface SubmitPayload {
|
|
223
|
+
answers: Answers;
|
|
224
|
+
}
|
|
225
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for evaluating per-question visibility predicates.
|
|
3
|
+
*
|
|
4
|
+
* `getVisibleQuestions` filters a questionnaire's flat question array down
|
|
5
|
+
* to those whose `visibleIf` predicate currently evaluates to true. A
|
|
6
|
+
* question with no `visibleIf` is always visible.
|
|
7
|
+
*
|
|
8
|
+
* Predicates are evaluated against the answers map (the same shape the
|
|
9
|
+
* embed maintains internally and posts on submit). They never throw — a
|
|
10
|
+
* predicate referencing an unknown questionId resolves as if that
|
|
11
|
+
* question is unanswered.
|
|
12
|
+
*/
|
|
13
|
+
import type { Answers, Question, Questionnaire, VisibilityRule } from "./types";
|
|
14
|
+
export declare function getVisibleQuestions(q: Questionnaire, answers: Answers): Question[];
|
|
15
|
+
export declare function evaluateRule(rule: VisibilityRule, answers: Answers): boolean;
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qaiddev/quests-embed",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Have Questions for your Users?",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/qaid-quests.umd.cjs",
|
|
7
|
+
"module": "./dist/qaid-quests.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/qaid-quests.js",
|
|
13
|
+
"require": "./dist/qaid-quests.umd.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/qaiddev/quests-embed.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/qaiddev/quests-embed#readme",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/qaiddev/quests-embed/issues"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "vite build --watch",
|
|
32
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
33
|
+
"preview": "vite preview",
|
|
34
|
+
"test": "vitest",
|
|
35
|
+
"test:run": "vitest run",
|
|
36
|
+
"prepublishOnly": "npm run test:run && npm run build"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
40
|
+
"happy-dom": "^20.5.1",
|
|
41
|
+
"typescript": "^5.3.3",
|
|
42
|
+
"vite": "^7.3.1",
|
|
43
|
+
"vitest": "^4.0.18"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"quests",
|
|
47
|
+
"questionnaire",
|
|
48
|
+
"question",
|
|
49
|
+
"answer",
|
|
50
|
+
"feedback",
|
|
51
|
+
"QA",
|
|
52
|
+
"form",
|
|
53
|
+
"survey",
|
|
54
|
+
"embed",
|
|
55
|
+
"qaid"
|
|
56
|
+
],
|
|
57
|
+
"license": "MIT"
|
|
58
|
+
}
|