@esri/solutions-components 0.5.6 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. package/dist/assets/t9n/map-select-tools/resources.json +2 -2
  2. package/dist/assets/t9n/map-select-tools/resources_en.json +2 -2
  3. package/dist/assets/t9n/public-notification/resources.json +1 -1
  4. package/dist/assets/t9n/public-notification/resources_en.json +1 -1
  5. package/dist/cjs/calcite-input-text_5.cjs.entry.js +4 -4
  6. package/dist/cjs/calcite-shell-panel_14.cjs.entry.js +1 -1
  7. package/dist/cjs/{downloadUtils-06876768.js → downloadUtils-8d66f03e.js} +95 -21
  8. package/dist/cjs/{index.es-d07646b4.js → index.es-4f5dfcc6.js} +1 -1
  9. package/dist/cjs/layer-table_2.cjs.entry.js +1 -1
  10. package/dist/cjs/public-notification.cjs.entry.js +12 -1
  11. package/dist/cjs/solution-configuration.cjs.entry.js +1 -1
  12. package/dist/cjs/solution-contents_3.cjs.entry.js +1 -1
  13. package/dist/cjs/{solution-store-70002b57.js → solution-store-ca4639d5.js} +3 -3
  14. package/dist/collection/components/map-select-tools/map-select-tools.css +7 -2
  15. package/dist/collection/components/map-select-tools/map-select-tools.js +2 -2
  16. package/dist/collection/components/public-notification/public-notification.js +12 -1
  17. package/dist/collection/components/refine-selection/refine-selection.css +85 -85
  18. package/dist/collection/utils/downloadUtils.js +94 -20
  19. package/dist/collection/utils/downloadUtils.ts +147 -27
  20. package/dist/components/downloadUtils.js +94 -20
  21. package/dist/components/map-select-tools2.js +14 -14
  22. package/dist/components/public-notification.js +12 -1
  23. package/dist/components/solution-store.js +3 -3
  24. package/dist/esm/calcite-input-text_5.entry.js +4 -4
  25. package/dist/esm/calcite-shell-panel_14.entry.js +1 -1
  26. package/dist/esm/{downloadUtils-046ce0aa.js → downloadUtils-b9785635.js} +95 -21
  27. package/dist/esm/{index.es-3fd0e8f6.js → index.es-70994c40.js} +1 -1
  28. package/dist/esm/layer-table_2.entry.js +1 -1
  29. package/dist/esm/public-notification.entry.js +12 -1
  30. package/dist/esm/solution-configuration.entry.js +1 -1
  31. package/dist/esm/solution-contents_3.entry.js +1 -1
  32. package/dist/esm/{solution-store-5d068b07.js → solution-store-70f874f8.js} +3 -3
  33. package/dist/solutions-components/{p-1850a763.entry.js → p-10336554.entry.js} +1 -1
  34. package/dist/solutions-components/{p-41802f6b.entry.js → p-16dfb254.entry.js} +1 -1
  35. package/dist/solutions-components/{p-99aec087.js → p-5d5e10d4.js} +1 -1
  36. package/dist/solutions-components/{p-4769a2a5.entry.js → p-5ed755a2.entry.js} +1 -1
  37. package/dist/solutions-components/{p-826a814d.js → p-78719506.js} +2 -2
  38. package/dist/solutions-components/{p-9f620303.entry.js → p-b4b19fd3.entry.js} +1 -1
  39. package/dist/solutions-components/{p-2e0db581.js → p-be943993.js} +11 -11
  40. package/dist/solutions-components/{p-7e409786.entry.js → p-c3e3d0bb.entry.js} +3 -3
  41. package/dist/solutions-components/p-f66a7ea7.entry.js +6 -0
  42. package/dist/solutions-components/solutions-components.esm.js +1 -1
  43. package/dist/solutions-components/utils/downloadUtils.ts +147 -27
  44. package/dist/solutions-components_commit.txt +8 -0
  45. package/dist/types/utils/downloadUtils.d.ts +21 -0
  46. package/package.json +1 -1
  47. package/dist/solutions-components/p-6e87198f.entry.js +0 -6
@@ -196,6 +196,38 @@ export function _createFilename(selectionSetNames) {
196
196
  const title = selectionSetNames.length > 0 ? selectionSetNames.join(", ") : "download";
197
197
  return title;
198
198
  }
