@case-framework/survey-core 0.3.0 → 0.4.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/build/editor.d.mts +52 -12
- package/build/editor.d.mts.map +1 -1
- package/build/editor.mjs +222 -43
- package/build/editor.mjs.map +1 -1
- package/build/index.d.mts +67 -45
- package/build/index.d.mts.map +1 -1
- package/build/index.mjs +171 -69
- package/build/index.mjs.map +1 -1
- package/build/package.json +5 -5
- package/build/{survey-wnGyIY66.d.mts → survey-DShEOjyz.d.mts} +378 -86
- package/build/survey-DShEOjyz.d.mts.map +1 -0
- package/build/{survey-DJbgFVPz.mjs → survey-yXdl8xkf.mjs} +428 -31
- package/build/survey-yXdl8xkf.mjs.map +1 -0
- package/package.json +5 -5
- package/build/survey-DJbgFVPz.mjs.map +0 -1
- package/build/survey-wnGyIY66.d.mts.map +0 -1
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
//#region src/utils.ts
|
|
2
|
+
function hashString(seed) {
|
|
3
|
+
let hash = 2166136261;
|
|
4
|
+
for (let i = 0; i < seed.length; i++) {
|
|
5
|
+
hash ^= seed.charCodeAt(i);
|
|
6
|
+
hash = Math.imul(hash, 16777619);
|
|
7
|
+
}
|
|
8
|
+
return hash >>> 0;
|
|
9
|
+
}
|
|
10
|
+
function createSeededRandom(seed) {
|
|
11
|
+
let state = hashString(seed);
|
|
12
|
+
return () => {
|
|
13
|
+
state = state + 1831565813 >>> 0;
|
|
14
|
+
let next = state;
|
|
15
|
+
next = Math.imul(next ^ next >>> 15, next | 1);
|
|
16
|
+
next ^= next + Math.imul(next ^ next >>> 7, next | 61);
|
|
17
|
+
return ((next ^ next >>> 14) >>> 0) / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function shuffleArray(values, random = Math.random) {
|
|
21
|
+
const shuffledValues = [...values];
|
|
22
|
+
for (let i = shuffledValues.length - 1; i > 0; i--) {
|
|
23
|
+
const j = Math.floor(random() * (i + 1));
|
|
24
|
+
[shuffledValues[i], shuffledValues[j]] = [shuffledValues[j], shuffledValues[i]];
|
|
25
|
+
}
|
|
26
|
+
return shuffledValues;
|
|
27
|
+
}
|
|
2
28
|
/**
|
|
3
29
|
* Shuffles an array of indices using the Fisher-Yates shuffle algorithm
|
|
4
30
|
* @param length - The length of the array to create indices for
|
|
5
31
|
* @returns A shuffled array of indices from 0 to length-1
|
|
6
32
|
*/
|
|
7
|
-
function shuffleIndices(length) {
|
|
8
|
-
|
|
9
|
-
for (let i = shuffledIndices.length - 1; i > 0; i--) {
|
|
10
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
11
|
-
[shuffledIndices[i], shuffledIndices[j]] = [shuffledIndices[j], shuffledIndices[i]];
|
|
12
|
-
}
|
|
13
|
-
return shuffledIndices;
|
|
33
|
+
function shuffleIndices(length, random = Math.random) {
|
|
34
|
+
return shuffleArray(Array.from({ length }, (_, i) => i), random);
|
|
14
35
|
}
|
|
15
36
|
function structuredCloneMethod(obj) {
|
|
16
37
|
if (typeof structuredClone !== "undefined") return structuredClone(obj);
|
|
@@ -87,6 +108,7 @@ let ReferenceUsageType = /* @__PURE__ */ function(ReferenceUsageType) {
|
|
|
87
108
|
ReferenceUsageType["templateValues"] = "templateValues";
|
|
88
109
|
ReferenceUsageType["validations"] = "validations";
|
|
89
110
|
ReferenceUsageType["disabledConditions"] = "disabledConditions";
|
|
111
|
+
ReferenceUsageType["prefills"] = "prefills";
|
|
90
112
|
return ReferenceUsageType;
|
|
91
113
|
}({});
|
|
92
114
|
//#endregion
|
|
@@ -271,7 +293,7 @@ const serializeTemplateValue = (templateValue) => {
|
|
|
271
293
|
returnType: templateValue.returnType,
|
|
272
294
|
expression: templateValue.expression?.serialize()
|
|
273
295
|
};
|
|
274
|
-
if (templateValue.type ===
|
|
296
|
+
if (templateValue.type === "date2string") json.dateFormat = templateValue.dateFormat;
|
|
275
297
|
return json;
|
|
276
298
|
};
|
|
277
299
|
const serializeTemplateValues = (templateValues) => {
|
|
@@ -318,6 +340,44 @@ const NumberPrecision = {
|
|
|
318
340
|
int: "int",
|
|
319
341
|
float: "float"
|
|
320
342
|
};
|
|
343
|
+
function isPlainObject(value) {
|
|
344
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
345
|
+
}
|
|
346
|
+
function isFiniteNumber(value) {
|
|
347
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
348
|
+
}
|
|
349
|
+
function isStringArray(value) {
|
|
350
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
351
|
+
}
|
|
352
|
+
function isNumberArray(value) {
|
|
353
|
+
return Array.isArray(value) && value.every((item) => isFiniteNumber(item));
|
|
354
|
+
}
|
|
355
|
+
function isOptionalNumberPrecision(value) {
|
|
356
|
+
return value === void 0 || value === NumberPrecision.int || value === NumberPrecision.float;
|
|
357
|
+
}
|
|
358
|
+
function isDurationUnit(value) {
|
|
359
|
+
return typeof value === "string" && Object.values(DurationUnits).includes(value);
|
|
360
|
+
}
|
|
361
|
+
function isResponseValue(value) {
|
|
362
|
+
if (!isPlainObject(value) || typeof value.type !== "string") return false;
|
|
363
|
+
switch (value.type) {
|
|
364
|
+
case ValueType.string:
|
|
365
|
+
case ValueType.reference: return typeof value.value === "string";
|
|
366
|
+
case ValueType.number: return isFiniteNumber(value.value) && isOptionalNumberPrecision(value.precision);
|
|
367
|
+
case ValueType.boolean: return typeof value.value === "boolean";
|
|
368
|
+
case ValueType.date: return isFiniteNumber(value.value);
|
|
369
|
+
case ValueType.duration: return isFiniteNumber(value.value) && isDurationUnit(value.unit) && isOptionalNumberPrecision(value.precision);
|
|
370
|
+
case ValueType.stringArray:
|
|
371
|
+
case ValueType.referenceArray: return isStringArray(value.value);
|
|
372
|
+
case ValueType.numberArray:
|
|
373
|
+
case ValueType.dateArray: return isNumberArray(value.value) && (value.type !== ValueType.numberArray || isOptionalNumberPrecision(value.precision));
|
|
374
|
+
case ValueType.durationArray: return isNumberArray(value.value) && isDurationUnit(value.unit) && isOptionalNumberPrecision(value.precision);
|
|
375
|
+
default: return false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function assertResponseValue(value, path) {
|
|
379
|
+
if (!isResponseValue(value)) throw new Error(`Invalid response value at '${path}'`);
|
|
380
|
+
}
|
|
321
381
|
//#endregion
|
|
322
382
|
//#region src/survey/items/utils.ts
|
|
323
383
|
const displayConditionsFromJson = (json) => {
|
|
@@ -330,6 +390,119 @@ const disabledConditionsFromJson = (json) => {
|
|
|
330
390
|
return { components: json.components ? Object.fromEntries(Object.entries(json.components).map(([key, value]) => [key, Expression.deserialize(value)])) : void 0 };
|
|
331
391
|
};
|
|
332
392
|
//#endregion
|
|
393
|
+
//#region src/survey/items/prefill.ts
|
|
394
|
+
const SurveyItemPrefillApplyMode = { ifEmpty: "ifEmpty" };
|
|
395
|
+
const SurveyItemPrefillTargetType = {
|
|
396
|
+
itemResponse: "itemResponse",
|
|
397
|
+
field: "field",
|
|
398
|
+
embeddedField: "embeddedField"
|
|
399
|
+
};
|
|
400
|
+
function deserializeSurveyItemPrefill(json) {
|
|
401
|
+
switch (json.source.type) {
|
|
402
|
+
case "static": return {
|
|
403
|
+
id: json.id,
|
|
404
|
+
target: json.target,
|
|
405
|
+
when: Expression.deserialize(json.when),
|
|
406
|
+
apply: json.apply,
|
|
407
|
+
source: {
|
|
408
|
+
type: "static",
|
|
409
|
+
value: json.source.value
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
case "expression": return {
|
|
413
|
+
id: json.id,
|
|
414
|
+
target: json.target,
|
|
415
|
+
when: Expression.deserialize(json.when),
|
|
416
|
+
apply: json.apply,
|
|
417
|
+
source: {
|
|
418
|
+
type: "expression",
|
|
419
|
+
expression: Expression.deserialize(json.source.expression) ?? (() => {
|
|
420
|
+
throw new Error(`Prefill '${json.id}' is missing a valid expression source`);
|
|
421
|
+
})()
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
case "templateValue": return {
|
|
425
|
+
id: json.id,
|
|
426
|
+
target: json.target,
|
|
427
|
+
when: Expression.deserialize(json.when),
|
|
428
|
+
apply: json.apply,
|
|
429
|
+
source: {
|
|
430
|
+
type: "templateValue",
|
|
431
|
+
key: json.source.key
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
case "previousResponse": return {
|
|
435
|
+
id: json.id,
|
|
436
|
+
target: json.target,
|
|
437
|
+
when: Expression.deserialize(json.when),
|
|
438
|
+
apply: json.apply,
|
|
439
|
+
source: {
|
|
440
|
+
type: "previousResponse",
|
|
441
|
+
ref: json.source.ref,
|
|
442
|
+
ifUnsupported: json.source.ifUnsupported
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
default: throw new Error(`Unsupported prefill source type: ${json.source.type}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function serializeSurveyItemPrefill(prefill) {
|
|
449
|
+
switch (prefill.source.type) {
|
|
450
|
+
case "static": return {
|
|
451
|
+
id: prefill.id,
|
|
452
|
+
target: prefill.target,
|
|
453
|
+
when: prefill.when?.serialize(),
|
|
454
|
+
apply: prefill.apply,
|
|
455
|
+
source: {
|
|
456
|
+
type: "static",
|
|
457
|
+
value: prefill.source.value
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
case "expression": return {
|
|
461
|
+
id: prefill.id,
|
|
462
|
+
target: prefill.target,
|
|
463
|
+
when: prefill.when?.serialize(),
|
|
464
|
+
apply: prefill.apply,
|
|
465
|
+
source: {
|
|
466
|
+
type: "expression",
|
|
467
|
+
expression: prefill.source.expression.serialize() ?? (() => {
|
|
468
|
+
throw new Error(`Prefill '${prefill.id}' cannot serialize an empty expression source`);
|
|
469
|
+
})()
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
case "templateValue": return {
|
|
473
|
+
id: prefill.id,
|
|
474
|
+
target: prefill.target,
|
|
475
|
+
when: prefill.when?.serialize(),
|
|
476
|
+
apply: prefill.apply,
|
|
477
|
+
source: {
|
|
478
|
+
type: "templateValue",
|
|
479
|
+
key: prefill.source.key
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
case "previousResponse": return {
|
|
483
|
+
id: prefill.id,
|
|
484
|
+
target: prefill.target,
|
|
485
|
+
when: prefill.when?.serialize(),
|
|
486
|
+
apply: prefill.apply,
|
|
487
|
+
source: {
|
|
488
|
+
type: "previousResponse",
|
|
489
|
+
ref: prefill.source.ref,
|
|
490
|
+
ifUnsupported: prefill.source.ifUnsupported
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
default: throw new Error(`Unsupported prefill source type: ${prefill.source.type}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function prefillTargetsEqual(left, right) {
|
|
497
|
+
if (left.type !== right.type) return false;
|
|
498
|
+
switch (left.type) {
|
|
499
|
+
case SurveyItemPrefillTargetType.itemResponse: return true;
|
|
500
|
+
case SurveyItemPrefillTargetType.field: return left.fieldId === right.fieldId;
|
|
501
|
+
case SurveyItemPrefillTargetType.embeddedField: return left.optionId === right.optionId && left.fieldId === right.fieldId;
|
|
502
|
+
default: return false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
333
506
|
//#region src/survey/items/survey-item.ts
|
|
334
507
|
/**
|
|
335
508
|
* Base class for survey item handlers.
|
|
@@ -344,7 +517,7 @@ var SurveyItemCore = class {
|
|
|
344
517
|
displayConditions;
|
|
345
518
|
disabledConditions;
|
|
346
519
|
validations;
|
|
347
|
-
|
|
520
|
+
prefills;
|
|
348
521
|
constructor(rawItem) {
|
|
349
522
|
this._rawItem = rawItem;
|
|
350
523
|
this.updateExpressions();
|
|
@@ -358,8 +531,8 @@ var SurveyItemCore = class {
|
|
|
358
531
|
getAvailableResponseValueSlots(byType) {
|
|
359
532
|
const valueRefs = {};
|
|
360
533
|
for (const slot of this.getResponseSlotDefinitions()) {
|
|
361
|
-
valueRefs[`${this.id}
|
|
362
|
-
valueRefs[`${this.id}
|
|
534
|
+
valueRefs[`${this.id}...get...${slot.slotId}`] = slot.valueType;
|
|
535
|
+
valueRefs[`${this.id}...isDefined...${slot.slotId}`] = ValueType.boolean;
|
|
363
536
|
}
|
|
364
537
|
Object.assign(valueRefs, this.getAdditionalResponseValueSlots());
|
|
365
538
|
if (!byType) return valueRefs;
|
|
@@ -388,37 +561,60 @@ var SurveyItemCore = class {
|
|
|
388
561
|
this.displayConditions = this._rawItem.displayConditions ? displayConditionsFromJson(this._rawItem.displayConditions) : void 0;
|
|
389
562
|
this.disabledConditions = this._rawItem.disabledConditions ? disabledConditionsFromJson(this._rawItem.disabledConditions) : void 0;
|
|
390
563
|
this.validations = this._rawItem.validations ? Object.fromEntries(Object.entries(this._rawItem.validations).map(([key, value]) => [key, Expression.deserialize(value)])) : void 0;
|
|
391
|
-
this.
|
|
564
|
+
this.prefills = this._rawItem.prefills?.map((prefill) => deserializeSurveyItemPrefill(prefill));
|
|
392
565
|
}
|
|
393
566
|
getReferenceUsages() {
|
|
394
567
|
const usages = [];
|
|
395
568
|
if (this.displayConditions) {
|
|
396
569
|
for (const ref of this.displayConditions.root?.responseVariableRefs || []) usages.push({
|
|
397
570
|
itemId: this.id,
|
|
398
|
-
usageType:
|
|
571
|
+
usageType: "displayConditions",
|
|
399
572
|
valueReference: ref
|
|
400
573
|
});
|
|
401
574
|
for (const [componentKey, expression] of Object.entries(this.displayConditions.components || {})) for (const ref of expression?.responseVariableRefs || []) usages.push({
|
|
402
575
|
itemId: this.id,
|
|
403
576
|
fullComponentKey: componentKey,
|
|
404
|
-
usageType:
|
|
577
|
+
usageType: "displayConditions",
|
|
405
578
|
valueReference: ref
|
|
406
579
|
});
|
|
407
580
|
}
|
|
408
581
|
if (this.disabledConditions) for (const [componentKey, expression] of Object.entries(this.disabledConditions.components || {})) for (const ref of expression?.responseVariableRefs || []) usages.push({
|
|
409
582
|
itemId: this.id,
|
|
410
583
|
fullComponentKey: componentKey,
|
|
411
|
-
usageType:
|
|
584
|
+
usageType: "disabledConditions",
|
|
412
585
|
valueReference: ref
|
|
413
586
|
});
|
|
414
587
|
if (this.validations) for (const [validationKey, expression] of Object.entries(this.validations)) for (const ref of expression?.responseVariableRefs || []) usages.push({
|
|
415
588
|
itemId: this.id,
|
|
416
589
|
fullComponentKey: validationKey,
|
|
417
|
-
usageType:
|
|
590
|
+
usageType: "validations",
|
|
418
591
|
valueReference: ref
|
|
419
592
|
});
|
|
593
|
+
if (this.prefills) for (const prefill of this.prefills) {
|
|
594
|
+
for (const ref of prefill.when?.responseVariableRefs || []) usages.push({
|
|
595
|
+
itemId: this.id,
|
|
596
|
+
fullComponentKey: prefill.id,
|
|
597
|
+
usageType: "prefills",
|
|
598
|
+
valueReference: ref
|
|
599
|
+
});
|
|
600
|
+
if (prefill.source.type === "expression") for (const ref of prefill.source.expression.responseVariableRefs || []) usages.push({
|
|
601
|
+
itemId: this.id,
|
|
602
|
+
fullComponentKey: prefill.id,
|
|
603
|
+
usageType: "prefills",
|
|
604
|
+
valueReference: ref
|
|
605
|
+
});
|
|
606
|
+
}
|
|
420
607
|
return usages;
|
|
421
608
|
}
|
|
609
|
+
resolvePrefillTarget(target) {
|
|
610
|
+
const slotDefinitions = this.getResponseSlotDefinitions();
|
|
611
|
+
const explicitMatch = slotDefinitions.find((slot) => slot.prefillTarget !== void 0 && prefillTargetsEqual(slot.prefillTarget, target));
|
|
612
|
+
if (explicitMatch) return explicitMatch;
|
|
613
|
+
if (target.type === SurveyItemPrefillTargetType.itemResponse && slotDefinitions.length === 1) return slotDefinitions[0];
|
|
614
|
+
}
|
|
615
|
+
normalizePrefillValue(_prefill, targetSlot, value) {
|
|
616
|
+
return value.type === targetSlot.valueType ? value : void 0;
|
|
617
|
+
}
|
|
422
618
|
};
|
|
423
619
|
//#endregion
|
|
424
620
|
//#region src/survey/items/types.ts
|
|
@@ -490,7 +686,7 @@ function moveArrayElement(arr, fromIndex, toIndex) {
|
|
|
490
686
|
//#endregion
|
|
491
687
|
//#region src/survey/registry/built-in-items.ts
|
|
492
688
|
var GroupItemCore = class extends SurveyItemCore {
|
|
493
|
-
type =
|
|
689
|
+
type = "group";
|
|
494
690
|
parseConfig(rawConfig) {
|
|
495
691
|
const cfg = rawConfig ?? {};
|
|
496
692
|
return {
|
|
@@ -610,7 +806,7 @@ var GroupItemCore = class extends SurveyItemCore {
|
|
|
610
806
|
}
|
|
611
807
|
};
|
|
612
808
|
var PageBreakItemCore = class extends SurveyItemCore {
|
|
613
|
-
type =
|
|
809
|
+
type = "page-break";
|
|
614
810
|
parseConfig(_rawConfig) {
|
|
615
811
|
return {};
|
|
616
812
|
}
|
|
@@ -623,8 +819,8 @@ var PageBreakItemCore = class extends SurveyItemCore {
|
|
|
623
819
|
}
|
|
624
820
|
};
|
|
625
821
|
const builtInItemCoreRegistry = {
|
|
626
|
-
[
|
|
627
|
-
[
|
|
822
|
+
["group"]: GroupItemCore,
|
|
823
|
+
["page-break"]: PageBreakItemCore
|
|
628
824
|
};
|
|
629
825
|
const isBuiltInItemType = (itemType) => itemType in builtInItemCoreRegistry;
|
|
630
826
|
//#endregion
|
|
@@ -672,7 +868,7 @@ const CURRENT_SURVEY_SCHEMA = "https://github.com/case-framework/case-survey-too
|
|
|
672
868
|
const validateLocale = (locale) => {
|
|
673
869
|
if (locale.trim() === "") throw new Error("Locale cannot be empty");
|
|
674
870
|
};
|
|
675
|
-
var SurveyItemTranslations = class {
|
|
871
|
+
var SurveyItemTranslations = class SurveyItemTranslations {
|
|
676
872
|
_translations;
|
|
677
873
|
constructor() {
|
|
678
874
|
this._translations = {};
|
|
@@ -705,6 +901,48 @@ var SurveyItemTranslations = class {
|
|
|
705
901
|
if (content) return content;
|
|
706
902
|
if (fallbackLocale) return this._translations?.[fallbackLocale]?.[contentKey];
|
|
707
903
|
}
|
|
904
|
+
/**
|
|
905
|
+
* Create a deep clone that can be safely mutated before persisting with
|
|
906
|
+
* `setItemTranslations` or `updateItemTranslations`.
|
|
907
|
+
*/
|
|
908
|
+
clone() {
|
|
909
|
+
const cloned = new SurveyItemTranslations();
|
|
910
|
+
for (const locale of this.locales) {
|
|
911
|
+
const localeContent = this.getAllForLocale(locale);
|
|
912
|
+
if (!localeContent) continue;
|
|
913
|
+
cloned.setAllForLocale(locale, structuredCloneMethod(localeContent));
|
|
914
|
+
}
|
|
915
|
+
return cloned;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Remove a single content key from every locale in this translation set.
|
|
919
|
+
*/
|
|
920
|
+
removeContentKey(contentKey) {
|
|
921
|
+
for (const locale of this.locales) delete this._translations?.[locale]?.[contentKey];
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Remove every content key that is exactly the prefix or nested beneath it.
|
|
925
|
+
*/
|
|
926
|
+
removeContentKeysWithPrefix(prefix) {
|
|
927
|
+
for (const locale of this.locales) {
|
|
928
|
+
const localeContent = this._translations?.[locale];
|
|
929
|
+
if (!localeContent) continue;
|
|
930
|
+
for (const contentKey of Object.keys(localeContent)) if (contentKey === prefix || contentKey.startsWith(prefix + ".")) delete localeContent[contentKey];
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Remove empty locales in place.
|
|
935
|
+
* Returns `undefined` when no translations remain so callers can pass the
|
|
936
|
+
* result directly into `setItemTranslations` / `updateItemTranslations`.
|
|
937
|
+
*/
|
|
938
|
+
compact() {
|
|
939
|
+
for (const locale of this.locales) {
|
|
940
|
+
const localeContent = this._translations?.[locale];
|
|
941
|
+
if (!localeContent) continue;
|
|
942
|
+
if (Object.keys(localeContent).length === 0) delete this._translations?.[locale];
|
|
943
|
+
}
|
|
944
|
+
return this.locales.length > 0 ? this : void 0;
|
|
945
|
+
}
|
|
708
946
|
};
|
|
709
947
|
var SurveyTranslations = class {
|
|
710
948
|
_translations;
|
|
@@ -789,20 +1027,26 @@ var SurveyTranslations = class {
|
|
|
789
1027
|
const contentForLocale = this._translations?.[locale].itemTranslations?.[itemId];
|
|
790
1028
|
itemTranslations.setAllForLocale(locale, contentForLocale);
|
|
791
1029
|
}
|
|
792
|
-
return itemTranslations;
|
|
1030
|
+
return itemTranslations.locales.length > 0 ? itemTranslations : void 0;
|
|
793
1031
|
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Persist item translations after normalizing them:
|
|
1034
|
+
* empty keys/locales are removed automatically, and an empty translation set
|
|
1035
|
+
* clears the item's stored translations entirely.
|
|
1036
|
+
*/
|
|
794
1037
|
setItemTranslations(itemId, itemContent) {
|
|
795
|
-
itemContent?.
|
|
796
|
-
|
|
1038
|
+
const normalizedItemContent = itemContent?.clone().compact();
|
|
1039
|
+
normalizedItemContent?.locales.forEach((locale) => validateLocale(locale));
|
|
1040
|
+
if (!normalizedItemContent) {
|
|
797
1041
|
for (const locale of this.locales) if (this._translations[locale].itemTranslations?.[itemId]) delete this._translations[locale].itemTranslations?.[itemId];
|
|
798
1042
|
} else {
|
|
799
|
-
const localesInUpdate =
|
|
1043
|
+
const localesInUpdate = normalizedItemContent.locales;
|
|
800
1044
|
for (const locale of localesInUpdate) if (!this.locales.includes(locale)) this._translations[locale] = {};
|
|
801
1045
|
for (const locale of this.locales) if (localesInUpdate.includes(locale)) {
|
|
802
1046
|
if (!this._translations[locale]) this._translations[locale] = {};
|
|
803
1047
|
if (!this._translations[locale].itemTranslations) this._translations[locale].itemTranslations = {};
|
|
804
|
-
this._translations[locale].itemTranslations[itemId] =
|
|
805
|
-
} else delete this._translations[locale].itemTranslations[itemId];
|
|
1048
|
+
this._translations[locale].itemTranslations[itemId] = normalizedItemContent.getAllForLocale(locale);
|
|
1049
|
+
} else delete this._translations[locale].itemTranslations?.[itemId];
|
|
806
1050
|
}
|
|
807
1051
|
}
|
|
808
1052
|
/**
|
|
@@ -844,12 +1088,117 @@ var SurveyTranslations = class {
|
|
|
844
1088
|
}
|
|
845
1089
|
};
|
|
846
1090
|
//#endregion
|
|
1091
|
+
//#region src/survey/utils/content.ts
|
|
1092
|
+
let ContentType = /* @__PURE__ */ function(ContentType) {
|
|
1093
|
+
ContentType["richText"] = "richText";
|
|
1094
|
+
ContentType["plain"] = "plain";
|
|
1095
|
+
ContentType["md"] = "md";
|
|
1096
|
+
return ContentType;
|
|
1097
|
+
}({});
|
|
1098
|
+
function hasRenderableRichTextBlock(block) {
|
|
1099
|
+
switch (block.type) {
|
|
1100
|
+
case "paragraph": return block.children.length > 0;
|
|
1101
|
+
case "heading": return block.children.length > 0;
|
|
1102
|
+
case "bulletList": return block.items.some((item) => item.children.some((paragraph) => paragraph.children.length > 0));
|
|
1103
|
+
case "image": return true;
|
|
1104
|
+
case "infoBox": return true;
|
|
1105
|
+
case "separator": return true;
|
|
1106
|
+
case "blockExtension": return true;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
function hasRenderableRichTextContent(content) {
|
|
1110
|
+
return content.doc.blocks.some((block) => hasRenderableRichTextBlock(block));
|
|
1111
|
+
}
|
|
1112
|
+
function createRichTextContent(text = "") {
|
|
1113
|
+
return {
|
|
1114
|
+
type: "richText",
|
|
1115
|
+
version: 1,
|
|
1116
|
+
doc: {
|
|
1117
|
+
type: "doc",
|
|
1118
|
+
blocks: [{
|
|
1119
|
+
type: "paragraph",
|
|
1120
|
+
children: text.length > 0 ? text.split("\n").flatMap((line, index) => {
|
|
1121
|
+
const parts = [];
|
|
1122
|
+
if (index > 0) parts.push({ type: "lineBreak" });
|
|
1123
|
+
if (line.length > 0) parts.push({
|
|
1124
|
+
type: "text",
|
|
1125
|
+
text: line
|
|
1126
|
+
});
|
|
1127
|
+
return parts;
|
|
1128
|
+
}) : []
|
|
1129
|
+
}]
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
function getPlainTextFromLinkChildren(children) {
|
|
1134
|
+
return children.map((inline) => {
|
|
1135
|
+
switch (inline.type) {
|
|
1136
|
+
case "text": return inline.text;
|
|
1137
|
+
case "template": return `{${inline.key}}`;
|
|
1138
|
+
case "lineBreak": return "\n";
|
|
1139
|
+
}
|
|
1140
|
+
}).join("");
|
|
1141
|
+
}
|
|
1142
|
+
function getPlainTextFromInlineNodes(inlines) {
|
|
1143
|
+
return inlines.map((inline) => {
|
|
1144
|
+
switch (inline.type) {
|
|
1145
|
+
case "text": return inline.text;
|
|
1146
|
+
case "template": return `{${inline.key}}`;
|
|
1147
|
+
case "link": return getPlainTextFromLinkChildren(inline.children);
|
|
1148
|
+
case "lineBreak": return "\n";
|
|
1149
|
+
case "inlineExtension": return "";
|
|
1150
|
+
}
|
|
1151
|
+
}).join("");
|
|
1152
|
+
}
|
|
1153
|
+
function getPlainTextFromParagraphs(paragraphs) {
|
|
1154
|
+
return paragraphs.map((paragraph) => getPlainTextFromInlineNodes(paragraph.children)).join("\n");
|
|
1155
|
+
}
|
|
1156
|
+
function getPlainTextFromBlock(block) {
|
|
1157
|
+
switch (block.type) {
|
|
1158
|
+
case "paragraph": return getPlainTextFromInlineNodes(block.children);
|
|
1159
|
+
case "heading": return getPlainTextFromInlineNodes(block.children);
|
|
1160
|
+
case "bulletList": return block.items.map((item) => getPlainTextFromParagraphs(item.children)).join("\n");
|
|
1161
|
+
case "image": {
|
|
1162
|
+
const captionText = block.caption ? getPlainTextFromParagraphs(block.caption) : "";
|
|
1163
|
+
return [block.alt ?? "", captionText].filter(Boolean).join("\n");
|
|
1164
|
+
}
|
|
1165
|
+
case "infoBox": return [block.title ? getPlainTextFromInlineNodes(block.title) : "", getPlainTextFromParagraphs(block.children)].filter(Boolean).join("\n");
|
|
1166
|
+
case "separator": return "";
|
|
1167
|
+
case "blockExtension": return "";
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function getPlainTextFromRichTextContent(content) {
|
|
1171
|
+
return content.doc.blocks.map((block) => getPlainTextFromBlock(block)).filter((text) => text.length > 0).join("\n").trim();
|
|
1172
|
+
}
|
|
1173
|
+
function getContentPlainText(content) {
|
|
1174
|
+
if (!content) return "";
|
|
1175
|
+
if (content.type !== "richText") return content.content ?? "";
|
|
1176
|
+
return getPlainTextFromRichTextContent(content);
|
|
1177
|
+
}
|
|
1178
|
+
function isContentEmpty(content) {
|
|
1179
|
+
if (!content) return true;
|
|
1180
|
+
if (content.type !== "richText") return content.content.trim().length === 0;
|
|
1181
|
+
return !hasRenderableRichTextContent(content);
|
|
1182
|
+
}
|
|
1183
|
+
function getAssetUsagesFromContent(content) {
|
|
1184
|
+
if (!content || content.type !== "richText") return [];
|
|
1185
|
+
const usages = [];
|
|
1186
|
+
content.doc.blocks.forEach((block, blockIndex) => {
|
|
1187
|
+
if (block.type === "image" && block.source.type === "asset") usages.push({
|
|
1188
|
+
assetId: block.source.assetId,
|
|
1189
|
+
blockIndex
|
|
1190
|
+
});
|
|
1191
|
+
});
|
|
1192
|
+
return usages;
|
|
1193
|
+
}
|
|
1194
|
+
//#endregion
|
|
847
1195
|
//#region src/survey/survey.ts
|
|
848
1196
|
var Survey = class Survey {
|
|
849
1197
|
maxItemsPerPage;
|
|
850
1198
|
metadata;
|
|
851
1199
|
surveyItems = /* @__PURE__ */ new Map();
|
|
852
1200
|
_templateValues = /* @__PURE__ */ new Map();
|
|
1201
|
+
_assets = /* @__PURE__ */ new Map();
|
|
853
1202
|
_translations;
|
|
854
1203
|
constructor(pluginRegistry) {
|
|
855
1204
|
this.pluginRegistry = pluginRegistry;
|
|
@@ -876,7 +1225,7 @@ var Survey = class Survey {
|
|
|
876
1225
|
surveyItems: [{
|
|
877
1226
|
id: generateId(),
|
|
878
1227
|
key: newKey,
|
|
879
|
-
itemType:
|
|
1228
|
+
itemType: "group",
|
|
880
1229
|
config: {
|
|
881
1230
|
isRoot: true,
|
|
882
1231
|
items: [],
|
|
@@ -897,6 +1246,7 @@ var Survey = class Survey {
|
|
|
897
1246
|
survey.surveyItems.set(surveyItem.id, surveyItem);
|
|
898
1247
|
});
|
|
899
1248
|
if (rawSurvey.templateValues) survey._templateValues = deserializeTemplateValues(rawSurvey.templateValues);
|
|
1249
|
+
if (rawSurvey.assets) survey._assets = new Map(Object.entries(rawSurvey.assets));
|
|
900
1250
|
survey._translations = new SurveyTranslations(rawSurvey.translations);
|
|
901
1251
|
if (rawSurvey.maxItemsPerPage) survey.maxItemsPerPage = rawSurvey.maxItemsPerPage;
|
|
902
1252
|
if (rawSurvey.metadata) survey.metadata = rawSurvey.metadata;
|
|
@@ -909,6 +1259,7 @@ var Survey = class Survey {
|
|
|
909
1259
|
};
|
|
910
1260
|
json.translations = this._translations?.serialize();
|
|
911
1261
|
if (this._templateValues) json.templateValues = serializeTemplateValues(this._templateValues);
|
|
1262
|
+
if (this._assets && this._assets.size > 0) json.assets = Object.fromEntries(this._assets.entries());
|
|
912
1263
|
if (this.maxItemsPerPage) json.maxItemsPerPage = this.maxItemsPerPage;
|
|
913
1264
|
if (this.metadata) json.metadata = this.metadata;
|
|
914
1265
|
return json;
|
|
@@ -988,6 +1339,52 @@ var Survey = class Survey {
|
|
|
988
1339
|
getTemplateValueKeys() {
|
|
989
1340
|
return Array.from(this._templateValues?.keys() || []);
|
|
990
1341
|
}
|
|
1342
|
+
getAsset(assetId) {
|
|
1343
|
+
const asset = this._assets?.get(assetId);
|
|
1344
|
+
return asset ? structuredCloneMethod(asset) : void 0;
|
|
1345
|
+
}
|
|
1346
|
+
setAsset(assetId, asset) {
|
|
1347
|
+
if (!this._assets) this._assets = /* @__PURE__ */ new Map();
|
|
1348
|
+
this._assets.set(assetId, structuredCloneMethod(asset));
|
|
1349
|
+
}
|
|
1350
|
+
deleteAsset(assetId) {
|
|
1351
|
+
this._assets?.delete(assetId);
|
|
1352
|
+
}
|
|
1353
|
+
hasAsset(assetId) {
|
|
1354
|
+
return this._assets?.has(assetId) ?? false;
|
|
1355
|
+
}
|
|
1356
|
+
getAssetIds() {
|
|
1357
|
+
return Array.from(this._assets?.keys() || []);
|
|
1358
|
+
}
|
|
1359
|
+
getAssets() {
|
|
1360
|
+
return new Map(Array.from(this._assets?.entries() || []).map(([assetId, asset]) => [assetId, structuredCloneMethod(asset)]));
|
|
1361
|
+
}
|
|
1362
|
+
getAssetUsages(filterAssetId) {
|
|
1363
|
+
const usages = [];
|
|
1364
|
+
const pushContentUsages = (content, usageScope, contentKey, locale, itemId) => {
|
|
1365
|
+
for (const usage of getAssetUsagesFromContent(content)) {
|
|
1366
|
+
if (filterAssetId && usage.assetId !== filterAssetId) continue;
|
|
1367
|
+
usages.push({
|
|
1368
|
+
assetId: usage.assetId,
|
|
1369
|
+
itemId,
|
|
1370
|
+
locale,
|
|
1371
|
+
contentKey,
|
|
1372
|
+
usageScope,
|
|
1373
|
+
blockIndex: usage.blockIndex
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
const serializedTranslations = this._translations?.serialize();
|
|
1378
|
+
const rootItemId = this.rootItem?.id;
|
|
1379
|
+
if (!serializedTranslations) return usages;
|
|
1380
|
+
for (const [locale, localeTranslations] of Object.entries(serializedTranslations)) {
|
|
1381
|
+
for (const [contentKey, content] of Object.entries(localeTranslations.surveyCardContent || {})) pushContentUsages(content, "surveyCard", contentKey, locale, rootItemId);
|
|
1382
|
+
for (const [contentKey, content] of Object.entries(localeTranslations.navigationContent || {})) pushContentUsages(content, "navigation", contentKey, locale, rootItemId);
|
|
1383
|
+
for (const [contentKey, content] of Object.entries(localeTranslations.validationMessages || {})) pushContentUsages(content, "validationMessages", contentKey, locale, rootItemId);
|
|
1384
|
+
for (const [itemId, itemTranslations] of Object.entries(localeTranslations.itemTranslations || {})) for (const [contentKey, content] of Object.entries(itemTranslations || {})) pushContentUsages(content, "itemTranslation", contentKey, locale, itemId);
|
|
1385
|
+
}
|
|
1386
|
+
return usages;
|
|
1387
|
+
}
|
|
991
1388
|
getAvailableResponseValueSlots(byType) {
|
|
992
1389
|
let valueRefs = {};
|
|
993
1390
|
for (const item of this.surveyItems.values()) valueRefs = {
|
|
@@ -1015,7 +1412,7 @@ var Survey = class Survey {
|
|
|
1015
1412
|
}
|
|
1016
1413
|
if (this._templateValues) for (const [templateValueKey, templateValue] of this._templateValues.entries()) for (const ref of templateValue.expression?.responseVariableRefs || []) usages.push({
|
|
1017
1414
|
itemId: templateValueKey,
|
|
1018
|
-
usageType:
|
|
1415
|
+
usageType: "templateValues",
|
|
1019
1416
|
valueReference: ref
|
|
1020
1417
|
});
|
|
1021
1418
|
return usages;
|
|
@@ -1026,6 +1423,6 @@ var Survey = class Survey {
|
|
|
1026
1423
|
}
|
|
1027
1424
|
};
|
|
1028
1425
|
//#endregion
|
|
1029
|
-
export {
|
|
1426
|
+
export { generateId as $, DurationUnits as A, ConstExpression as B, ReservedSurveyItemTypes as C, deserializeSurveyItemPrefill as D, SurveyItemPrefillTargetType as E, TemplateDefTypes as F, FunctionExpression as G, ContextVariableType as H, deserializeTemplateValue as I, ReferenceUsageType as J, FunctionExpressionNames as K, deserializeTemplateValues as L, ValueType as M, assertResponseValue as N, prefillTargetsEqual as O, isResponseValue as P, generateCodingKey as Q, serializeTemplateValue as R, SurveyItemKey as S, SurveyItemPrefillApplyMode as T, Expression as U, ContextVariableExpression as V, ExpressionType as W, ValueReferenceMethod as X, ValueReference as Y, createSeededRandom as Z, toItemTypeDefinitionRegistry as _, getContentPlainText as a, builtInItemCoreRegistry as b, hasRenderableRichTextContent as c, SurveyTranslations as d, shuffleArray as et, validateLocale as f, createItemTypeDefinitionRegistry as g, createItemCore as h, getAssetUsagesFromContent as i, NumberPrecision as j, serializeSurveyItemPrefill as k, isContentEmpty as l, createFullRegistry as m, ContentType as n, structuredCloneMethod as nt, getPlainTextFromRichTextContent as o, CURRENT_SURVEY_SCHEMA as p, ResponseVariableExpression as q, createRichTextContent as r, hasRenderableRichTextBlock as s, Survey as t, shuffleIndices as tt, SurveyItemTranslations as u, GroupItemCore as v, SurveyItemCore as w, isBuiltInItemType as x, PageBreakItemCore as y, serializeTemplateValues as z };
|
|
1030
1427
|
|
|
1031
|
-
//# sourceMappingURL=survey-
|
|
1428
|
+
//# sourceMappingURL=survey-yXdl8xkf.mjs.map
|