@getodk/xforms-engine 0.9.0 → 0.11.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.
Files changed (40) hide show
  1. package/dist/client/SelectNode.d.ts +1 -0
  2. package/dist/client/TextRange.d.ts +4 -0
  3. package/dist/index.js +133 -160
  4. package/dist/index.js.map +1 -1
  5. package/dist/instance/SelectControl.d.ts +1 -0
  6. package/dist/instance/text/TextRange.d.ts +11 -1
  7. package/dist/lib/reactivity/text/createTextRange.d.ts +1 -2
  8. package/dist/parse/expression/TextChunkExpression.d.ts +16 -0
  9. package/dist/parse/shared/parseInstanceXML.d.ts +1 -1
  10. package/dist/parse/text/ItemsetLabelDefinition.d.ts +2 -4
  11. package/dist/parse/text/MessageDefinition.d.ts +3 -7
  12. package/dist/parse/text/abstract/TextElementDefinition.d.ts +2 -8
  13. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +2 -2
  14. package/dist/solid.js +133 -160
  15. package/dist/solid.js.map +1 -1
  16. package/package.json +2 -2
  17. package/src/client/SelectNode.ts +2 -0
  18. package/src/client/TextRange.ts +5 -1
  19. package/src/error/LoadFormFailureError.ts +9 -35
  20. package/src/instance/SelectControl.ts +6 -0
  21. package/src/instance/text/TextRange.ts +21 -1
  22. package/src/lib/reactivity/text/createTextRange.ts +56 -43
  23. package/src/lib/reactivity/validation/createValidation.ts +1 -3
  24. package/src/parse/expression/TextChunkExpression.ts +78 -0
  25. package/src/parse/shared/parseInstanceXML.ts +4 -2
  26. package/src/parse/text/ItemsetLabelDefinition.ts +8 -12
  27. package/src/parse/text/MessageDefinition.ts +9 -16
  28. package/src/parse/text/abstract/TextElementDefinition.ts +10 -26
  29. package/src/parse/text/abstract/TextRangeDefinition.ts +2 -2
  30. package/src/parse/xpath/semantic-analysis.ts +7 -2
  31. package/dist/parse/expression/TextLiteralExpression.d.ts +0 -9
  32. package/dist/parse/expression/TextOutputExpression.d.ts +0 -7
  33. package/dist/parse/expression/TextReferenceExpression.d.ts +0 -7
  34. package/dist/parse/expression/TextTranslationExpression.d.ts +0 -8
  35. package/dist/parse/expression/abstract/TextChunkExpression.d.ts +0 -17
  36. package/src/parse/expression/TextLiteralExpression.ts +0 -19
  37. package/src/parse/expression/TextOutputExpression.ts +0 -25
  38. package/src/parse/expression/TextReferenceExpression.ts +0 -14
  39. package/src/parse/expression/TextTranslationExpression.ts +0 -38
  40. package/src/parse/expression/abstract/TextChunkExpression.ts +0 -38
@@ -13,6 +13,7 @@ export interface SelectItem {
13
13
  }
14
14
  export type SelectValueOptions = readonly SelectItem[];