199
+ /**
200
+ * Creates relationship queries for each relationship flag in a popup.
201
+ * @param layer Layer whose popup is to be examined
202
+ * @return Hash of relationships by their id, or null if there are no relationship flags in the
203
+ * popup; each relationship has the properties layer and relatedQuery for the related layer
204
+ * and the query for that layer
205
+ */
206
+ export function _createRelationshipQueries(layer) {
207
+ const relationships = {};
208
+ const relationshipFieldPattern = /\{relationships\/\d+\//gm;
209
+ const relationshipIdPattern = /\d+/;
210
+ // Test if this popup has any relationship references
211
+ const matches = layer.popupTemplate.content[0].text.match(relationshipFieldPattern);
212
+ if (matches) {
213
+ matches.forEach(match => {
214
+ // Add a query to a found relationship if we don't already have one
215
+ const id = match.match(relationshipIdPattern)[0];
216
+ if (!relationships.hasOwnProperty(id)) {
217
+ const relatedQuery = {
218
+ outFields: ['*'],
219
+ relationshipId: id,
220
+ returnGeometry: false
221
+ };
222
+ relationships[id] = {
223
+ layer,
224
+ relatedQuery
225
+ };
226
+ }
227
+ });
228
+ }
229
+ return relationships;
230
+ }
199
231
  /**
200
232
  * Prepares an attribute's value by applying domain and type information.
201
233
  *
@@ -256,7 +288,7 @@ function _prepareAttributeValue(attributeValue, attributeType, attributeDomain,
256
288
  * @returns Promise resolving when function is done
257
289
  */
258
290
  async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
259
- var _a, _b, _c, _d;
291
+ var _a, _b, _c, _d, _e, _f;
260
292
  const [intl] = await loadModules(["esri/intl"]);
261
293
  // Get the features to export
262
294
  const featureSet = await queryFeaturesByID(ids, layer);
@@ -270,6 +302,7 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
270
302
  const attributeFormats = {};
271
303
  // Get the label formatting, if any
272
304
  let labelFormat;
305
+ let relationshipQueries = {};
273
306
  let arcadeExecutors = {};
274
307
  if (layer.popupEnabled) {
275
308
  layer.popupTemplate.fieldInfos.forEach(
@@ -298,6 +331,8 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
298
331
  }
299
332
  else if (formatUsingLayerPopup && ((_d = (_c = layer.popupTemplate) === null || _c === void 0 ? void 0 : _c.content[0]) === null || _d === void 0 ? void 0 : _d.type) === "text") {
300
333
  labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
334
+ // Do we need any relationship queries?
335
+ relationshipQueries = _createRelationshipQueries(layer);
301
336
  // Do we need any Arcade executors?
302
337
  arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
303
338
  }
@@ -306,36 +341,75 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
306
341
  let labels;
307
342
  // eslint-disable-next-line unicorn/prefer-ternary
308
343
  if (labelFormat) {
344
+ console.log("labelFormat", labelFormat); //???
309
345
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
310
346
  const attributeRegExp = /\{\w+\}/g;
311
347
  // Find the label fields that we need to replace with values
312
- const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
313
- const attributeMatches = labelFormat.match(attributeRegExp);
348
+ const arcadeExpressionMatches = (_e = labelFormat.match(arcadeExpressionRegExp)) !== null && _e !== void 0 ? _e : [];
349
+ const attributeMatches = (_f = labelFormat.match(attributeRegExp)) !== null && _f !== void 0 ? _f : [];
314
350
  // Convert feature attributes into an array of labels
315
- labels = featureSet.features.map(feature => {
351
+ const relationshipKeys = Object.keys(relationshipQueries);
352
+ labels = await Promise.all(featureSet.features.map(async (feature) => {
316
353
  let labelPrep = labelFormat;
317
- // Replace Arcade expressions
318
- if (arcadeExpressionMatches) {
319
- arcadeExpressionMatches.forEach((match) => {
320
- const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
321
- const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
322
- labelPrep = labelPrep.replace(match, value);
323
- });
324
- }
325
- // Replace non-Arcade fields
326
- if (attributeMatches) {
327
- attributeMatches.forEach((match) => {
328
- const attributeName = match.substring(1, match.length - 1);
329
- const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], attributeFormats[attributeName], intl);
330
- labelPrep = labelPrep.replace(match, value);
354
+ // Replace Arcade expressions in this feature
355
+ arcadeExpressionMatches.forEach((match) => {
356
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
357
+ const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
358
+ labelPrep = labelPrep.replace(match, value);
359
+ });
360
+ // Replace relationship expressions in this feature
361
+ const relatedFeatureQueries = [];
362
+ const relationshipIds = [];
363
+ relationshipKeys.forEach((relationshipId) => {
364
+ const relationship = relationshipQueries[relationshipId];
365
+ const objectId = feature.attributes[relationship.layer.objectIdField];
366
+ const relatedQuery = Object.assign(Object.assign({}, relationship.relatedQuery), { objectIds: [objectId] });
367
+ relatedFeatureQueries.push(relationship.layer.queryRelatedFeatures(relatedQuery));
368
+ relationshipIds.push(relationshipId);
369
+ });
370
+ // Wait for all of the queries for related records for this label
371
+ const relatedFeatureQueryResults = await Promise.all(relatedFeatureQueries);
372
+ console.log("relatedFeatureQueryResults", relatedFeatureQueryResults); //???
373
+ relatedFeatureQueryResults.forEach((relatedFeatureQueryResult, i) => {
374
+ // We have an object with FeatureSets grouped by source layer or table objectIds
375
+ const relationshipId = relationshipIds[i];
376
+ console.log("relationshipId", relationshipId); //???
377
+ console.log("relatedFeatureQueryResult", relatedFeatureQueryResult); //???
378
+ // Run through the source layer or table objectIds
379
+ Object.keys(relatedFeatureQueryResult).forEach(relatedFeatureSetId => {
380
+ console.log("relatedFeatureSetId", relatedFeatureSetId); //???
381
+ // We have a feature set
382
+ const relatedFeatures = relatedFeatureQueryResult[relatedFeatureSetId].features;
383
+ console.log("relatedFeatures", relatedFeatures); //???
384
+ // Get the values from each feature and replace them in the label
385
+ relatedFeatures.forEach(feature => {
386
+ // Merge the base and related feature attributes and create the label
387
+ // Prefix related feature's attributes with "relationships/<id>/" to match popup
388
+ const rePrefix = "\{relationships/" + relationshipId + "/";
389
+ const reSuffix = "\}";
390
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + "attributeName" + reSuffix); //???
391
+ const attributes = feature.attributes;
392
+ Object.keys(attributes).forEach(attributeName => {
393
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + attributeName + reSuffix); //???
394
+ // Replace the value using the attribute name as a relationship
395
+ const attributeRelationshipRegExp = new RegExp(rePrefix + attributeName + reSuffix, "g");
396
+ labelPrep = labelPrep.replaceAll(attributeRelationshipRegExp, attributes[attributeName]);
397
+ });
398
+ });
331
399
  });
332
- }
400
+ });
401
+ // Replace non-Arcade fields in this feature
402
+ attributeMatches.forEach((match) => {
403
+ const attributeName = match.substring(1, match.length - 1);
404
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], attributeFormats[attributeName], intl);
405
+ labelPrep = labelPrep.replace(match, value);
406
+ });
333
407
  // Split label into lines
334
408
  let label = labelPrep.split(lineSeparatorChar);
335
409
  // Trim lines
336
410
  label = label.map(line => line.trim());
337
411
  return label;
338
- });
412
+ }));
339
413
  }
