@case-framework/survey-core 0.1.0 → 0.3.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/README.md +181 -0
- package/build/editor.d.mts +169 -5
- package/build/editor.d.mts.map +1 -1
- package/build/editor.mjs +429 -12
- package/build/editor.mjs.map +1 -1
- package/build/index.d.mts +197 -6
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +365 -42
- package/build/index.mjs.map +1 -1
- package/build/{package.json/package.json → package.json} +6 -6
- package/build/{survey-C3ZHI-5z.mjs → survey-DJbgFVPz.mjs} +298 -198
- package/build/survey-DJbgFVPz.mjs.map +1 -0
- package/build/{survey-TUPUXiXl.d.mts → survey-wnGyIY66.d.mts} +81 -6
- package/build/survey-wnGyIY66.d.mts.map +1 -0
- package/package.json +6 -6
- package/build/survey-C3ZHI-5z.mjs.map +0 -1
- package/build/survey-TUPUXiXl.d.mts.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@case-framework/survey-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Implementation of the survey core to use in typescript/javascript projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"author": "phev8",
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@eslint/js": "^9.39.
|
|
21
|
+
"@eslint/js": "^9.39.4",
|
|
22
22
|
"@types/jest": "^30.0.0",
|
|
23
|
-
"eslint": "^9.39.
|
|
24
|
-
"jest": "^30.0
|
|
23
|
+
"eslint": "^9.39.4",
|
|
24
|
+
"jest": "^30.3.0",
|
|
25
25
|
"ts-jest": "^29.4.6",
|
|
26
|
-
"tsdown": "^0.
|
|
26
|
+
"tsdown": "^0.21.4",
|
|
27
27
|
"typescript": "^5.8.3",
|
|
28
|
-
"typescript-eslint": "^8.
|
|
28
|
+
"typescript-eslint": "^8.57.1"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"date-fns": "^4.1.0"
|
|
@@ -31,7 +31,19 @@ function generateId() {
|
|
|
31
31
|
for (let i = 0; i < length; i++) randomPart += ALPHABET.charAt(Math.floor(Math.random() * base));
|
|
32
32
|
return randomPart + encodedTimestamp;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
/** Alphanumeric chars without ambiguous ones (0/O, 1/I) for human-readable coding keys. */
|
|
35
|
+
const CODING_KEY_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
36
|
+
/**
|
|
37
|
+
* Generates a short random coding key suitable for item keys in exports/codebooks.
|
|
38
|
+
* @param length - Key length (default 4)
|
|
39
|
+
* @returns A random string of the specified length.
|
|
40
|
+
*/
|
|
41
|
+
function generateCodingKey(length = 4) {
|
|
42
|
+
const base = 32;
|
|
43
|
+
let key = "";
|
|
44
|
+
for (let i = 0; i < length; i++) key += CODING_KEY_ALPHABET.charAt(Math.floor(Math.random() * base));
|
|
45
|
+
return key;
|
|
46
|
+
}
|
|
35
47
|
//#endregion
|
|
36
48
|
//#region src/survey/utils/value-reference.ts
|
|
37
49
|
const SEPARATOR = "...";
|
|
@@ -77,7 +89,6 @@ let ReferenceUsageType = /* @__PURE__ */ function(ReferenceUsageType) {
|
|
|
77
89
|
ReferenceUsageType["disabledConditions"] = "disabledConditions";
|
|
78
90
|
return ReferenceUsageType;
|
|
79
91
|
}({});
|
|
80
|
-
|
|
81
92
|
//#endregion
|
|
82
93
|
//#region src/expressions/expression.ts
|
|
83
94
|
const ExpressionType = {
|
|
@@ -247,7 +258,6 @@ var FunctionExpression = class FunctionExpression extends Expression {
|
|
|
247
258
|
};
|
|
248
259
|
}
|
|
249
260
|
};
|
|
250
|
-
|
|
251
261
|
//#endregion
|
|
252
262
|
//#region src/expressions/template-value.ts
|
|
253
263
|
let TemplateDefTypes = /* @__PURE__ */ function(TemplateDefTypes) {
|
|
@@ -280,7 +290,34 @@ const deserializeTemplateValue = (json) => {
|
|
|
280
290
|
const deserializeTemplateValues = (json) => {
|
|
281
291
|
return new Map(Object.entries(json).map(([key, value]) => [key, deserializeTemplateValue(value)]));
|
|
282
292
|
};
|
|
283
|
-
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/survey/responses/value-types.ts
|
|
295
|
+
const ValueType = {
|
|
296
|
+
string: "string",
|
|
297
|
+
duration: "duration",
|
|
298
|
+
reference: "reference",
|
|
299
|
+
number: "number",
|
|
300
|
+
boolean: "boolean",
|
|
301
|
+
date: "date",
|
|
302
|
+
stringArray: "string[]",
|
|
303
|
+
durationArray: "duration[]",
|
|
304
|
+
referenceArray: "reference[]",
|
|
305
|
+
numberArray: "number[]",
|
|
306
|
+
dateArray: "date[]"
|
|
307
|
+
};
|
|
308
|
+
const DurationUnits = {
|
|
309
|
+
seconds: "seconds",
|
|
310
|
+
minutes: "minutes",
|
|
311
|
+
hours: "hours",
|
|
312
|
+
days: "days",
|
|
313
|
+
weeks: "weeks",
|
|
314
|
+
months: "months",
|
|
315
|
+
years: "years"
|
|
316
|
+
};
|
|
317
|
+
const NumberPrecision = {
|
|
318
|
+
int: "int",
|
|
319
|
+
float: "float"
|
|
320
|
+
};
|
|
284
321
|
//#endregion
|
|
285
322
|
//#region src/survey/items/utils.ts
|
|
286
323
|
const displayConditionsFromJson = (json) => {
|
|
@@ -292,7 +329,6 @@ const displayConditionsFromJson = (json) => {
|
|
|
292
329
|
const disabledConditionsFromJson = (json) => {
|
|
293
330
|
return { components: json.components ? Object.fromEntries(Object.entries(json.components).map(([key, value]) => [key, Expression.deserialize(value)])) : void 0 };
|
|
294
331
|
};
|
|
295
|
-
|
|
296
332
|
//#endregion
|
|
297
333
|
//#region src/survey/items/survey-item.ts
|
|
298
334
|
/**
|
|
@@ -316,9 +352,31 @@ var SurveyItemCore = class {
|
|
|
316
352
|
this.key = this._rawItem.key;
|
|
317
353
|
this.config = this.parseConfig(this._rawItem.config);
|
|
318
354
|
}
|
|
355
|
+
getAdditionalResponseValueSlots() {
|
|
356
|
+
return {};
|
|
357
|
+
}
|
|
358
|
+
getAvailableResponseValueSlots(byType) {
|
|
359
|
+
const valueRefs = {};
|
|
360
|
+
for (const slot of this.getResponseSlotDefinitions()) {
|
|
361
|
+
valueRefs[`${this.id}...${ValueReferenceMethod.get}...${slot.slotId}`] = slot.valueType;
|
|
362
|
+
valueRefs[`${this.id}...${ValueReferenceMethod.isDefined}...${slot.slotId}`] = ValueType.boolean;
|
|
363
|
+
}
|
|
364
|
+
Object.assign(valueRefs, this.getAdditionalResponseValueSlots());
|
|
365
|
+
if (!byType) return valueRefs;
|
|
366
|
+
return Object.fromEntries(Object.entries(valueRefs).filter(([, valueType]) => valueType === byType));
|
|
367
|
+
}
|
|
319
368
|
get rawItem() {
|
|
320
369
|
return this._rawItem;
|
|
321
370
|
}
|
|
371
|
+
get metadata() {
|
|
372
|
+
return this._rawItem.metadata ?? {};
|
|
373
|
+
}
|
|
374
|
+
set metadata(metadata) {
|
|
375
|
+
this.updateRawItem({
|
|
376
|
+
...this._rawItem,
|
|
377
|
+
metadata
|
|
378
|
+
});
|
|
379
|
+
}
|
|
322
380
|
updateRawItem(rawItem) {
|
|
323
381
|
if (rawItem.id !== this.id) throw new Error("Cannot update raw item with a different id");
|
|
324
382
|
this._rawItem = rawItem;
|
|
@@ -362,7 +420,6 @@ var SurveyItemCore = class {
|
|
|
362
420
|
return usages;
|
|
363
421
|
}
|
|
364
422
|
};
|
|
365
|
-
|
|
366
423
|
//#endregion
|
|
367
424
|
//#region src/survey/items/types.ts
|
|
368
425
|
let ReservedSurveyItemTypes = /* @__PURE__ */ function(ReservedSurveyItemTypes) {
|
|
@@ -370,7 +427,6 @@ let ReservedSurveyItemTypes = /* @__PURE__ */ function(ReservedSurveyItemTypes)
|
|
|
370
427
|
ReservedSurveyItemTypes["PageBreak"] = "page-break";
|
|
371
428
|
return ReservedSurveyItemTypes;
|
|
372
429
|
}({});
|
|
373
|
-
|
|
374
430
|
//#endregion
|
|
375
431
|
//#region src/survey/item-key.ts
|
|
376
432
|
/**
|
|
@@ -407,11 +463,210 @@ var SurveyItemKey = class {
|
|
|
407
463
|
this._keySeparator = keySeparator;
|
|
408
464
|
}
|
|
409
465
|
};
|
|
410
|
-
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/survey/utils/group-utils.ts
|
|
468
|
+
/**
|
|
469
|
+
* Shared utilities for array operations.
|
|
470
|
+
*/
|
|
471
|
+
/**
|
|
472
|
+
* Swap two elements in an array by index.
|
|
473
|
+
* @returns A new array with the elements swapped
|
|
474
|
+
*/
|
|
475
|
+
function swapArrayElements(arr, fromIndex, toIndex) {
|
|
476
|
+
const result = [...arr];
|
|
477
|
+
[result[fromIndex], result[toIndex]] = [result[toIndex], result[fromIndex]];
|
|
478
|
+
return result;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Move an element from one index to another.
|
|
482
|
+
* @returns A new array with the element moved
|
|
483
|
+
*/
|
|
484
|
+
function moveArrayElement(arr, fromIndex, toIndex) {
|
|
485
|
+
const result = [...arr];
|
|
486
|
+
const [moved] = result.splice(fromIndex, 1);
|
|
487
|
+
result.splice(toIndex, 0, moved);
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region src/survey/registry/built-in-items.ts
|
|
492
|
+
var GroupItemCore = class extends SurveyItemCore {
|
|
493
|
+
type = ReservedSurveyItemTypes.Group;
|
|
494
|
+
parseConfig(rawConfig) {
|
|
495
|
+
const cfg = rawConfig ?? {};
|
|
496
|
+
return {
|
|
497
|
+
items: cfg.items,
|
|
498
|
+
shuffleItems: cfg.shuffleItems,
|
|
499
|
+
isRoot: cfg.isRoot
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
serializeConfig() {
|
|
503
|
+
return this.config;
|
|
504
|
+
}
|
|
505
|
+
isInteractive() {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
getResponseSlotDefinitions() {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
isRoot() {
|
|
512
|
+
return this.config.isRoot ?? false;
|
|
513
|
+
}
|
|
514
|
+
get items() {
|
|
515
|
+
return this.config.items ?? [];
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Add a child item id to this group at the given index.
|
|
519
|
+
* @param itemId The id of the item to add to this group.
|
|
520
|
+
* @param index The index at which to add the item. If not provided, the item is added to the end of the group.
|
|
521
|
+
*/
|
|
522
|
+
addChild(itemId, index) {
|
|
523
|
+
const items = this.config.items ??= [];
|
|
524
|
+
const insertIndex = index !== void 0 ? Math.min(index, items.length) : items.length;
|
|
525
|
+
items.splice(insertIndex, 0, itemId);
|
|
526
|
+
this.updateRawItem({
|
|
527
|
+
...this.rawItem,
|
|
528
|
+
config: { ...this.config }
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Remove a child item id from this group.
|
|
533
|
+
* @param itemId The id of the item to remove from this group.
|
|
534
|
+
*/
|
|
535
|
+
removeChild(itemId) {
|
|
536
|
+
const items = this.config.items ??= [];
|
|
537
|
+
const index = items.indexOf(itemId);
|
|
538
|
+
if (index !== -1) {
|
|
539
|
+
items.splice(index, 1);
|
|
540
|
+
this.updateRawItem({
|
|
541
|
+
...this.rawItem,
|
|
542
|
+
config: { ...this.config }
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Check if an item is a direct child of this group.
|
|
548
|
+
*/
|
|
549
|
+
hasChild(itemId) {
|
|
550
|
+
return this.items.includes(itemId);
|
|
551
|
+
}
|
|
552
|
+
/** Whether items in this group should be shuffled. */
|
|
553
|
+
get shuffleItems() {
|
|
554
|
+
return this.config.shuffleItems ?? false;
|
|
555
|
+
}
|
|
556
|
+
set shuffleItems(value) {
|
|
557
|
+
this.config.shuffleItems = value;
|
|
558
|
+
this.updateRawItem({
|
|
559
|
+
...this.rawItem,
|
|
560
|
+
config: { ...this.config }
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Get the ordered list of child item ids.
|
|
565
|
+
*/
|
|
566
|
+
getChildrenIds() {
|
|
567
|
+
return [...this.items];
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Swap two items by their positions in the group.
|
|
571
|
+
* @throws Error if indices are out of bounds
|
|
572
|
+
*/
|
|
573
|
+
swapItemsByIndex(from, to) {
|
|
574
|
+
const items = this.items;
|
|
575
|
+
if (from < 0 || from >= items.length || to < 0 || to >= items.length) throw new Error(`Index out of bounds. Valid range is 0-${items.length - 1}`);
|
|
576
|
+
if (from === to) return;
|
|
577
|
+
this.config.items = swapArrayElements(items, from, to);
|
|
578
|
+
this.updateRawItem({
|
|
579
|
+
...this.rawItem,
|
|
580
|
+
config: { ...this.config }
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Swap two items by their ids.
|
|
585
|
+
* @throws Error if either id is not found in this group
|
|
586
|
+
*/
|
|
587
|
+
swapItemsById(id, withId) {
|
|
588
|
+
const items = this.items;
|
|
589
|
+
const index1 = items.indexOf(id);
|
|
590
|
+
const index2 = items.indexOf(withId);
|
|
591
|
+
if (index1 === -1) throw new Error(`Item '${id}' not found in group '${this.key}'`);
|
|
592
|
+
if (index2 === -1) throw new Error(`Item '${withId}' not found in group '${this.key}'`);
|
|
593
|
+
this.swapItemsByIndex(index1, index2);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Move an item by id to a specific index.
|
|
597
|
+
* @throws Error if the item is not found or index is out of bounds
|
|
598
|
+
*/
|
|
599
|
+
moveItemToIndex(id, index) {
|
|
600
|
+
const items = this.items;
|
|
601
|
+
const fromIndex = items.indexOf(id);
|
|
602
|
+
if (fromIndex === -1) throw new Error(`Item '${id}' not found in group '${this.key}'`);
|
|
603
|
+
if (index < 0 || index >= items.length) throw new Error(`Index out of bounds. Valid range is 0-${items.length - 1}`);
|
|
604
|
+
if (fromIndex === index) return;
|
|
605
|
+
this.config.items = moveArrayElement(items, fromIndex, index);
|
|
606
|
+
this.updateRawItem({
|
|
607
|
+
...this.rawItem,
|
|
608
|
+
config: { ...this.config }
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
var PageBreakItemCore = class extends SurveyItemCore {
|
|
613
|
+
type = ReservedSurveyItemTypes.PageBreak;
|
|
614
|
+
parseConfig(_rawConfig) {
|
|
615
|
+
return {};
|
|
616
|
+
}
|
|
617
|
+
serializeConfig() {}
|
|
618
|
+
isInteractive() {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
getResponseSlotDefinitions() {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
const builtInItemCoreRegistry = {
|
|
626
|
+
[ReservedSurveyItemTypes.Group]: GroupItemCore,
|
|
627
|
+
[ReservedSurveyItemTypes.PageBreak]: PageBreakItemCore
|
|
628
|
+
};
|
|
629
|
+
const isBuiltInItemType = (itemType) => itemType in builtInItemCoreRegistry;
|
|
630
|
+
//#endregion
|
|
631
|
+
//#region src/survey/registry/item-registry.ts
|
|
632
|
+
/** Convert constructor registry to explicit definitions for higher-layer composition. */
|
|
633
|
+
function toItemTypeDefinitionRegistry(registry) {
|
|
634
|
+
return Object.fromEntries(Object.entries(registry).map(([itemType, constructor]) => [itemType, {
|
|
635
|
+
itemType,
|
|
636
|
+
constructor
|
|
637
|
+
}]));
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Creates a handler instance for a raw survey item.
|
|
641
|
+
* Uses built-in registry for group/page-break, otherwise the provided plugin registry.
|
|
642
|
+
*/
|
|
643
|
+
function createItemCore(rawItem, pluginRegistry) {
|
|
644
|
+
if (isBuiltInItemType(rawItem.itemType)) {
|
|
645
|
+
const HandlerClass = builtInItemCoreRegistry[rawItem.itemType];
|
|
646
|
+
return new HandlerClass(rawItem);
|
|
647
|
+
}
|
|
648
|
+
if (pluginRegistry && rawItem.itemType in pluginRegistry) {
|
|
649
|
+
const HandlerClass = pluginRegistry[rawItem.itemType];
|
|
650
|
+
return new HandlerClass(rawItem);
|
|
651
|
+
}
|
|
652
|
+
throw new Error(`Unknown item type: ${rawItem.itemType}`);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Merge built-in handlers with plugin registry for a complete registry.
|
|
656
|
+
*/
|
|
657
|
+
function createFullRegistry(pluginRegistry) {
|
|
658
|
+
return {
|
|
659
|
+
...builtInItemCoreRegistry,
|
|
660
|
+
...pluginRegistry
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/** Build full registry as explicit definitions for editor/ui composition. */
|
|
664
|
+
function createItemTypeDefinitionRegistry(pluginRegistry) {
|
|
665
|
+
return toItemTypeDefinitionRegistry(createFullRegistry(pluginRegistry));
|
|
666
|
+
}
|
|
411
667
|
//#endregion
|
|
412
668
|
//#region src/survey/survey-file-schema.ts
|
|
413
669
|
const CURRENT_SURVEY_SCHEMA = "https://github.com/case-framework/case-survey-toolkit/packages/survey-core/schemas/survey-schema.json";
|
|
414
|
-
|
|
415
670
|
//#endregion
|
|
416
671
|
//#region src/survey/utils/translations.ts
|
|
417
672
|
const validateLocale = (locale) => {
|
|
@@ -588,191 +843,6 @@ var SurveyTranslations = class {
|
|
|
588
843
|
for (const locale of this.locales) delete this._translations[locale].itemTranslations?.[id];
|
|
589
844
|
}
|
|
590
845
|
};
|
|
591
|
-
|
|
592
|
-
//#endregion
|
|
593
|
-
//#region src/survey/utils/group-utils.ts
|
|
594
|
-
/**
|
|
595
|
-
* Shared utilities for array operations.
|
|
596
|
-
*/
|
|
597
|
-
/**
|
|
598
|
-
* Swap two elements in an array by index.
|
|
599
|
-
* @returns A new array with the elements swapped
|
|
600
|
-
*/
|
|
601
|
-
function swapArrayElements(arr, fromIndex, toIndex) {
|
|
602
|
-
const result = [...arr];
|
|
603
|
-
[result[fromIndex], result[toIndex]] = [result[toIndex], result[fromIndex]];
|
|
604
|
-
return result;
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Move an element from one index to another.
|
|
608
|
-
* @returns A new array with the element moved
|
|
609
|
-
*/
|
|
610
|
-
function moveArrayElement(arr, fromIndex, toIndex) {
|
|
611
|
-
const result = [...arr];
|
|
612
|
-
const [moved] = result.splice(fromIndex, 1);
|
|
613
|
-
result.splice(toIndex, 0, moved);
|
|
614
|
-
return result;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
//#endregion
|
|
618
|
-
//#region src/survey/registry/built-in-items.ts
|
|
619
|
-
var GroupItemCore = class extends SurveyItemCore {
|
|
620
|
-
type = ReservedSurveyItemTypes.Group;
|
|
621
|
-
parseConfig(rawConfig) {
|
|
622
|
-
const cfg = rawConfig ?? {};
|
|
623
|
-
return {
|
|
624
|
-
items: cfg.items,
|
|
625
|
-
shuffleItems: cfg.shuffleItems,
|
|
626
|
-
isRoot: cfg.isRoot
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
serializeConfig() {
|
|
630
|
-
return this.config;
|
|
631
|
-
}
|
|
632
|
-
isInteractive() {
|
|
633
|
-
return false;
|
|
634
|
-
}
|
|
635
|
-
getAvailableResponseValueSlots() {
|
|
636
|
-
return {};
|
|
637
|
-
}
|
|
638
|
-
isRoot() {
|
|
639
|
-
return this.config.isRoot ?? false;
|
|
640
|
-
}
|
|
641
|
-
get items() {
|
|
642
|
-
return this.config.items ?? [];
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Add a child item id to this group at the given index.
|
|
646
|
-
* @param itemId The id of the item to add to this group.
|
|
647
|
-
* @param index The index at which to add the item. If not provided, the item is added to the end of the group.
|
|
648
|
-
*/
|
|
649
|
-
addChild(itemId, index) {
|
|
650
|
-
const items = this.config.items ??= [];
|
|
651
|
-
const insertIndex = index !== void 0 ? Math.min(index, items.length) : items.length;
|
|
652
|
-
items.splice(insertIndex, 0, itemId);
|
|
653
|
-
this.updateRawItem({
|
|
654
|
-
...this.rawItem,
|
|
655
|
-
config: { ...this.config }
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* Remove a child item id from this group.
|
|
660
|
-
* @param itemId The id of the item to remove from this group.
|
|
661
|
-
*/
|
|
662
|
-
removeChild(itemId) {
|
|
663
|
-
const items = this.config.items ??= [];
|
|
664
|
-
const index = items.indexOf(itemId);
|
|
665
|
-
if (index !== -1) {
|
|
666
|
-
items.splice(index, 1);
|
|
667
|
-
this.updateRawItem({
|
|
668
|
-
...this.rawItem,
|
|
669
|
-
config: { ...this.config }
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
/**
|
|
674
|
-
* Check if an item is a direct child of this group.
|
|
675
|
-
*/
|
|
676
|
-
hasChild(itemId) {
|
|
677
|
-
return this.items.includes(itemId);
|
|
678
|
-
}
|
|
679
|
-
/** Whether items in this group should be shuffled. */
|
|
680
|
-
get shuffleItems() {
|
|
681
|
-
return this.config.shuffleItems ?? false;
|
|
682
|
-
}
|
|
683
|
-
set shuffleItems(value) {
|
|
684
|
-
this.config.shuffleItems = value;
|
|
685
|
-
this.updateRawItem({
|
|
686
|
-
...this.rawItem,
|
|
687
|
-
config: { ...this.config }
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* Get the ordered list of child item ids.
|
|
692
|
-
*/
|
|
693
|
-
getChildrenIds() {
|
|
694
|
-
return [...this.items];
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Swap two items by their positions in the group.
|
|
698
|
-
* @throws Error if indices are out of bounds
|
|
699
|
-
*/
|
|
700
|
-
swapItemsByIndex(from, to) {
|
|
701
|
-
const items = this.items;
|
|
702
|
-
if (from < 0 || from >= items.length || to < 0 || to >= items.length) throw new Error(`Index out of bounds. Valid range is 0-${items.length - 1}`);
|
|
703
|
-
if (from === to) return;
|
|
704
|
-
this.config.items = swapArrayElements(items, from, to);
|
|
705
|
-
this.updateRawItem({
|
|
706
|
-
...this.rawItem,
|
|
707
|
-
config: { ...this.config }
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Swap two items by their ids.
|
|
712
|
-
* @throws Error if either id is not found in this group
|
|
713
|
-
*/
|
|
714
|
-
swapItemsById(id, withId) {
|
|
715
|
-
const items = this.items;
|
|
716
|
-
const index1 = items.indexOf(id);
|
|
717
|
-
const index2 = items.indexOf(withId);
|
|
718
|
-
if (index1 === -1) throw new Error(`Item '${id}' not found in group '${this.key}'`);
|
|
719
|
-
if (index2 === -1) throw new Error(`Item '${withId}' not found in group '${this.key}'`);
|
|
720
|
-
this.swapItemsByIndex(index1, index2);
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Move an item by id to a specific index.
|
|
724
|
-
* @throws Error if the item is not found or index is out of bounds
|
|
725
|
-
*/
|
|
726
|
-
moveItemToIndex(id, index) {
|
|
727
|
-
const items = this.items;
|
|
728
|
-
const fromIndex = items.indexOf(id);
|
|
729
|
-
if (fromIndex === -1) throw new Error(`Item '${id}' not found in group '${this.key}'`);
|
|
730
|
-
if (index < 0 || index >= items.length) throw new Error(`Index out of bounds. Valid range is 0-${items.length - 1}`);
|
|
731
|
-
if (fromIndex === index) return;
|
|
732
|
-
this.config.items = moveArrayElement(items, fromIndex, index);
|
|
733
|
-
this.updateRawItem({
|
|
734
|
-
...this.rawItem,
|
|
735
|
-
config: { ...this.config }
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
var PageBreakItemCore = class extends SurveyItemCore {
|
|
740
|
-
type = ReservedSurveyItemTypes.PageBreak;
|
|
741
|
-
parseConfig(_rawConfig) {
|
|
742
|
-
return {};
|
|
743
|
-
}
|
|
744
|
-
serializeConfig() {}
|
|
745
|
-
isInteractive() {
|
|
746
|
-
return false;
|
|
747
|
-
}
|
|
748
|
-
getAvailableResponseValueSlots() {
|
|
749
|
-
return {};
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
const builtInItemCoreRegistry = {
|
|
753
|
-
[ReservedSurveyItemTypes.Group]: GroupItemCore,
|
|
754
|
-
[ReservedSurveyItemTypes.PageBreak]: PageBreakItemCore
|
|
755
|
-
};
|
|
756
|
-
const isBuiltInItemType = (itemType) => itemType in builtInItemCoreRegistry;
|
|
757
|
-
|
|
758
|
-
//#endregion
|
|
759
|
-
//#region src/survey/registry/item-registry.ts
|
|
760
|
-
/**
|
|
761
|
-
* Creates a handler instance for a raw survey item.
|
|
762
|
-
* Uses built-in registry for group/page-break, otherwise the provided plugin registry.
|
|
763
|
-
*/
|
|
764
|
-
function createItemCore(rawItem, pluginRegistry) {
|
|
765
|
-
if (isBuiltInItemType(rawItem.itemType)) {
|
|
766
|
-
const HandlerClass = builtInItemCoreRegistry[rawItem.itemType];
|
|
767
|
-
return new HandlerClass(rawItem);
|
|
768
|
-
}
|
|
769
|
-
if (pluginRegistry && rawItem.itemType in pluginRegistry) {
|
|
770
|
-
const HandlerClass = pluginRegistry[rawItem.itemType];
|
|
771
|
-
return new HandlerClass(rawItem);
|
|
772
|
-
}
|
|
773
|
-
throw new Error(`Unknown item type: ${rawItem.itemType}`);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
846
|
//#endregion
|
|
777
847
|
//#region src/survey/survey.ts
|
|
778
848
|
var Survey = class Survey {
|
|
@@ -785,6 +855,10 @@ var Survey = class Survey {
|
|
|
785
855
|
this.pluginRegistry = pluginRegistry;
|
|
786
856
|
this._translations = new SurveyTranslations();
|
|
787
857
|
}
|
|
858
|
+
/** Plugin registry used when parsing items. Exposed for editors that re-parse without their own registry. */
|
|
859
|
+
getPluginRegistry() {
|
|
860
|
+
return this.pluginRegistry;
|
|
861
|
+
}
|
|
788
862
|
/**
|
|
789
863
|
* Create a survey item from raw JSON data.
|
|
790
864
|
* Uses the survey's plugin registry when available.
|
|
@@ -792,10 +866,30 @@ var Survey = class Survey {
|
|
|
792
866
|
createItemFromRaw(rawItem) {
|
|
793
867
|
return createItemCore(rawItem, this.pluginRegistry);
|
|
794
868
|
}
|
|
869
|
+
/**
|
|
870
|
+
* Create a minimal blank survey with a root group and one empty page.
|
|
871
|
+
*/
|
|
872
|
+
static createBlankSurvey(pluginRegistry, surveyKey) {
|
|
873
|
+
const newKey = surveyKey ?? generateCodingKey();
|
|
874
|
+
const rawSurvey = {
|
|
875
|
+
$schema: CURRENT_SURVEY_SCHEMA,
|
|
876
|
+
surveyItems: [{
|
|
877
|
+
id: generateId(),
|
|
878
|
+
key: newKey,
|
|
879
|
+
itemType: ReservedSurveyItemTypes.Group,
|
|
880
|
+
config: {
|
|
881
|
+
isRoot: true,
|
|
882
|
+
items: [],
|
|
883
|
+
shuffleItems: false
|
|
884
|
+
}
|
|
885
|
+
}]
|
|
886
|
+
};
|
|
887
|
+
return Survey.fromJson(rawSurvey, pluginRegistry);
|
|
888
|
+
}
|
|
795
889
|
static fromJson(json, pluginRegistry) {
|
|
796
890
|
const survey = new Survey(pluginRegistry);
|
|
797
891
|
const rawSurvey = json;
|
|
798
|
-
if (rawSurvey.$schema !==
|
|
892
|
+
if (rawSurvey.$schema !== "https://github.com/case-framework/case-survey-toolkit/packages/survey-core/schemas/survey-schema.json") throw new Error(`Unsupported survey schema: ${rawSurvey.$schema}`);
|
|
799
893
|
survey.surveyItems = /* @__PURE__ */ new Map();
|
|
800
894
|
if (!rawSurvey.surveyItems || rawSurvey.surveyItems.length === 0) throw new Error("surveyItems is required");
|
|
801
895
|
rawSurvey.surveyItems.forEach((item) => {
|
|
@@ -902,6 +996,12 @@ var Survey = class Survey {
|
|
|
902
996
|
};
|
|
903
997
|
return valueRefs;
|
|
904
998
|
}
|
|
999
|
+
getResponseSlotDefinitions() {
|
|
1000
|
+
return Array.from(this.surveyItems.values()).map((item) => ({
|
|
1001
|
+
itemId: item.id,
|
|
1002
|
+
slots: item.getResponseSlotDefinitions()
|
|
1003
|
+
}));
|
|
1004
|
+
}
|
|
905
1005
|
/**
|
|
906
1006
|
* Get all reference usages for the survey
|
|
907
1007
|
* @param forItemId - optional item id to filter usages for a specific item and its children (if not provided, all usages are returned)
|
|
@@ -925,7 +1025,7 @@ var Survey = class Survey {
|
|
|
925
1025
|
return this.getItemPath(targetId).includes(ancestorId);
|
|
926
1026
|
}
|
|
927
1027
|
};
|
|
928
|
-
|
|
929
1028
|
//#endregion
|
|
930
|
-
export {
|
|
931
|
-
|
|
1029
|
+
export { FunctionExpressionNames as A, serializeTemplateValues as C, Expression as D, ContextVariableType as E, generateCodingKey as F, generateId as I, shuffleIndices as L, ReferenceUsageType as M, ValueReference as N, ExpressionType as O, ValueReferenceMethod as P, structuredCloneMethod as R, serializeTemplateValue as S, ContextVariableExpression as T, NumberPrecision as _, createFullRegistry as a, deserializeTemplateValue as b, toItemTypeDefinitionRegistry as c, builtInItemCoreRegistry as d, isBuiltInItemType as f, DurationUnits as g, SurveyItemCore as h, validateLocale as i, ResponseVariableExpression as j, FunctionExpression as k, GroupItemCore as l, ReservedSurveyItemTypes as m, SurveyItemTranslations as n, createItemCore as o, SurveyItemKey as p, SurveyTranslations as r, createItemTypeDefinitionRegistry as s, Survey as t, PageBreakItemCore as u, ValueType as v, ConstExpression as w, deserializeTemplateValues as x, TemplateDefTypes as y };
|
|
1030
|
+
|
|
1031
|
+
//# sourceMappingURL=survey-DJbgFVPz.mjs.map
|