case-editor-tools 1.0.0-beta.15 → 1.0.0-beta.16
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/.editorconfig +13 -0
- package/{index.d.ts → CHANGELOG.md} +0 -0
- package/LICENSE +202 -0
- package/package.json +3 -3
- package/publish.sh +9 -0
- package/readme.md +18 -0
- package/rollup.config.js +49 -0
- package/src/constants/key-definitions.ts +14 -0
- package/src/exporter/index.ts +101 -0
- package/{expression-utils/expressionGen.js → src/expression-utils/expressionGen.ts} +26 -29
- package/src/expression-utils/studyEngineExpressions.ts +359 -0
- package/src/index.ts +1 -0
- package/{logger/index.d.ts → src/logger/index.ts} +1 -1
- package/src/logger/logger.ts +31 -0
- package/{surveys/index.d.ts → src/surveys/index.ts} +3 -3
- package/src/surveys/survey-editor/component-editor.ts +133 -0
- package/{surveys/survey-editor/data-types.d.ts → src/surveys/survey-editor/data-types.ts} +18 -13
- package/src/surveys/survey-editor/item-editor.ts +529 -0
- package/src/surveys/survey-editor/survey-editor.ts +261 -0
- package/src/surveys/survey-engine-expressions.ts +483 -0
- package/src/surveys/survey-items.ts +1490 -0
- package/src/surveys/types/group.ts +73 -0
- package/{surveys/types/index.d.ts → src/surveys/types/index.ts} +4 -4
- package/src/surveys/types/item-properties.ts +204 -0
- package/{surveys/types/item.d.ts → src/surveys/types/item.ts} +42 -22
- package/src/surveys/types/survey-definition.ts +83 -0
- package/src/surveys/utils/componentGenerators.ts +76 -0
- package/src/surveys/utils/optionDefGenerators.ts +195 -0
- package/{surveys/utils/randomKeyGenerator.js → src/surveys/utils/randomKeyGenerator.ts} +9 -14
- package/src/surveys/utils/simple-generators.ts +106 -0
- package/src/surveys/utils/simple-question-editor.ts +52 -0
- package/{types/duration.js → src/types/duration.ts} +30 -27
- package/src/types/expression.ts +22 -0
- package/src/types/study.ts +17 -0
- package/src/types/studyRules.ts +39 -0
- package/tsconfig.json +100 -0
- package/constants/key-definitions.d.ts +0 -13
- package/constants/key-definitions.js +0 -32
- package/constants/key-definitions.js.map +0 -1
- package/exporter/index.d.ts +0 -2
- package/exporter/index.js +0 -99
- package/exporter/index.js.map +0 -1
- package/expression-utils/expressionGen.d.ts +0 -2
- package/expression-utils/expressionGen.js.map +0 -1
- package/expression-utils/studyEngineExpressions.d.ts +0 -114
- package/expression-utils/studyEngineExpressions.js +0 -283
- package/expression-utils/studyEngineExpressions.js.map +0 -1
- package/index.js +0 -3
- package/index.js.map +0 -1
- package/item-editor-7f825952.js +0 -522
- package/item-editor-7f825952.js.map +0 -1
- package/logger/index.js +0 -10
- package/logger/index.js.map +0 -1
- package/logger/logger.d.ts +0 -9
- package/logger/logger.js +0 -31
- package/logger/logger.js.map +0 -1
- package/surveys/index.js +0 -26
- package/surveys/index.js.map +0 -1
- package/surveys/survey-editor/component-editor.d.ts +0 -84
- package/surveys/survey-editor/component-editor.js +0 -112
- package/surveys/survey-editor/component-editor.js.map +0 -1
- package/surveys/survey-editor/data-types.js +0 -3
- package/surveys/survey-editor/data-types.js.map +0 -1
- package/surveys/survey-editor/item-editor.d.ts +0 -65
- package/surveys/survey-editor/item-editor.js +0 -13
- package/surveys/survey-editor/item-editor.js.map +0 -1
- package/surveys/survey-editor/survey-editor.d.ts +0 -43
- package/surveys/survey-editor/survey-editor.js +0 -231
- package/surveys/survey-editor/survey-editor.js.map +0 -1
- package/surveys/survey-engine-expressions.d.ts +0 -120
- package/surveys/survey-engine-expressions.js +0 -407
- package/surveys/survey-engine-expressions.js.map +0 -1
- package/surveys/survey-items.d.ts +0 -132
- package/surveys/survey-items.js +0 -1018
- package/surveys/survey-items.js.map +0 -1
- package/surveys/types/group.d.ts +0 -23
- package/surveys/types/group.js +0 -71
- package/surveys/types/group.js.map +0 -1
- package/surveys/types/index.js +0 -20
- package/surveys/types/index.js.map +0 -1
- package/surveys/types/item-properties.d.ts +0 -189
- package/surveys/types/item-properties.js +0 -3
- package/surveys/types/item-properties.js.map +0 -1
- package/surveys/types/item.js +0 -34
- package/surveys/types/item.js.map +0 -1
- package/surveys/types/survey-definition.d.ts +0 -19
- package/surveys/types/survey-definition.js +0 -60
- package/surveys/types/survey-definition.js.map +0 -1
- package/surveys/utils/componentGenerators.d.ts +0 -24
- package/surveys/utils/componentGenerators.js +0 -58
- package/surveys/utils/componentGenerators.js.map +0 -1
- package/surveys/utils/optionDefGenerators.d.ts +0 -68
- package/surveys/utils/optionDefGenerators.js +0 -162
- package/surveys/utils/optionDefGenerators.js.map +0 -1
- package/surveys/utils/randomKeyGenerator.d.ts +0 -1
- package/surveys/utils/randomKeyGenerator.js.map +0 -1
- package/surveys/utils/simple-generators.d.ts +0 -13
- package/surveys/utils/simple-generators.js +0 -102
- package/surveys/utils/simple-generators.js.map +0 -1
- package/surveys/utils/simple-question-editor.d.ts +0 -14
- package/surveys/utils/simple-question-editor.js +0 -45
- package/surveys/utils/simple-question-editor.js.map +0 -1
- package/types/duration.d.ts +0 -9
- package/types/duration.js.map +0 -1
- package/types/expression.d.ts +0 -4
- package/types/expression.js +0 -23
- package/types/expression.js.map +0 -1
- package/types/study.d.ts +0 -13
- package/types/study.js +0 -3
- package/types/study.js.map +0 -1
- package/types/studyRules.d.ts +0 -8
- package/types/studyRules.js +0 -32
- package/types/studyRules.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Expression, SurveyGroupItem, SurveyItem } from "survey-engine/lib/data_types";
|
|
2
|
+
import { Logger } from "../../logger/logger";
|
|
3
|
+
import { ItemEditor } from "../survey-editor/item-editor";
|
|
4
|
+
import { generatePageBreak } from "../utils/simple-generators";
|
|
5
|
+
|
|
6
|
+
export abstract class Group {
|
|
7
|
+
groupEditor: ItemEditor;
|
|
8
|
+
key: string;
|
|
9
|
+
private parentKey: string;
|
|
10
|
+
|
|
11
|
+
constructor(parentKey: string, groupKey: string, selectionMethod?: Expression) {
|
|
12
|
+
this.parentKey = parentKey;
|
|
13
|
+
this.key = [this.parentKey, groupKey].join('.');
|
|
14
|
+
this.groupEditor = new ItemEditor(undefined, {
|
|
15
|
+
itemKey: this.key,
|
|
16
|
+
isGroup: true
|
|
17
|
+
});
|
|
18
|
+
if (!selectionMethod) {
|
|
19
|
+
this.groupEditor.setSelectionMethod({ name: 'sequential' });
|
|
20
|
+
} else {
|
|
21
|
+
this.groupEditor.setSelectionMethod(selectionMethod);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
abstract buildGroup(): void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if this group is currently part of a path, by checking if the key attribute exists in the path of the group.
|
|
29
|
+
* @param partialKey Is this key segment included somewhere in the parent key of this item.
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
isPartOf(partialKey: string): boolean {
|
|
33
|
+
return this.parentKey.includes(partialKey)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Use this method to retrieve the generated survey item
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
get() {
|
|
41
|
+
this.buildGroup();
|
|
42
|
+
this.checkDuplicateChildKeys();
|
|
43
|
+
return this.groupEditor.getItem();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addPageBreak() {
|
|
47
|
+
this.groupEditor.addSurveyItem(generatePageBreak(this.key));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addItem(item: SurveyItem, pageBreakAfter?: boolean) {
|
|
51
|
+
this.groupEditor.addSurveyItem(item);
|
|
52
|
+
if (pageBreakAfter) {
|
|
53
|
+
this.addPageBreak();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private checkDuplicateChildKeys() {
|
|
58
|
+
const group = this.groupEditor.getItem() as SurveyGroupItem;
|
|
59
|
+
const keys = group.items.map(item => item.key);
|
|
60
|
+
const hasDuplicates = keys.some((key, index) => {
|
|
61
|
+
const isDuplicateKey = keys.indexOf(key) !== index;
|
|
62
|
+
if (isDuplicateKey) {
|
|
63
|
+
Logger.error('Duplicate key:');
|
|
64
|
+
Logger.criticalError(`${key} is used twice (at index ${keys.indexOf(key)} and at index ${index})`);
|
|
65
|
+
}
|
|
66
|
+
return isDuplicateKey;
|
|
67
|
+
});
|
|
68
|
+
if (hasDuplicates) {
|
|
69
|
+
Logger.error('\nSurvey contains duplicate keys.\nPlease check the lines above for more infos.\nProcess stopped prematurely.')
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from './group';
|
|
2
|
-
export * from './item';
|
|
3
|
-
export * from './item-properties';
|
|
4
|
-
export * from './survey-definition';
|
|
1
|
+
export * from './group';
|
|
2
|
+
export * from './item';
|
|
3
|
+
export * from './item-properties';
|
|
4
|
+
export * from './survey-definition';
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ComponentProperties, Expression, ItemComponent, Validation } from "survey-engine/lib/data_types";
|
|
2
|
+
import { Duration } from "../../types/duration";
|
|
3
|
+
|
|
4
|
+
export interface StyledTextComponentProp {
|
|
5
|
+
content: Map<string, string>;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface OptionDef {
|
|
10
|
+
key: string;
|
|
11
|
+
role: string;
|
|
12
|
+
content?: Map<string, string>;
|
|
13
|
+
items?: Array<StyledTextComponentProp> | Array<OptionDef>;
|
|
14
|
+
description?: Map<string, string>;
|
|
15
|
+
displayCondition?: Expression;
|
|
16
|
+
disabled?: Expression;
|
|
17
|
+
style?: Array<{ key: string, value: string }>;
|
|
18
|
+
optionProps?: ComponentProperties;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface GenericQuestionProps {
|
|
22
|
+
parentKey: string;
|
|
23
|
+
itemKey: string;
|
|
24
|
+
version?: number;
|
|
25
|
+
questionText: Map<string, string> | Array<StyledTextComponentProp>;
|
|
26
|
+
questionSubText?: Map<string, string>;
|
|
27
|
+
titleClassName?: string;
|
|
28
|
+
helpGroupContent?: Array<{
|
|
29
|
+
content: Map<string, string>,
|
|
30
|
+
style?: Array<{ key: string, value: string }>,
|
|
31
|
+
}>;
|
|
32
|
+
condition?: Expression;
|
|
33
|
+
topDisplayCompoments?: Array<ItemComponent>;
|
|
34
|
+
bottomDisplayCompoments?: Array<ItemComponent>;
|
|
35
|
+
isRequired?: boolean;
|
|
36
|
+
footnoteText?: Map<string, string>;
|
|
37
|
+
customValidations?: Array<Validation>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Numeric Input
|
|
43
|
+
*/
|
|
44
|
+
export interface NumericInputProps {
|
|
45
|
+
inputLabel: Map<string, string>;
|
|
46
|
+
labelBehindInput?: boolean;
|
|
47
|
+
componentProperties?: ComponentProperties;
|
|
48
|
+
inputMaxWidth?: string;
|
|
49
|
+
disabled?: Expression;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type NumericInputQuestionProps = GenericQuestionProps & NumericInputProps;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Date Input
|
|
57
|
+
*/
|
|
58
|
+
export interface DateInputProps {
|
|
59
|
+
dateInputMode: 'YMD' | 'YM' | 'Y';
|
|
60
|
+
inputLabelText?: Map<string, string>;
|
|
61
|
+
placeholderText?: Map<string, string>;
|
|
62
|
+
minRelativeDate?: {
|
|
63
|
+
reference?: number | Expression;
|
|
64
|
+
delta: Duration;
|
|
65
|
+
};
|
|
66
|
+
maxRelativeDate?: {
|
|
67
|
+
reference?: number | Expression;
|
|
68
|
+
delta: Duration;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type DateInputQuestionProps = DateInputProps & GenericQuestionProps;
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Text Input
|
|
77
|
+
*/
|
|
78
|
+
export interface TextInputProps {
|
|
79
|
+
placeholderText?: Map<string, string>;
|
|
80
|
+
inputLabel?: Map<string, string>;
|
|
81
|
+
inputMaxWidth?: string;
|
|
82
|
+
className?: string;
|
|
83
|
+
maxLength?: number; // number of charachters allowed
|
|
84
|
+
disabled?: Expression;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type TextInputQuestionProps = TextInputProps & GenericQuestionProps;
|
|
88
|
+
|
|
89
|
+
export interface MultiLineTextInput extends GenericQuestionProps {
|
|
90
|
+
inputLabelText?: Map<string, string>;
|
|
91
|
+
placeholderText?: Map<string, string>;
|
|
92
|
+
maxLength?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Cloze Question
|
|
98
|
+
*/
|
|
99
|
+
export interface ClozeProps {
|
|
100
|
+
items: Array<OptionDef>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type ClozeQuestionProps = ClozeProps & GenericQuestionProps;
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Response Single Choice Likert Array
|
|
108
|
+
*/
|
|
109
|
+
export type ResponsiveSingleChoiceArrayVariant = 'horizontal' | 'vertical' | 'table';
|
|
110
|
+
|
|
111
|
+
export interface ResponsiveSingleChoiceArrayProps {
|
|
112
|
+
scaleOptions: Array<{
|
|
113
|
+
key: string;
|
|
114
|
+
className?: string;
|
|
115
|
+
content: Map<string, string> | StyledTextComponentProp[];
|
|
116
|
+
}>,
|
|
117
|
+
rows: Array<{
|
|
118
|
+
key: string;
|
|
119
|
+
content: Map<string, string> | StyledTextComponentProp[];
|
|
120
|
+
horizontalModeProps?: {
|
|
121
|
+
labelPlacement?: 'none' | 'top' | 'bottom';
|
|
122
|
+
className?: string;
|
|
123
|
+
},
|
|
124
|
+
verticalModeProps?: {
|
|
125
|
+
className?: string;
|
|
126
|
+
}
|
|
127
|
+
tableModeProps?: {
|
|
128
|
+
className?: string;
|
|
129
|
+
}
|
|
130
|
+
}>,
|
|
131
|
+
defaultMode: ResponsiveSingleChoiceArrayVariant;
|
|
132
|
+
responsiveModes?: {
|
|
133
|
+
sm?: ResponsiveSingleChoiceArrayVariant;
|
|
134
|
+
md?: ResponsiveSingleChoiceArrayVariant;
|
|
135
|
+
lg?: ResponsiveSingleChoiceArrayVariant;
|
|
136
|
+
xl?: ResponsiveSingleChoiceArrayVariant;
|
|
137
|
+
xxl?: ResponsiveSingleChoiceArrayVariant;
|
|
138
|
+
},
|
|
139
|
+
rgClassName?: string;
|
|
140
|
+
tableModeProps?: {
|
|
141
|
+
className?: string;
|
|
142
|
+
layout?: "fixed";
|
|
143
|
+
firstColWidth?: string;
|
|
144
|
+
optionHeaderClassName?: string;
|
|
145
|
+
hideRowBorder?: boolean;
|
|
146
|
+
},
|
|
147
|
+
horizontalModeProps?: {
|
|
148
|
+
hideRowBorder?: boolean;
|
|
149
|
+
},
|
|
150
|
+
verticalModeProps?: {
|
|
151
|
+
hideRowBorder?: boolean;
|
|
152
|
+
useReverseOptionOrder?: boolean;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export type ResponsiveSingleChoiceArrayQuestionProps = GenericQuestionProps & ResponsiveSingleChoiceArrayProps;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Response Bipolar Likert Array
|
|
159
|
+
*/
|
|
160
|
+
export type ResponsiveBipolarLikertArrayVariant = 'withLabelRow' | 'vertical' | 'table';
|
|
161
|
+
export interface ResponsiveBipolarLikertArrayProps {
|
|
162
|
+
scaleOptions: Array<{
|
|
163
|
+
key: string;
|
|
164
|
+
}>,
|
|
165
|
+
rows: Array<{
|
|
166
|
+
key: string;
|
|
167
|
+
startLabel: Map<string, string> | StyledTextComponentProp[];
|
|
168
|
+
endLabel: Map<string, string> | StyledTextComponentProp[];
|
|
169
|
+
withLabelRowModeProps?: {
|
|
170
|
+
className?: string;
|
|
171
|
+
},
|
|
172
|
+
verticalModeProps?: {
|
|
173
|
+
className?: string;
|
|
174
|
+
}
|
|
175
|
+
tableModeProps?: {
|
|
176
|
+
className?: string;
|
|
177
|
+
}
|
|
178
|
+
}>,
|
|
179
|
+
defaultMode: ResponsiveBipolarLikertArrayVariant;
|
|
180
|
+
responsiveModes?: {
|
|
181
|
+
sm?: ResponsiveBipolarLikertArrayVariant;
|
|
182
|
+
md?: ResponsiveBipolarLikertArrayVariant;
|
|
183
|
+
lg?: ResponsiveBipolarLikertArrayVariant;
|
|
184
|
+
xl?: ResponsiveBipolarLikertArrayVariant;
|
|
185
|
+
xxl?: ResponsiveBipolarLikertArrayVariant;
|
|
186
|
+
},
|
|
187
|
+
rgClassName?: string;
|
|
188
|
+
tableModeProps?: {
|
|
189
|
+
className?: string;
|
|
190
|
+
layout?: "fixed";
|
|
191
|
+
labelColWidth?: string;
|
|
192
|
+
hideRowBorder?: boolean;
|
|
193
|
+
},
|
|
194
|
+
withLabelRowModeProps?: {
|
|
195
|
+
hideRowBorder?: boolean;
|
|
196
|
+
maxLabelWidth?: string;
|
|
197
|
+
useBottomLabel?: boolean;
|
|
198
|
+
},
|
|
199
|
+
verticalModeProps?: {
|
|
200
|
+
hideRowBorder?: boolean;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export type ResponsiveBipolarLikertArrayQuestionProps = GenericQuestionProps & ResponsiveBipolarLikertArrayProps;
|
|
@@ -1,22 +1,42 @@
|
|
|
1
|
-
import { Expression, SurveySingleItem } from "survey-engine/lib/data_types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
protected
|
|
8
|
-
protected
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { Expression, SurveySingleItem } from "survey-engine/lib/data_types";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export abstract class Item {
|
|
5
|
+
key: string;
|
|
6
|
+
item: SurveySingleItem;
|
|
7
|
+
protected itemKey: string;
|
|
8
|
+
protected parentKey: string;
|
|
9
|
+
|
|
10
|
+
protected condition?: Expression;
|
|
11
|
+
protected isRequired?: boolean;
|
|
12
|
+
|
|
13
|
+
constructor(parentKey: string, itemKey: string) {
|
|
14
|
+
this.parentKey = parentKey;
|
|
15
|
+
this.itemKey = itemKey;
|
|
16
|
+
this.key = [this.parentKey, itemKey].join('.');
|
|
17
|
+
this.item = {
|
|
18
|
+
version: 0,
|
|
19
|
+
key: "not defined"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
abstract buildItem(): SurveySingleItem;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if this item is currently part of a path, by checking if the key attribute exists in the path of the group.
|
|
27
|
+
* @param partialKey Is this key segment included somewhere in the parent key of this item.
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
isPartOf(partialKey: string): boolean {
|
|
31
|
+
return this.parentKey.includes(partialKey)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Use this method to retrieve the generated survey item
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
get() {
|
|
39
|
+
this.item = this.buildItem();
|
|
40
|
+
return this.item;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SurveyGroupItem, SurveyItem } from "survey-engine/lib/data_types";
|
|
2
|
+
import { Logger } from "../../logger/logger";
|
|
3
|
+
import { ItemEditor } from "../survey-editor/item-editor";
|
|
4
|
+
import { SurveyEditor } from "../survey-editor/survey-editor";
|
|
5
|
+
import { generateLocStrings } from "../utils/simple-generators";
|
|
6
|
+
|
|
7
|
+
export interface SurveyProps {
|
|
8
|
+
surveyKey: string;
|
|
9
|
+
name: Map<string, string>;
|
|
10
|
+
description: Map<string, string>;
|
|
11
|
+
durationText: Map<string, string>;
|
|
12
|
+
// max item per page
|
|
13
|
+
// set prefill rules
|
|
14
|
+
// set context rules
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export abstract class SurveyDefinition {
|
|
19
|
+
key: string;
|
|
20
|
+
editor: SurveyEditor;
|
|
21
|
+
|
|
22
|
+
constructor(props: SurveyProps) {
|
|
23
|
+
this.editor = new SurveyEditor();
|
|
24
|
+
this.editor.changeItemKey('survey', props.surveyKey);
|
|
25
|
+
this.key = props.surveyKey;
|
|
26
|
+
|
|
27
|
+
// define name and description of the survey
|
|
28
|
+
this.editor.setSurveyName(generateLocStrings(
|
|
29
|
+
props.name
|
|
30
|
+
));
|
|
31
|
+
|
|
32
|
+
this.editor.setSurveyDescription(generateLocStrings(
|
|
33
|
+
props.description
|
|
34
|
+
));
|
|
35
|
+
|
|
36
|
+
this.editor.setSurveyDuration(generateLocStrings(
|
|
37
|
+
props.durationText
|
|
38
|
+
));
|
|
39
|
+
|
|
40
|
+
const rootItemEditor = new ItemEditor(this.editor.findSurveyItem(props.surveyKey) as SurveyGroupItem);
|
|
41
|
+
rootItemEditor.setSelectionMethod({ name: 'sequential' });
|
|
42
|
+
this.editor.updateSurveyItem(rootItemEditor.getItem());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
abstract buildSurvey(): void;
|
|
46
|
+
|
|
47
|
+
getSurvey() {
|
|
48
|
+
this.buildSurvey();
|
|
49
|
+
this.checkDuplicateChildKeys();
|
|
50
|
+
return this.editor.getSurvey();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getSurveyJSON(pretty?: boolean) {
|
|
54
|
+
this.buildSurvey();
|
|
55
|
+
this.checkDuplicateChildKeys();
|
|
56
|
+
return this.editor.getSurveyJSON(pretty);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
addItem(item: SurveyItem) {
|
|
60
|
+
this.editor.addExistingSurveyItem(item, this.key);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
addSurveyItemToPath(item: SurveyItem, parentKey: string) {
|
|
64
|
+
this.editor.addExistingSurveyItem(item, parentKey);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private checkDuplicateChildKeys() {
|
|
68
|
+
const group = this.editor.getSurvey().current.surveyDefinition;
|
|
69
|
+
const keys = group.items.map(item => item.key);
|
|
70
|
+
const hasDuplicates = keys.some((key, index) => {
|
|
71
|
+
const isDuplicateKey = keys.indexOf(key) !== index;
|
|
72
|
+
if (isDuplicateKey) {
|
|
73
|
+
Logger.error('Duplicate key:');
|
|
74
|
+
Logger.criticalError(`${key} is used twice (at index ${keys.indexOf(key)} and at index ${index})`);
|
|
75
|
+
}
|
|
76
|
+
return isDuplicateKey;
|
|
77
|
+
});
|
|
78
|
+
if (hasDuplicates) {
|
|
79
|
+
Logger.error('\nSurvey item contains duplicate keys.\nPlease check the lines above for more infos.\nProcess stopped prematurely.')
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Expression, ItemComponent } from "survey-engine/lib/data_types"
|
|
2
|
+
import { StyledTextComponentProp } from "../types/item-properties";
|
|
3
|
+
import { generateRandomKey } from "./randomKeyGenerator"
|
|
4
|
+
import { generateLocStrings } from "./simple-generators"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
interface CommonProps {
|
|
8
|
+
key?: string,
|
|
9
|
+
content?: Map<string, string> | StyledTextComponentProp[];
|
|
10
|
+
displayCondition?: Expression;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface TextProps extends CommonProps {
|
|
14
|
+
content: Map<string, string>;
|
|
15
|
+
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'li';
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const text = (props: TextProps): ItemComponent => {
|
|
20
|
+
const styles = [];
|
|
21
|
+
if (props.className !== undefined) {
|
|
22
|
+
styles.push({
|
|
23
|
+
key: 'className', value: props.className
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
if (props.variant !== undefined) {
|
|
27
|
+
styles.push({
|
|
28
|
+
key: 'variant', value: props.variant
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
key: generateRandomKey(3),
|
|
33
|
+
role: 'text',
|
|
34
|
+
style: styles.length > 0 ? styles : undefined,
|
|
35
|
+
content: generateLocStrings(props.content),
|
|
36
|
+
displayCondition: props.displayCondition,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface MarkdownProps extends CommonProps {
|
|
41
|
+
content: Map<string, string>;
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const markdown = (props: MarkdownProps): ItemComponent => {
|
|
46
|
+
const styles = [];
|
|
47
|
+
if (props.className !== undefined) {
|
|
48
|
+
styles.push({
|
|
49
|
+
key: 'className', value: props.className
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
key: generateRandomKey(3),
|
|
54
|
+
role: 'markdown',
|
|
55
|
+
style: styles.length > 0 ? styles : undefined,
|
|
56
|
+
content: generateLocStrings(props.content),
|
|
57
|
+
displayCondition: props.displayCondition,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const footnote = (props: {
|
|
62
|
+
content: Map<string, string>;
|
|
63
|
+
}): ItemComponent => {
|
|
64
|
+
return {
|
|
65
|
+
role: 'footnote', content: generateLocStrings(props.content), style: [
|
|
66
|
+
{ key: 'className', value: 'fs-small fst-italic text-center' }
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
export const ComponentGenerators = {
|
|
73
|
+
footnote,
|
|
74
|
+
text,
|
|
75
|
+
markdown,
|
|
76
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { Expression } from "survey-engine/lib/data_types";
|
|
2
|
+
import { SurveyEngine } from "../survey-engine-expressions";
|
|
3
|
+
import { ClozeProps, DateInputProps, NumericInputProps, OptionDef, StyledTextComponentProp, TextInputProps } from "../types/item-properties";
|
|
4
|
+
import { generateRandomKey } from "./randomKeyGenerator";
|
|
5
|
+
|
|
6
|
+
interface CommonProps {
|
|
7
|
+
key?: string,
|
|
8
|
+
content?: Map<string, string> | StyledTextComponentProp[];
|
|
9
|
+
displayCondition?: Expression;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface OptionProps {
|
|
13
|
+
disabled?: Expression;
|
|
14
|
+
className?: string;
|
|
15
|
+
displayCondition?: Expression;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface OptionTextProps extends CommonProps {
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const option = (key: string, content: Map<string, string> | StyledTextComponentProp[], extraProps?: OptionProps): OptionDef => {
|
|
23
|
+
const styles = [];
|
|
24
|
+
if (extraProps?.className !== undefined) {
|
|
25
|
+
styles.push({
|
|
26
|
+
key: 'className', value: extraProps.className
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
key: key,
|
|
32
|
+
role: 'option',
|
|
33
|
+
content: !Array.isArray(content) ? content : undefined,
|
|
34
|
+
items: Array.isArray(content) ? content : undefined,
|
|
35
|
+
displayCondition: extraProps?.displayCondition,
|
|
36
|
+
disabled: extraProps?.disabled,
|
|
37
|
+
style: styles,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const multipleChoiceOptionSubtitle = (props: OptionTextProps): OptionDef => {
|
|
42
|
+
const styles = [];
|
|
43
|
+
if (props.className !== undefined) {
|
|
44
|
+
styles.push({
|
|
45
|
+
key: 'className', value: props.className
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
key: props.key ? props.key : generateRandomKey(6),
|
|
50
|
+
role: 'text',
|
|
51
|
+
content: !Array.isArray(props.content) ? props.content : undefined,
|
|
52
|
+
displayCondition: props.displayCondition,
|
|
53
|
+
style: styles,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const markdown = (props: OptionTextProps): OptionDef => {
|
|
58
|
+
const styles = [];
|
|
59
|
+
if (props.className !== undefined) {
|
|
60
|
+
styles.push({
|
|
61
|
+
key: 'className', value: props.className
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
key: props.key ? props.key : generateRandomKey(6),
|
|
66
|
+
role: 'markdown',
|
|
67
|
+
content: !Array.isArray(props.content) ? props.content : undefined,
|
|
68
|
+
displayCondition: props.displayCondition,
|
|
69
|
+
style: styles,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const textInput = (props: TextInputProps & { key: string, displayCondition?: Expression }): OptionDef => {
|
|
74
|
+
const style: Array<{ key: string; value: string }> = [];
|
|
75
|
+
if (props.maxLength !== undefined) {
|
|
76
|
+
style.push({ key: 'maxLength', value: props.maxLength.toFixed(0) })
|
|
77
|
+
}
|
|
78
|
+
if (props.className !== undefined) {
|
|
79
|
+
style.push({ key: 'className', value: props.className })
|
|
80
|
+
}
|
|
81
|
+
if (props.inputMaxWidth !== undefined) {
|
|
82
|
+
style.push({ key: 'inputMaxWidth', value: props.inputMaxWidth })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
key: props.key,
|
|
87
|
+
role: 'input',
|
|
88
|
+
content: props.inputLabel,
|
|
89
|
+
description: props.placeholderText,
|
|
90
|
+
displayCondition: props.displayCondition,
|
|
91
|
+
disabled: props.disabled,
|
|
92
|
+
style: style.length > 0 ? style : undefined,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const numberInput = (props: NumericInputProps & { key: string, displayCondition?: Expression }): OptionDef => {
|
|
97
|
+
const style: Array<{ key: string; value: string }> = [];
|
|
98
|
+
if (props.inputMaxWidth !== undefined) {
|
|
99
|
+
style.push({ key: 'inputMaxWidth', value: props.inputMaxWidth })
|
|
100
|
+
}
|
|
101
|
+
if (props.labelBehindInput) {
|
|
102
|
+
style.push({ key: 'labelPlacement', value: 'after' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
key: props.key,
|
|
107
|
+
role: 'numberInput',
|
|
108
|
+
content: props.inputLabel,
|
|
109
|
+
displayCondition: props.displayCondition,
|
|
110
|
+
disabled: props.disabled,
|
|
111
|
+
optionProps: {
|
|
112
|
+
min: props.componentProperties?.min !== undefined ? (typeof (props.componentProperties.min) === 'number' ? { dtype: 'num', num: props.componentProperties.min } : props.componentProperties.min) : undefined,
|
|
113
|
+
max: props.componentProperties?.max !== undefined ? (typeof (props.componentProperties?.max) === 'number' ? { dtype: 'num', num: props.componentProperties.max } : props.componentProperties.max) : undefined,
|
|
114
|
+
stepSize: props.componentProperties?.stepSize ? (typeof (props.componentProperties.stepSize) === 'number' ? { dtype: 'num', num: props.componentProperties.stepSize } : props.componentProperties.stepSize) : undefined,
|
|
115
|
+
},
|
|
116
|
+
style: style.length > 0 ? style : undefined,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const dateInput = (props: DateInputProps & { key: string, displayCondition?: Expression }): OptionDef => {
|
|
121
|
+
return {
|
|
122
|
+
key: props.key,
|
|
123
|
+
role: 'dateInput',
|
|
124
|
+
optionProps: {
|
|
125
|
+
dateInputMode: { str: props.dateInputMode },
|
|
126
|
+
min: props.minRelativeDate ? {
|
|
127
|
+
dtype: 'exp', exp: SurveyEngine.timestampWithOffset(
|
|
128
|
+
props.minRelativeDate.delta,
|
|
129
|
+
props.minRelativeDate.reference
|
|
130
|
+
)
|
|
131
|
+
} : undefined,
|
|
132
|
+
max: props.maxRelativeDate ? {
|
|
133
|
+
dtype: 'exp', exp: SurveyEngine.timestampWithOffset(
|
|
134
|
+
props.maxRelativeDate.delta,
|
|
135
|
+
props.maxRelativeDate.reference ? props.maxRelativeDate.reference : undefined
|
|
136
|
+
)
|
|
137
|
+
} : undefined,
|
|
138
|
+
},
|
|
139
|
+
content: props.inputLabelText,
|
|
140
|
+
displayCondition: props.displayCondition,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const dropDown = (props: { key: string, displayCondition?: Expression, options: Array<OptionDef> }): OptionDef => {
|
|
145
|
+
return {
|
|
146
|
+
key: props.key,
|
|
147
|
+
role: 'dropDownGroup',
|
|
148
|
+
displayCondition: props.displayCondition,
|
|
149
|
+
items: props.options,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
const cloze = (props: ClozeProps & { key: string, displayCondition?: Expression }): OptionDef => {
|
|
155
|
+
return {
|
|
156
|
+
key: props.key,
|
|
157
|
+
role: 'cloze',
|
|
158
|
+
items: props.items,
|
|
159
|
+
displayCondition: props.displayCondition,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const clozeLineBreak = (): OptionDef => {
|
|
164
|
+
return {
|
|
165
|
+
key: generateRandomKey(5),
|
|
166
|
+
role: 'lineBreak',
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const SingleChoiceOptionTypes = {
|
|
171
|
+
option,
|
|
172
|
+
textInput,
|
|
173
|
+
//dateInput,
|
|
174
|
+
numberInput,
|
|
175
|
+
cloze,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const MultipleChoiceOptionTypes = {
|
|
179
|
+
option,
|
|
180
|
+
subtitle: multipleChoiceOptionSubtitle,
|
|
181
|
+
textInput,
|
|
182
|
+
// dateInput,
|
|
183
|
+
numberInput,
|
|
184
|
+
cloze,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const ClozeItemTypes = {
|
|
188
|
+
text: multipleChoiceOptionSubtitle,
|
|
189
|
+
markdown,
|
|
190
|
+
textInput,
|
|
191
|
+
dateInput,
|
|
192
|
+
clozeLineBreak,
|
|
193
|
+
numberInput,
|
|
194
|
+
dropDown,
|
|
195
|
+
}
|