340
414
  else {
341
415
  // Export all attributes
@@ -43,6 +43,25 @@ interface IAttributeTypes {
43
43
  [attributeName: string]: string;
44
44
  }
45
45
 
46
+ interface ILayerRelationshipQuery {
47
+ layer: __esri.FeatureLayer;
48
+ relatedQuery: IRelatedFeaturesQuery;
49
+ }
50
+
51
+ interface ILayerRelationshipQueryHash {
52
+ [relationshipId: string]: ILayerRelationshipQuery;
53
+ }
54
+
55
+ // Class RelationshipQuery doesn't appear to work, and so since
56
+ // https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#queryRelatedFeatures
57
+ // says that the relationshipQuery argument is autocast, we'll set up a variant for the class
58
+ interface IRelatedFeaturesQuery {
59
+ outFields: string[];
60
+ relationshipId: string;
61
+ returnGeometry: boolean;
62
+ objectIds?: number;
63
+ }
64
+
46
65
  const lineSeparatorChar = "|";
47
66
 
48
67
  //#endregion
@@ -278,6 +297,44 @@ export function _createFilename(
278
297
  return title;
279
298
  }
280
299
 
300
+ /**
301
+ * Creates relationship queries for each relationship flag in a popup.
302
+ * @param layer Layer whose popup is to be examined
303
+ * @return Hash of relationships by their id, or null if there are no relationship flags in the
304
+ * popup; each relationship has the properties layer and relatedQuery for the related layer
305
+ * and the query for that layer
306
+ */
307
+ export function _createRelationshipQueries(
308
+ layer: __esri.FeatureLayer,
309
+ ): ILayerRelationshipQueryHash {
310
+
311
+ const relationships: ILayerRelationshipQueryHash = {};
312
+ const relationshipFieldPattern = /\{relationships\/\d+\//gm;
313
+ const relationshipIdPattern = /\d+/;
314
+
315
+ // Test if this popup has any relationship references
316
+ const matches = layer.popupTemplate.content[0].text.match(relationshipFieldPattern);
317
+ if (matches) {
318
+ matches.forEach(match => {
319
+ // Add a query to a found relationship if we don't already have one
320
+ const id = match.match(relationshipIdPattern)[0];
321
+ if (!relationships.hasOwnProperty(id)) {
322
+ const relatedQuery: IRelatedFeaturesQuery = {
323
+ outFields: ['*'],
324
+ relationshipId: id,
325
+ returnGeometry: false
326
+ };
327
+ relationships[id] = {
328
+ layer,
329
+ relatedQuery
330
+ } as ILayerRelationshipQuery;
331
+ }
332
+ });
333
+ }
334
+
335
+ return relationships;
336
+ }
337
+
281
338
  /**
282
339
  * Prepares an attribute's value by applying domain and type information.
283
340
  *
@@ -369,6 +426,7 @@ async function _prepareLabels(
369
426
 
370
427
  // Get the label formatting, if any
371
428
  let labelFormat: string;
429
+ let relationshipQueries: ILayerRelationshipQueryHash = {};
372
430
  let arcadeExecutors: IArcadeExecutors = {};
373
431
  if (layer.popupEnabled) {
374
432
  layer.popupTemplate.fieldInfos.forEach(
@@ -401,6 +459,9 @@ async function _prepareLabels(
401
459
  } else if (formatUsingLayerPopup && layer.popupTemplate?.content[0]?.type === "text") {
402
460
  labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
403
461
 
462
+ // Do we need any relationship queries?
463
+ relationshipQueries = _createRelationshipQueries(layer);
464
+
404
465
  // Do we need any Arcade executors?
405
466
  arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
406
467
  }
@@ -410,41 +471,100 @@ async function _prepareLabels(
410
471
  let labels: string[][];
411
472
  // eslint-disable-next-line unicorn/prefer-ternary
412
473
  if (labelFormat) {
474
+ console.log("labelFormat", labelFormat);//???
413
475
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
414
476
  const attributeRegExp = /\{\w+\}/g;
415
477
 
416
478
  // Find the label fields that we need to replace with values
417
- const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
418
- const attributeMatches = labelFormat.match(attributeRegExp);
479
+ const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp) ?? [];
480
+ const attributeMatches = labelFormat.match(attributeRegExp) ?? [];
419
481
 
420
482
  // Convert feature attributes into an array of labels
421
- labels = featureSet.features.map(
422
- feature => {
483
+ const relationshipKeys = Object.keys(relationshipQueries);
484
+ labels = await Promise.all(featureSet.features.map(
485
+ async feature => {
423
486
  let labelPrep = labelFormat;
424
487
 
425
- // Replace Arcade expressions
426
- if (arcadeExpressionMatches) {
427
- arcadeExpressionMatches.forEach(
428
- (match: string) => {
429
- const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
430
- const value = arcadeExecutors[expressionName].execute({"$feature": feature});
431
- labelPrep = labelPrep.replace(match, value);
432
- }
433
- )
434
- }
488
+ // Replace Arcade expressions in this feature
489
+ arcadeExpressionMatches.forEach(
490
+ (match: string) => {
491
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
492
+ const value = arcadeExecutors[expressionName].execute({"$feature": feature});
493
+ labelPrep = labelPrep.replace(match, value);
494
+ }
495
+ )
496
+
497
+ // Replace relationship expressions in this feature
498
+ const relatedFeatureQueries = [] as Promise<__esri.FeatureSet>[];
499
+ const relationshipIds = [] as string[];
500
+ relationshipKeys.forEach(
501
+ (relationshipId) => {
502
+ const relationship = relationshipQueries[relationshipId];
503
+ const objectId = feature.attributes[relationship.layer.objectIdField];
504
+ const relatedQuery = {
505
+ ...relationship.relatedQuery,
506
+ objectIds: [objectId]
507
+ };
508
+ relatedFeatureQueries.push(relationship.layer.queryRelatedFeatures(relatedQuery as any));
509
+ relationshipIds.push(relationshipId);
510
+ }
511
+ );
435
512
 
436
- // Replace non-Arcade fields
437
- if (attributeMatches) {
438
- attributeMatches.forEach(
439
- (match: string) => {
440
- const attributeName = match.substring(1, match.length - 1);
441
- const value = _prepareAttributeValue(feature.attributes[attributeName],
442
- attributeTypes[attributeName], attributeDomains[attributeName],
443
- attributeFormats[attributeName], intl);
444
- labelPrep = labelPrep.replace(match, value);
445
- }
446
- )
447
- }
513
+ // Wait for all of the queries for related records for this label
514
+ const relatedFeatureQueryResults = await Promise.all(relatedFeatureQueries);
515
+ console.log("relatedFeatureQueryResults", relatedFeatureQueryResults);//???
516
+ relatedFeatureQueryResults.forEach(
517
+ (relatedFeatureQueryResult, i) => {
518
+ // We have an object with FeatureSets grouped by source layer or table objectIds
519
+ const relationshipId = relationshipIds[i];
520
+ console.log("relationshipId", relationshipId);//???
521
+ console.log("relatedFeatureQueryResult", relatedFeatureQueryResult);//???
522
+
523
+ // Run through the source layer or table objectIds
524
+ Object.keys(relatedFeatureQueryResult).forEach(
525
+ relatedFeatureSetId => {
526
+ console.log("relatedFeatureSetId", relatedFeatureSetId);//???
527
+ // We have a feature set
528
+ const relatedFeatures = relatedFeatureQueryResult[relatedFeatureSetId].features;
529
+ console.log("relatedFeatures", relatedFeatures);//???
530
+
531
+ // Get the values from each feature and replace them in the label
532
+ relatedFeatures.forEach(
533
+ feature => {
534
+ // Merge the base and related feature attributes and create the label
535
+ // Prefix related feature's attributes with "relationships/<id>/" to match popup
536
+ const rePrefix = "\{relationships/" + relationshipId + "/";
537
+ const reSuffix = "\}";
538
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + "attributeName" + reSuffix);//???
539
+
540
+ const attributes = feature.attributes;
541
+ Object.keys(attributes).forEach(
542
+ attributeName => {
543
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + attributeName + reSuffix);//???
544
+ // Replace the value using the attribute name as a relationship
545
+ const attributeRelationshipRegExp = new RegExp(rePrefix + attributeName + reSuffix, "g");
546
+ labelPrep = labelPrep.replaceAll(attributeRelationshipRegExp, attributes[attributeName]);
547
+ }
548
+ );
549
+ }
550
+ );
551
+ }
552
+ );
553
+ }
554
+ );
555
+
556
+ // Replace non-Arcade fields in this feature
557
+ attributeMatches.forEach(
558
+ (match: string) => {
559
+ const attributeName = match.substring(1, match.length - 1);
560
+
561
+ const value = _prepareAttributeValue(feature.attributes[attributeName],
562
+ attributeTypes[attributeName], attributeDomains[attributeName],
563
+ attributeFormats[attributeName], intl);
564
+ labelPrep = labelPrep.replace(match, value);
565
+
566
+ }
567
+ )
448
568
 
449
569
  // Split label into lines
450
570
  let label = labelPrep.split(lineSeparatorChar);
@@ -454,7 +574,7 @@ async function _prepareLabels(
454
574
 
455
575
  return label;
456
576
  }
457
- );
577
+ ));
458
578
 
459
579
  } else {
460
580
  // Export all attributes
@@ -2425,6 +2425,38 @@ function _createFilename(selectionSetNames) {
2425
2425
  const title = selectionSetNames.length > 0 ? selectionSetNames.join(", ") : "download";
2426
2426
  return title;
2427
2427
  }
2428
+ /**
2429
+ * Creates relationship queries for each relationship flag in a popup.
2430
+ * @param layer Layer whose popup is to be examined
2431
+ * @return Hash of relationships by their id, or null if there are no relationship flags in the
2432
+ * popup; each relationship has the properties layer and relatedQuery for the related layer
2433
+ * and the query for that layer
2434
+ */
2435
+ function _createRelationshipQueries(layer) {
2436
+ const relationships = {};
2437
+ const relationshipFieldPattern = /\{relationships\/\d+\//gm;
2438
+ const relationshipIdPattern = /\d+/;
2439
+ // Test if this popup has any relationship references
2440
+ const matches = layer.popupTemplate.content[0].text.match(relationshipFieldPattern);
2441
+ if (matches) {
2442
+ matches.forEach(match => {
2443
+ // Add a query to a found relationship if we don't already have one
2444
+ const id = match.match(relationshipIdPattern)[0];
2445
+ if (!relationships.hasOwnProperty(id)) {
2446
+ const relatedQuery = {
2447
+ outFields: ['*'],
2448
+ relationshipId: id,
2449
+ returnGeometry: false
2450
+ };
2451
+ relationships[id] = {
2452
+ layer,
2453
+ relatedQuery
2454
+ };
2455
+ }
2456
+ });
2457
+ }
2458
+ return relationships;
2459
+ }
2428
2460
  /**
2429
2461
  * Prepares an attribute's value by applying domain and type information.
2430
2462
  *
@@ -2485,7 +2517,7 @@ function _prepareAttributeValue(attributeValue, attributeType, attributeDomain,
2485
2517
  * @returns Promise resolving when function is done
2486
2518
  */
2487
2519
  async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
2488
- var _a, _b, _c, _d;
2520
+ var _a, _b, _c, _d, _e, _f;
2489
2521
  const [intl] = await loadModules(["esri/intl"]);
2490
2522
  // Get the features to export
2491
2523
  const featureSet = await queryFeaturesByID(ids, layer);
@@ -2499,6 +2531,7 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
2499
2531
  const attributeFormats = {};
2500
2532
  // Get the label formatting, if any
2501
2533
  let labelFormat;
2534
+ let relationshipQueries = {};
2502
2535
  let arcadeExecutors = {};
2503
2536
  if (layer.popupEnabled) {
2504
2537
  layer.popupTemplate.fieldInfos.forEach(
@@ -2527,6 +2560,8 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
2527
2560
  }
2528
2561
  else if (formatUsingLayerPopup && ((_d = (_c = layer.popupTemplate) === null || _c === void 0 ? void 0 : _c.content[0]) === null || _d === void 0 ? void 0 : _d.type) === "text") {
2529
2562
  labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
2563
+ // Do we need any relationship queries?
2564
+ relationshipQueries = _createRelationshipQueries(layer);
2530
2565
  // Do we need any Arcade executors?
2531
2566
  arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
2532
2567
  }
@@ -2535,36 +2570,75 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
2535
2570
  let labels;
2536
2571
  // eslint-disable-next-line unicorn/prefer-ternary
2537
2572
  if (labelFormat) {
2573
+ console.log("labelFormat", labelFormat); //???
2538
2574
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
2539
2575
  const attributeRegExp = /\{\w+\}/g;
2540
2576
  // Find the label fields that we need to replace with values
2541
- const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
2542
- const attributeMatches = labelFormat.match(attributeRegExp);
2577
+ const arcadeExpressionMatches = (_e = labelFormat.match(arcadeExpressionRegExp)) !== null && _e !== void 0 ? _e : [];
2578
+ const attributeMatches = (_f = labelFormat.match(attributeRegExp)) !== null && _f !== void 0 ? _f : [];
2543
2579
  // Convert feature attributes into an array of labels
2544
- labels = featureSet.features.map(feature => {
2580
+ const relationshipKeys = Object.keys(relationshipQueries);
2581
+ labels = await Promise.all(featureSet.features.map(async (feature) => {
2545
2582
  let labelPrep = labelFormat;
2546
- // Replace Arcade expressions
2547
- if (arcadeExpressionMatches) {
2548
- arcadeExpressionMatches.forEach((match) => {
2549
- const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
2550
- const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
2551
- labelPrep = labelPrep.replace(match, value);
2552
- });
2553
- }
2554
- // Replace non-Arcade fields
2555
- if (attributeMatches) {
2556
- attributeMatches.forEach((match) => {
2557
- const attributeName = match.substring(1, match.length - 1);
2558
- const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], attributeFormats[attributeName], intl);
2559
- labelPrep = labelPrep.replace(match, value);
2583
+ // Replace Arcade expressions in this feature
2584
+ arcadeExpressionMatches.forEach((match) => {
2585
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
2586
+ const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
2587
+ labelPrep = labelPrep.replace(match, value);
2588
+ });
2589
+ // Replace relationship expressions in this feature
2590
+ const relatedFeatureQueries = [];
2591
+ const relationshipIds = [];
2592
+ relationshipKeys.forEach((relationshipId) => {
2593
+ const relationship = relationshipQueries[relationshipId];
2594
+ const objectId = feature.attributes[relationship.layer.objectIdField];
2595
+ const relatedQuery = Object.assign(Object.assign({}, relationship.relatedQuery), { objectIds: [objectId] });
2596
+ relatedFeatureQueries.push(relationship.layer.queryRelatedFeatures(relatedQuery));
2597
+ relationshipIds.push(relationshipId);
2598
+ });
2599
+ // Wait for all of the queries for related records for this label
2600
+ const relatedFeatureQueryResults = await Promise.all(relatedFeatureQueries);
2601
+ console.log("relatedFeatureQueryResults", relatedFeatureQueryResults); //???
2602
+ relatedFeatureQueryResults.forEach((relatedFeatureQueryResult, i) => {
2603
+ // We have an object with FeatureSets grouped by source layer or table objectIds
2604
+ const relationshipId = relationshipIds[i];
2605
+ console.log("relationshipId", relationshipId); //???
2606
+ console.log("relatedFeatureQueryResult", relatedFeatureQueryResult); //???
2607
+ // Run through the source layer or table objectIds
2608
+ Object.keys(relatedFeatureQueryResult).forEach(relatedFeatureSetId => {
2609
+ console.log("relatedFeatureSetId", relatedFeatureSetId); //???
2610
+ // We have a feature set
2611
+ const relatedFeatures = relatedFeatureQueryResult[relatedFeatureSetId].features;
2612
+ console.log("relatedFeatures", relatedFeatures); //???
2613
+ // Get the values from each feature and replace them in the label
2614
+ relatedFeatures.forEach(feature => {
2615
+ // Merge the base and related feature attributes and create the label
2616
+ // Prefix related feature's attributes with "relationships/<id>/" to match popup
2617
+ const rePrefix = "\{relationships/" + relationshipId + "/";
2618
+ const reSuffix = "\}";
2619
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + "attributeName" + reSuffix); //???
2620
+ const attributes = feature.attributes;
2621
+ Object.keys(attributes).forEach(attributeName => {
2622
+ console.log("/rePrefix + attributeName + reSuffix/g", rePrefix + attributeName + reSuffix); //???
2623
+ // Replace the value using the attribute name as a relationship
2624
+ const attributeRelationshipRegExp = new RegExp(rePrefix + attributeName + reSuffix, "g");
2625
+ labelPrep = labelPrep.replaceAll(attributeRelationshipRegExp, attributes[attributeName]);
2626
+ });
2627
+ });
2560
2628
  });
2561
- }
2629
+ });
2630
+ // Replace non-Arcade fields in this feature
2631
+ attributeMatches.forEach((match) => {
2632
+ const attributeName = match.substring(1, match.length - 1);
2633
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], attributeFormats[attributeName], intl);
2634
+ labelPrep = labelPrep.replace(match, value);
2635
+ });
2562
2636
  // Split label into lines
2563
2637
  let label = labelPrep.split(lineSeparatorChar);
2564
2638
  // Trim lines
2565
2639
  label = label.map(line => line.trim());
2566
2640
  return label;
2567
- });
2641
+ }));
2568
2642
  }
2569
2643
  else {
2570
2644
  // Export all attributes
@@ -22,14 +22,14 @@ import { d as defineCustomElement$b } from './input-message.js';
22
22
  import { d as defineCustomElement$a } from './label.js';
23
23
  import { d as defineCustomElement$9 } from './loader.js';
24
24
  import { d as defineCustomElement$8 } from './option.js';
25
- import { d as defineCustomElement$7 } from './progress.js';
26
- import { d as defineCustomElement$6 } from './select.js';
27
- import { d as defineCustomElement$5 } from './slider.js';
28
- import { d as defineCustomElement$4 } from './switch.js';
29
- import { d as defineCustomElement$3 } from './tooltip.js';
25
+ import { d as defineCustomElement$7 } from './popover.js';
26
+ import { d as defineCustomElement$6 } from './progress.js';
27
+ import { d as defineCustomElement$5 } from './select.js';
28
+ import { d as defineCustomElement$4 } from './slider.js';
29
+ import { d as defineCustomElement$3 } from './switch.js';
30
30
  import { d as defineCustomElement$2 } from './map-draw-tools2.js';
31
31
 
32
- const mapSelectToolsCss = ":host{display:block}.div-visible{display:inherit}.div-visible-search{display:flex;height:44px;align-items:center;padding-bottom:0}.div-not-visible{display:none}.padding-bottom-1{padding-bottom:1rem}.padding-top-1{padding-top:1rem}.search-widget{width:100% !important;border:1px solid var(--calcite-ui-border-input)}.w-100{width:100%}.w-50{width:50%}.search-distance-container{padding-top:\"1rem\" !important}.end-border{-webkit-border-end:1px solid var(--calcite-ui-border-2);border-inline-end:1px solid var(--calcite-ui-border-2)}.search-distance{display:flex;padding-top:1rem}.font-bold{font:bold}.border-bottom{border-bottom:1px solid var(--calcite-ui-border-2)}.tooltip-container{justify-content:center;display:flex}.padding-start-1-2{-webkit-padding-start:0.5rem;padding-inline-start:0.5rem}.icon{--calcite-ui-icon-color:var(--calcite-ui-brand)}";
32
+ const mapSelectToolsCss = ":host{display:block}.div-visible{display:inherit}.div-visible-search{display:flex;height:44px;align-items:center;padding-bottom:0}.div-not-visible{display:none}.padding-bottom-1{padding-bottom:1rem}.padding-top-1{padding-top:1rem}.search-widget{width:100% !important;border:1px solid var(--calcite-ui-border-input)}.w-100{width:100%}.w-50{width:50%}.search-distance-container{padding-top:\"1rem\" !important}.end-border{-webkit-border-end:1px solid var(--calcite-ui-border-2);border-inline-end:1px solid var(--calcite-ui-border-2)}.search-distance{display:flex;padding-top:1rem}.font-bold{font:bold}.border-bottom{border-bottom:1px solid var(--calcite-ui-border-2)}.tooltip-container{display:flex}.padding-start-1-2{-webkit-padding-start:0.5rem;padding-inline-start:0.5rem}.icon{--calcite-ui-icon-color:var(--calcite-ui-brand)}.tooltip-message{padding:5px 8px;font-weight:var(--calcite-font-weight-normal);color:var(--calcite-ui-text-2)}";
33
33
 
34
34
  const MapSelectTools = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
35
35
  constructor() {
@@ -200,14 +200,14 @@ const MapSelectTools = /*@__PURE__*/ proxyCustomElement(class extends HTMLElemen
200
200
  var _a, _b;
201
201
  const showBufferToolsClass = this._searchDistanceEnabled ? "search-distance" : "div-not-visible";
202
202
  const bufferDistance = typeof ((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.distance) === "number" ? this.selectionSet.distance : this.defaultBufferDistance;
203
- return (h("div", null, h("div", { class: "padding-top-1 display-flex" }, h("calcite-label", { class: "label-margin-0 w-100", layout: "inline-space-between" }, h("div", { class: "tooltip-container" }, this._translations.searchDistance, h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "search-distance-icon", scale: "s" }), h("calcite-tooltip", { label: "Data disclaimer", placement: "auto", "reference-element": "search-distance-icon" }, h("span", null, this._translations.useSearchDistanceTootip))), h("calcite-switch", { checked: this._searchDistanceEnabled, onCalciteSwitchChange: () => this._searchDistanceEnabled = !this._searchDistanceEnabled }))), h("div", { class: showBufferToolsClass }, h("buffer-tools", { disabled: !this._searchDistanceEnabled, distance: bufferDistance, geometries: this.geometries, onBufferComplete: (evt) => this._bufferComplete(evt), ref: (el) => this._bufferTools = el, unit: ((_b = this.selectionSet) === null || _b === void 0 ? void 0 : _b.unit) || this.defaultBufferUnit }))));
203
+ return (h("div", null, h("div", { class: "padding-top-1 display-flex" }, h("calcite-label", { class: "label-margin-0 w-100", layout: "inline-space-between" }, h("div", { class: "tooltip-container" }, this._translations.searchDistance, h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "search-distance-icon", scale: "s" }))), h("calcite-popover", { closable: true, label: "", referenceElement: "search-distance-icon" }, h("span", { class: "tooltip-message" }, this._translations.useSearchDistanceTootip)), h("calcite-switch", { checked: this._searchDistanceEnabled, onCalciteSwitchChange: () => this._searchDistanceEnabled = !this._searchDistanceEnabled })), h("div", { class: showBufferToolsClass }, h("buffer-tools", { disabled: !this._searchDistanceEnabled, distance: bufferDistance, geometries: this.geometries, onBufferComplete: (evt) => this._bufferComplete(evt), ref: (el) => this._bufferTools = el, unit: ((_b = this.selectionSet) === null || _b === void 0 ? void 0 : _b.unit) || this.defaultBufferUnit }))));
204
204
  }