15
15
  export interface SelectNodeState extends BaseValueNodeState<readonly string[]> {
16
+ get isSelectWithImages(): boolean;
16
17
  get children(): null;
17
18
  get valueOptions(): readonly SelectItem[];
18
19
  /**
@@ -1,3 +1,4 @@
1
+ import { JRResourceURL } from '../../../common/src/jr-resources/JRResourceURL.ts';
1
2
  import { ActiveLanguage } from './FormLanguage.ts';
2
3
  /**
3
4
  * **COMMENTARY**
@@ -135,4 +136,7 @@ export interface TextRange<Role extends TextRole, Origin extends TextOrigin = Te
135
136
  [Symbol.iterator](): Iterable<TextChunk>;
136
137
  get asString(): string;
137
138
  get formatted(): unknown;
139
+ get imageSource(): JRResourceURL | undefined;
140
+ get audioSource(): JRResourceURL | undefined;
141
+ get videoSource(): JRResourceURL | undefined;
138
142
  }
package/dist/index.js CHANGED
@@ -424,37 +424,17 @@ const formResourceMetadata = (resource) => {
424
424
  rawData: null
425
425
  };
426
426
  }
427
- return {
428
- description: "Raw string data",
429
- rawData: resource
430
- };
427
+ return;
431
428
  };
432
429
  class LoadFormFailureError extends AggregateError {
433
430
  constructor(resource, errors) {
434
- const { description, rawData } = formResourceMetadata(resource);
435
- const messageLines = [
436
- `Failed to load form resource: ${description}`,
437
- "\n",
438
- ...errors.map((error) => {
439
- const aggregatedMessage = error.message;
440
- if (aggregatedMessage == null) {
441
- return "- Unknown error";
442
- }
443
- return `- ${aggregatedMessage}`;
444
- })
445
- ];
446
- if (rawData != null) {
447
- messageLines.push("\n- - -\n", "Raw resource data:", rawData);
448
- }
449
- const message = messageLines.join("\n");
431
+ const metadata = formResourceMetadata(resource);
432
+ const errorMessages = errors.map((error) => error.message || "Unknown error").join("\n");
433
+ const message = metadata?.description ? `Form source: ${metadata.description}
434
+ ${errorMessages}` : errorMessages;
450
435
  super(errors, message);
451
436
  const [head, ...tail] = errors;
452
- if (head != null && tail.length === 0) {
453
- const { stack } = head;
454
- if (typeof stack === "string") {
455
- this.stack = stack;
456
- }
457
- }
437
+ this.stack = typeof head?.stack === "string" && !tail.length ? head.stack : "No error trace available.";
458
438
  }
459
439
  }
460
440
 
@@ -9759,7 +9739,7 @@ const substring = new StringFunction(
9759
9739
  return string2.substring(start, end);
9760
9740
  }
9761
9741
  );
9762
- const string$2 = new StringFunction(
9742
+ const string$1 = new StringFunction(
9763
9743
  "string",
9764
9744
  [{ arityType: "optional" }],
9765
9745
  (context, [expression]) => (expression?.evaluate(context) ?? context).toString()
@@ -9792,13 +9772,13 @@ const translate = new StringFunction(
9792
9772
  }
9793
9773
  );
9794
9774
 
9795
- const string$3 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
9775
+ const string$2 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
9796
9776
  __proto__: null,
9797
9777
  concat: concat$1,
9798
9778
  contains,
9799
9779
  normalizeSpace,
9800
9780
  startsWith,
9801
- string: string$2,
9781
+ string: string$1,
9802
9782
  stringLength,
9803
9783
  substring,
9804
9784
  substringAfter,
@@ -9810,7 +9790,7 @@ const fn$2 = new FunctionLibrary(FN_NAMESPACE_URI, [
9810
9790
  ...Object.values(boolean$2),
9811
9791
  ...Object.values(nodeset$1),
9812
9792
  ...Object.values(number$3),
9813
- ...Object.values(string$3)
9793
+ ...Object.values(string$2)
9814
9794
  ]);
9815
9795
 
9816
9796
  class BaseStep {
@@ -11590,21 +11570,21 @@ const enk = new FunctionLibrary(ENKETO_NAMESPACE_URI, [
11590
11570
  new FunctionAlias("format-date", formatDateTime)
11591
11571
  ]);
11592
11572
 
11593
- const itext = new StringFunction(
11573
+ const itext = new NodeSetFunction(
11594
11574
  "itext",
11595
11575
  [{ arityType: "required", typeHint: "string" }],
11596
11576
  (context, [itextIDExpression]) => {
11597
11577
  const itextID = itextIDExpression.evaluate(context).toString();
11598
- return XFormsXPathEvaluator.getDefaultTranslationText(context, itextID);
11578
+ return XFormsXPathEvaluator.getTranslationValues(context, itextID) ?? [];
11599
11579
  }
11600
11580
  );
11601
11581
 
11602
- const string$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
11582
+ const nodeSet = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
11603
11583
  __proto__: null,
11604
11584
  itext
11605
11585
  }, Symbol.toStringTag, { value: 'Module' }));
11606
11586
 
11607
- const jr$2 = new FunctionLibrary(JAVAROSA_NAMESPACE_URI, [...Object.values(string$1)]);
11587
+ const jr$2 = new FunctionLibrary(JAVAROSA_NAMESPACE_URI, [...Object.values(nodeSet)]);
11608
11588
 
11609
11589
  const booleanFromString = new BooleanFunction(
11610
11590
  "boolean-from-string",
@@ -19307,46 +19287,39 @@ class XFormsItextTranslations {
19307
19287
  return textMap.get(itextID) ?? null;
19308
19288
  }
19309
19289
  /**
19290
+ * Retrieves all translation value elements for a given Itext in the active language.
19291
+ *
19310
19292
  * @package
19311
19293
  *
19312
- * Here, "default" is meant as more precise language than "regular" as
19313
- * {@link https://getodk.github.io/xforms-spec/#languages | specified by ODK XForms}. In other words, this is equivalent to the following hypothetical XPath pseudo-code (whitespace added to improve structural clarity):
19294
+ * This method fetches the `text` element matching `itextID` and returns its child `value` elements,
19295
+ * which may have an optional `@form` attribute.
19296
+ *
19297
+ * The operation is conceptually similar to the following XPath query:
19314
19298
  *
19315
19299
  * ```xpath
19316
- * string(
19317
- * imaginary:itext-translation(
19318
- * xpath3-fn:environment-variable('activeLanguage')
19319
- * )
19320
- * /text[@id = $itextID]
19321
- * /value[not(@form)]
19300
+ * imaginary:itext-translation(
19301
+ * xpath3-fn:environment-variable('activeLanguage')
19322
19302
  * )
19303
+ * /text[@id = $itextID]
19304
+ * /value
19323
19305
  * ```
19324
19306
  *
19325
19307
  * Or alternately:
19326
19308
  *
19327
19309
  * ```xpath
19328
- * string(
19329
- * imaginary:itext-root()
19330
- * /translation[@lang = xpath3-fn:environment-variable('activeLanguage')]
19331
- * /text[@id = $itextID]
19332
- * /value[not(@form)]
19333
- * )
19310
+ * imaginary:itext-root()
19311
+ * /translation[@lang = xpath3-fn:environment-variable('activeLanguage')]
19312
+ * /text[@id = $itextID]
19313
+ * /value
19334
19314
  * ```
19335
- *
19336
- * @todo The above really feels like it adds some helpful clarity to how `jr:itext()` is designed to work, and the kinds of structures, state and input involved. Since there's already some discomfort around that API as specified, it's worth considering
19315
+ * Returns an array of `XFormsItextTranslationValueElement<T>`
19337
19316
  */
19338
- getDefaultTranslationText(itextID) {
19317
+ getTranslationValues(itextID) {
19339
19318
  const textElement = this.getTranslationTextElement(itextID);
19340
19319
  if (textElement == null) {
19341
- return "";
19342
- }
19343
- const { domProvider } = this;
19344
- for (const valueElement of domProvider.getChildrenByLocalName(textElement, "value")) {
19345
- if (!domProvider.hasLocalNamedAttribute(valueElement, "form")) {
19346
- return domProvider.getNodeValue(valueElement);
19347
- }
19320
+ return [];
19348
19321
  }
19349
- return "";
19322
+ return [...this.domProvider.getChildrenByLocalName(textElement, "value")];
19350
19323
  }
19351
19324
  getLanguages() {
19352
19325
  return this.languages;
@@ -19383,9 +19356,9 @@ class XFormsXPathEvaluator extends Evaluator {
19383
19356
  assertInternalXFormsXPathEvaluatorContext(context);
19384
19357
  return context.evaluator.secondaryInstancesById.get(id) ?? null;
19385
19358
  }
19386
- static getDefaultTranslationText(context, itextID) {
19359
+ static getTranslationValues(context, itextID) {
19387
19360
  assertInternalXFormsXPathEvaluatorContext(context);
19388
- return context.evaluator.itextTranslations.getDefaultTranslationText(itextID);
19361
+ return context.evaluator.itextTranslations.getTranslationValues(itextID);
19389
19362
  }
19390
19363
  rootNode;
19391
19364
  /**
@@ -19939,8 +19912,13 @@ const isTranslationFunctionCall = (syntaxNode) => {
19939
19912
  ]);
19940
19913
  };
19941
19914
  const isTranslationExpression = (expression) => {
19942
- const { rootNode } = expressionParser.parse(expression);
19943
- const functionCallNode = findTypedPrincipalExpressionNode(["function_call"], rootNode);
19915
+ let result;
19916
+ try {
19917
+ result = expressionParser.parse(expression);
19918
+ } catch {
19919
+ return false;
19920
+ }
19921
+ const functionCallNode = findTypedPrincipalExpressionNode(["function_call"], result.rootNode);
19944
19922
  if (functionCallNode == null) {
19945
19923
  return false;
19946
19924
  }
@@ -20104,77 +20082,43 @@ class DependentExpression {
20104
20082
  }
20105
20083
  }
20106
20084
 
20085
+ const isOutputElement = (element) => {
20086
+ return element.localName === "output" && element.hasAttribute("value");
20087
+ };
20107
20088
  class TextChunkExpression extends DependentExpression {
20089
+ source;
20090
+ // Set for the literal source, blank otherwise
20108
20091
  stringValue;
20109
- constructor(context, expression, options = {}) {
20110
- super(context, "string", expression, {
20092
+ constructor(context, resultType, expression, source, options = {}, literalValue = "") {
20093
+ super(context, resultType, expression, {
20111
20094
  semanticDependencies: {
20112
20095
  translations: options.isTranslated
20113
20096
  },
20114
20097
  ignoreContextReference: true
20115
20098
  });
20099
+ this.source = source;
20100
+ this.stringValue = literalValue;
20116
20101
  }
20117
- }
20118
-
20119
- class TextLiteralExpression extends TextChunkExpression {
20120
- constructor(context, stringValue) {
20121
- super(context, "null");
20122
- this.stringValue = stringValue;
20123
- }
20124
- static from(context, stringValue) {
20125
- return new this(context, stringValue);
20126
- }
20127
- source = "literal";
20128
- }
20129
-
20130
- const isOutputElement = (element) => {
20131
- return element.localName === "output" && element.hasAttribute("value");
20132
- };
20133
- class TextOutputExpression extends TextChunkExpression {
20134
- static from(context, element) {
20135
- if (isOutputElement(element)) {
20136
- return new this(context, element);
20137
- }
20138
- return null;
20139
- }
20140
- source = "output";
20141
- constructor(context, element) {
20142
- super(context, element.getAttribute("value"));
20102
+ static fromLiteral(context, stringValue) {
20103
+ return new TextChunkExpression(context, "string", "null", "literal", {}, stringValue);
20143
20104
  }
20144
- }
20145
-
20146
- class TextReferenceExpression extends TextChunkExpression {
20147
- static from(context, refExpression) {
20148
- return new this(context, refExpression);
20105
+ static fromReference(context, ref) {
20106
+ return new TextChunkExpression(context, "string", ref, "reference");
20149
20107
  }
20150
- source = "reference";
20151
- constructor(context, refExpression) {
20152
- super(context, refExpression);
20153
- }
20154
- }
20155
-
20156
- class TextTranslationExpression extends TextChunkExpression {
20157
- static fromMessage(context, maybeExpression) {
20158
- try {
20159
- expressionParser.parse(maybeExpression);
20160
- } catch {
20108
+ static fromOutput(context, element) {
20109
+ if (!isOutputElement(element)) {
20161
20110
  return null;
20162
20111
  }
20163
- if (isTranslationExpression(maybeExpression)) {
20164
- return new this(context, maybeExpression);
20165
- }
20166
- return null;
20112
+ return new TextChunkExpression(context, "string", element.getAttribute("value"), "output");
20167
20113
  }
20168
- static from(context, maybeExpression) {
20114
+ static fromTranslation(context, maybeExpression) {
20169
20115
  if (isTranslationExpression(maybeExpression)) {
20170
- return new this(context, maybeExpression);
20116
+ return new TextChunkExpression(context, "nodes", maybeExpression, "translation", {
20117
+ isTranslated: true
20118
+ });
20171
20119
  }
20172
20120
  return null;
20173
20121
  }
20174
- source = "translation";
20175
- constructor(context, expression) {
20176
- super(context, expression, { isTranslated: true });
20177
- }
20178
20122
  }
20179
20123
 
20180
20124
  const absolutePathNodeList = (pathNode) => {
@@ -20423,16 +20367,20 @@ class TextElementDefinition extends TextRangeDefinition {
20423
20367
  if (refExpression == null) {
20424
20368
  this.chunks = Array.from(sourceNode.childNodes).flatMap((childNode) => {
20425
20369
  if (isElementNode(childNode)) {
20426
- return TextOutputExpression.from(context, childNode) ?? [];
20370
+ return TextChunkExpression.fromOutput(context, childNode) ?? [];
20427
20371
  }
20428
20372
  if (isTextNode(childNode)) {
20429
- return TextLiteralExpression.from(context, childNode.data);
20373
+ return TextChunkExpression.fromLiteral(context, childNode.data);
20430
20374
  }
20431
20375
  return [];
20432
20376
  });
20433
20377
  } else {
20434
- const refChunk = TextTranslationExpression.from(context, refExpression) ?? TextReferenceExpression.from(context, refExpression);
20435
- this.chunks = [refChunk];
20378
+ const expression = TextChunkExpression.fromTranslation(context, refExpression);
20379
+ if (expression != null) {
20380
+ this.chunks = [expression];
20381
+ } else {
20382
+ this.chunks = [TextChunkExpression.fromReference(context, refExpression)];
20383
+ }
20436
20384
  }
20437
20385
  }
20438
20386
  }
@@ -20684,8 +20632,12 @@ class ItemsetLabelDefinition extends TextRangeDefinition {
20684
20632
  if (refExpression == null) {
20685
20633
  throw new Error("<itemset><label> missing ref attribute");
20686
20634
  }
20687
- const refChunk = TextTranslationExpression.from(this, refExpression) ?? TextReferenceExpression.from(this, refExpression);
20688
- this.chunks = [refChunk];
20635
+ const expression = TextChunkExpression.fromTranslation(this, refExpression);
20636
+ if (expression != null) {
20637
+ this.chunks = [expression];
20638
+ } else {
20639
+ this.chunks = [TextChunkExpression.fromReference(this, refExpression)];
20640
+ }
20689
20641
  }
20690
20642
  }
20691
20643
 
@@ -21593,8 +21545,12 @@ class MessageDefinition extends TextRangeDefinition {
21593
21545
  constructor(bind, role, message) {
21594
21546
  super(bind.form, bind, null);
21595
21547
  this.role = role;
21596
- const chunk = TextTranslationExpression.fromMessage(this, message) ?? TextLiteralExpression.from(this, message);
21597
- this.chunks = [chunk];
21548
+ const expression = TextChunkExpression.fromTranslation(this, message);
21549
+ if (expression != null) {
21550
+ this.chunks = [expression];
21551
+ } else {
21552
+ this.chunks = [TextChunkExpression.fromLiteral(this, message)];
21553
+ }
21598
21554
  }
21599
21555
  static from(bind, type) {
21600
21556
  const message = bind.bindElement.getAttributeNS(JAVAROSA_NAMESPACE_URI$1, type);
@@ -27508,7 +27464,8 @@ const getWrappedInstanceRootElement = (xml) => {
27508
27464
  return root;
27509
27465
  };
27510
27466
  const parseInstanceXML = (model, instanceXML) => {
27511
- const wrappedXML = wrapInstanceXML(model, instanceXML);
27467
+ const cleanedXML = instanceXML.replace(/<\?xml\s+[^?]*\?>\s*/, "");
27468
+ const wrappedXML = wrapInstanceXML(model, cleanedXML);
27512
27469
  const root = getWrappedInstanceRootElement(wrappedXML);
27513
27470
  return parseStaticDocumentFromDOMSubtree(root);
27514
27471
  };
@@ -29479,10 +29436,11 @@ class TextChunk {
29479
29436
  }
29480
29437
 
29481
29438
  class TextRange {
29482
- constructor(origin, role, chunks) {
29439
+ constructor(origin, role, chunks, mediaSources) {
29483
29440
  this.origin = origin;
29484
29441
  this.role = role;
29485
29442
  this.chunks = chunks;
29443
+ this.mediaSources = mediaSources;
29486
29444
  }
29487
29445
  *[Symbol.iterator]() {
29488
29446
  yield* this.chunks;
@@ -29493,44 +29451,56 @@ class TextRange {
29493
29451
  get asString() {
29494
29452
  return this.chunks.map((chunk) => chunk.asString).join("");
29495
29453
  }
29454
+ get imageSource() {
29455
+ return this.mediaSources?.image;
29456
+ }
29457
+ get audioSource() {
29458
+ return this.mediaSources?.audio;
29459
+ }
29460
+ get videoSource() {
29461
+ return this.mediaSources?.video;
29462
+ }
29496
29463
  }
29497
29464
 
29498
- const createComputedTextChunk = (context, textSource) => {
29499
- const { source } = textSource;
29500
- if (source === "literal") {
29501
- const { stringValue } = textSource;
29502
- return {
29503
- source,
29504
- getText: () => stringValue
29505
- };
29506
- }
29507
- return context.scope.runTask(() => {
29508
- const getText = createComputedExpression(context, textSource, {
29509
- defaultValue: ""
29510
- });
29511
- return {
29512
- source,
29513
- getText
29514
- };
29515
- });
29516
- };
29517
- const createTextChunks = (context, textSources) => {
29518
- return context.scope.runTask(() => {
29519
- const chunkComputations = textSources.map((textSource) => {
29520
- return createComputedTextChunk(context, textSource);
29521
- });
29522
- return createMemo(() => {
29523
- return chunkComputations.map(({ source, getText }) => {
29524
- return new TextChunk(context, source, getText());
29525
- });
29465
+ const createTextChunks = (context, chunkExpressions) => {
29466
+ return createMemo(() => {
29467
+ const chunks = [];
29468
+ const mediaSources = {};
29469
+ chunkExpressions.forEach((chunkExpression) => {
29470
+ if (chunkExpression.source === "literal") {
29471
+ chunks.push(new TextChunk(context, chunkExpression.source, chunkExpression.stringValue));
29472
+ return;
29473
+ }
29474
+ const computed = createComputedExpression(context, chunkExpression)();
29475
+ if (typeof computed === "string") {
29476
+ chunks.push(new TextChunk(context, chunkExpression.source, computed));
29477
+ return;
29478
+ } else {
29479
+ computed.forEach((itextForm) => {
29480
+ if (isEngineXPathElement(itextForm) && itextForm instanceof StaticElement) {
29481
+ const formAttribute = itextForm.getAttributeValue("form");
29482
+ if (!formAttribute) {
29483
+ const defaultFormValue = itextForm.getXPathValue();
29484
+ chunks.push(new TextChunk(context, chunkExpression.source, defaultFormValue));
29485
+ } else if (["image", "video", "audio"].includes(formAttribute)) {
29486
+ const formValue = itextForm.getXPathValue();
29487
+ if (JRResourceURL.isJRResourceReference(formValue)) {
29488
+ mediaSources[formAttribute] = JRResourceURL.from(formValue);
29489
+ }
29490
+ }
29491
+ }
29492
+ });
29493
+ }
29526
29494
  });
29495
+ return { chunks, mediaSources };
29527
29496
  });
29528
29497
  };
29529
29498
  const createTextRange = (context, role, definition) => {
29530
29499
  return context.scope.runTask(() => {
29531
- const getTextChunks = createTextChunks(context, definition.chunks);
29500
+ const textChunks = createTextChunks(context, definition.chunks);
29532
29501
  return createMemo(() => {
29533
- return new TextRange("form", role, getTextChunks());
29502
+ const chunks = textChunks();
29503
+ return new TextRange("form", role, chunks.chunks, chunks.mediaSources);
29534
29504
  });
29535
29505
  });
29536
29506
  };
@@ -29697,8 +29667,7 @@ const createInstanceValueState = (context) => {
29697
29667
  const engineViolationMessage = (context, role) => {
29698
29668
  const messageText = VALIDATION_TEXT[role];
29699
29669
  const chunk = new TextChunk(context, "literal", messageText);
29700
- const message = new TextRange("engine", role, [chunk]);
29701
- return () => message;
29670
+ return () => new TextRange("engine", role, [chunk]);
29702
29671
  };
29703
29672
  const createViolationMessage = (context, role, definition) => {
29704
29673
  if (definition == null) {
@@ -30875,6 +30844,9 @@ class SelectControl extends ValueNode {
30875
30844
  this.appearances = definition.bodyElement.appearances;
30876
30845
  this.selectType = definition.bodyElement.type;
30877
30846
  const valueOptions = createItemCollection(this);
30847
+ const isSelectWithImages = this.scope.runTask(() => {
30848
+ return createMemo(() => valueOptions().some((item) => !!item.label.imageSource));
30849
+ });
30878
30850
  const mapOptionsByValue = this.scope.runTask(() => {
30879
30851
  return createMemo(() => {
30880
30852
  return new Map(valueOptions().map((item) => [item.value, item]));
@@ -30909,7 +30881,8 @@ class SelectControl extends ValueNode {
30909
30881
  children: null,
30910
30882
  valueOptions,
30911
30883
  value: valueState,
30912
- instanceValue: this.getInstanceValue
30884
+ instanceValue: this.getInstanceValue,
30885
+ isSelectWithImages
30913
30886
  },
30914
30887
  this.instanceConfig
30915
30888
  );