205
205
  /**
206
206
  * Renders the map layer picker component.
207
207
  */
208
208
  _getUseLayerFeaturesOptions() {
209
209
  const useLayerFeaturesClass = this._useLayerFeaturesEnabled ? "div-visible" : "div-not-visible";
210
- return (h("div", null, h("div", { class: "padding-top-1 display-flex" }, h("calcite-label", { class: "label-margin-0 w-100", layout: "inline-space-between" }, h("div", { class: "tooltip-container" }, this._translations.useLayerFeatures, h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "use-layer-features-icon", scale: "s" }), h("calcite-tooltip", { label: "Data disclaimer", placement: "auto", "reference-element": "use-layer-features-icon" }, h("span", null, this._translations.useLayerFeaturesTooltip))), h("calcite-switch", { checked: this._useLayerFeaturesEnabled, onCalciteSwitchChange: () => { this._useLayerFeaturesEnabledChanged(); } }))), h("div", { class: useLayerFeaturesClass + " padding-top-1" }, h("map-layer-picker", { enabledLayerIds: this.enabledLayerIds, mapView: this.mapView, onLayerSelectionChange: (evt) => { void this._layerSelectionChange(evt); }, selectedLayerIds: this.layerViews.map(l => l.layer.id), selectionMode: "single" }))));
210
+ return (h("div", null, h("div", { class: "padding-top-1 display-flex" }, h("calcite-label", { class: "label-margin-0 w-100", layout: "inline-space-between" }, h("div", { class: "tooltip-container" }, this._translations.useLayerFeatures, h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "use-layer-features-icon", scale: "s" }))), h("calcite-popover", { closable: true, label: "", referenceElement: "use-layer-features-icon" }, h("span", { class: "tooltip-message" }, this._translations.useLayerFeaturesTooltip)), h("calcite-switch", { checked: this._useLayerFeaturesEnabled, onCalciteSwitchChange: () => { this._useLayerFeaturesEnabledChanged(); } })), h("div", { class: useLayerFeaturesClass + " padding-top-1" }, h("map-layer-picker", { enabledLayerIds: this.enabledLayerIds, mapView: this.mapView, onLayerSelectionChange: (evt) => { void this._layerSelectionChange(evt); }, selectedLayerIds: this.layerViews.map(l => l.layer.id), selectionMode: "single" }))));
211
211
  }
212
212
  /**
213
213
  * Renders the number of selected features
@@ -809,7 +809,7 @@ function defineCustomElement() {
809
809
  if (typeof customElements === "undefined") {
810
810
  return;
811
811
  }
812
- const components = ["map-select-tools", "buffer-tools", "calcite-action", "calcite-chip", "calcite-combobox", "calcite-combobox-item", "calcite-graph", "calcite-icon", "calcite-input", "calcite-input-message", "calcite-label", "calcite-loader", "calcite-option", "calcite-progress", "calcite-select", "calcite-slider", "calcite-switch", "calcite-tooltip", "map-draw-tools", "map-layer-picker"];
812
+ const components = ["map-select-tools", "buffer-tools", "calcite-action", "calcite-chip", "calcite-combobox", "calcite-combobox-item", "calcite-graph", "calcite-icon", "calcite-input", "calcite-input-message", "calcite-label", "calcite-loader", "calcite-option", "calcite-popover", "calcite-progress", "calcite-select", "calcite-slider", "calcite-switch", "map-draw-tools", "map-layer-picker"];
813
813
  components.forEach(tagName => { switch (tagName) {
814
814
  case "map-select-tools":
815
815
  if (!customElements.get(tagName)) {
@@ -876,27 +876,27 @@ function defineCustomElement() {
876
876
  defineCustomElement$8();
877
877
  }
878
878
  break;
879
- case "calcite-progress":
879
+ case "calcite-popover":
880
880
  if (!customElements.get(tagName)) {
881
881
  defineCustomElement$7();
882
882
  }
883
883
  break;
884
- case "calcite-select":
884
+ case "calcite-progress":
885
885
  if (!customElements.get(tagName)) {
886
886
  defineCustomElement$6();
887
887
  }
888
888
  break;
889
- case "calcite-slider":
889
+ case "calcite-select":
890
890
  if (!customElements.get(tagName)) {
891
891
  defineCustomElement$5();
892
892
  }
893
893
  break;
894
- case "calcite-switch":
894
+ case "calcite-slider":
895
895
  if (!customElements.get(tagName)) {
896
896
  defineCustomElement$4();
897
897
  }
898
898
  break;
899
- case "calcite-tooltip":
899
+ case "calcite-switch":
900
900
  if (!customElements.get(tagName)) {
901
901
  defineCustomElement$3();
902
902
  }