@esri/solutions-components 0.5.9 → 0.5.11

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 (102) hide show
  1. package/dist/assets/data/images/onboarding.png +0 -0
  2. package/dist/assets/t9n/public-notification/resources.json +1 -1
  3. package/dist/assets/t9n/public-notification/resources_ar.json +5 -4
  4. package/dist/assets/t9n/public-notification/resources_bg.json +5 -4
  5. package/dist/assets/t9n/public-notification/resources_bs.json +5 -4
  6. package/dist/assets/t9n/public-notification/resources_ca.json +5 -4
  7. package/dist/assets/t9n/public-notification/resources_cs.json +5 -4
  8. package/dist/assets/t9n/public-notification/resources_da.json +5 -4
  9. package/dist/assets/t9n/public-notification/resources_de.json +5 -4
  10. package/dist/assets/t9n/public-notification/resources_el.json +5 -4
  11. package/dist/assets/t9n/public-notification/resources_en.json +1 -1
  12. package/dist/assets/t9n/public-notification/resources_es.json +5 -4
  13. package/dist/assets/t9n/public-notification/resources_et.json +5 -4
  14. package/dist/assets/t9n/public-notification/resources_fi.json +5 -4
  15. package/dist/assets/t9n/public-notification/resources_fr.json +5 -4
  16. package/dist/assets/t9n/public-notification/resources_he.json +5 -4
  17. package/dist/assets/t9n/public-notification/resources_hr.json +5 -4
  18. package/dist/assets/t9n/public-notification/resources_hu.json +5 -4
  19. package/dist/assets/t9n/public-notification/resources_id.json +5 -4
  20. package/dist/assets/t9n/public-notification/resources_it.json +5 -4
  21. package/dist/assets/t9n/public-notification/resources_ja.json +5 -4
  22. package/dist/assets/t9n/public-notification/resources_ko.json +5 -4
  23. package/dist/assets/t9n/public-notification/resources_lt.json +5 -4
  24. package/dist/assets/t9n/public-notification/resources_lv.json +5 -4
  25. package/dist/assets/t9n/public-notification/resources_nb.json +5 -4
  26. package/dist/assets/t9n/public-notification/resources_nl.json +5 -4
  27. package/dist/assets/t9n/public-notification/resources_pl.json +5 -4
  28. package/dist/assets/t9n/public-notification/resources_pt-BR.json +5 -4
  29. package/dist/assets/t9n/public-notification/resources_pt-PT.json +5 -4
  30. package/dist/assets/t9n/public-notification/resources_ro.json +5 -4
  31. package/dist/assets/t9n/public-notification/resources_ru.json +5 -4
  32. package/dist/assets/t9n/public-notification/resources_sk.json +5 -4
  33. package/dist/assets/t9n/public-notification/resources_sl.json +5 -4
  34. package/dist/assets/t9n/public-notification/resources_sr.json +5 -4
  35. package/dist/assets/t9n/public-notification/resources_sv.json +5 -4
  36. package/dist/assets/t9n/public-notification/resources_th.json +5 -4
  37. package/dist/assets/t9n/public-notification/resources_tr.json +5 -4
  38. package/dist/assets/t9n/public-notification/resources_uk.json +5 -4
  39. package/dist/assets/t9n/public-notification/resources_vi.json +5 -4
  40. package/dist/assets/t9n/public-notification/resources_zh-CN.json +5 -4
  41. package/dist/assets/t9n/public-notification/resources_zh-HK.json +5 -4
  42. package/dist/assets/t9n/public-notification/resources_zh-TW.json +5 -4
  43. package/dist/cjs/buffer-tools_4.cjs.entry.js +18 -4
  44. package/dist/cjs/calcite-input-text_5.cjs.entry.js +6 -11
  45. package/dist/cjs/{downloadUtils-4ef4b28b.js → downloadUtils-7a0fd3c0.js} +77 -20
  46. package/dist/cjs/{index.es-cbe67d5b.js → index.es-9965b78c.js} +1 -1
  47. package/dist/cjs/layer-table_2.cjs.entry.js +8 -3
  48. package/dist/cjs/loader.cjs.js +1 -1
  49. package/dist/cjs/public-notification.cjs.entry.js +72 -23
  50. package/dist/cjs/solutions-components.cjs.js +1 -1
  51. package/dist/collection/components/buffer-tools/buffer-tools.css +4 -0
  52. package/dist/collection/components/buffer-tools/buffer-tools.js +1 -1
  53. package/dist/collection/components/layer-table/layer-table.js +7 -2
  54. package/dist/collection/components/map-draw-tools/map-draw-tools.css +4 -0
  55. package/dist/collection/components/map-draw-tools/map-draw-tools.js +15 -1
  56. package/dist/collection/components/map-select-tools/map-select-tools.js +18 -1
  57. package/dist/collection/components/pdf-download/pdf-download.js +3 -9
  58. package/dist/collection/components/public-notification/public-notification.css +210 -203
  59. package/dist/collection/components/public-notification/public-notification.js +73 -22
  60. package/dist/collection/demos/new-public-notification.html +6 -4
  61. package/dist/collection/utils/downloadUtils.js +74 -19
  62. package/dist/collection/utils/downloadUtils.ts +93 -26
  63. package/dist/collection/utils/interfaces.ts +2 -2
  64. package/dist/components/buffer-tools2.js +2 -2
  65. package/dist/components/downloadUtils.js +75 -20
  66. package/dist/components/layer-table2.js +7 -2
  67. package/dist/components/map-draw-tools2.js +16 -2
  68. package/dist/components/map-select-tools2.js +3 -1
  69. package/dist/components/pdf-download2.js +3 -9
  70. package/dist/components/public-notification.js +74 -24
  71. package/dist/esm/buffer-tools_4.entry.js +18 -4
  72. package/dist/esm/calcite-combobox_3.entry.js +1 -1
  73. package/dist/esm/calcite-input-text_5.entry.js +7 -12
  74. package/dist/esm/{downloadUtils-2ebeb46d.js → downloadUtils-a447bab1.js} +77 -22
  75. package/dist/esm/{index.es-6dd27a48.js → index.es-b9cb902a.js} +2 -2
  76. package/dist/esm/layer-table_2.entry.js +9 -4
  77. package/dist/esm/loader.js +1 -1
  78. package/dist/esm/{mapViewUtils-ebbd4733.js → mapViewUtils-27dfdc29.js} +1 -1
  79. package/dist/esm/public-notification.entry.js +74 -25
  80. package/dist/esm/solutions-components.js +1 -1
  81. package/dist/solutions-components/demos/new-public-notification.html +6 -4
  82. package/dist/solutions-components/p-2f4e1ddf.entry.js +6 -0
  83. package/dist/solutions-components/{p-657caece.js → p-345f517c.js} +5 -5
  84. package/dist/solutions-components/{p-390d7de8.js → p-4b426bab.js} +1 -1
  85. package/dist/solutions-components/p-62492a2d.entry.js +17 -0
  86. package/dist/solutions-components/{p-ad82c173.js → p-80757ebd.js} +1 -1
  87. package/dist/solutions-components/{p-83e3db8e.entry.js → p-db099e05.entry.js} +1 -1
  88. package/dist/solutions-components/p-f3fbc327.entry.js +6 -0
  89. package/dist/solutions-components/{p-b4d4b50a.entry.js → p-f516f183.entry.js} +3 -3
  90. package/dist/solutions-components/solutions-components.esm.js +1 -1
  91. package/dist/solutions-components/utils/downloadUtils.ts +93 -26
  92. package/dist/solutions-components/utils/interfaces.ts +2 -2
  93. package/dist/types/components/map-draw-tools/map-draw-tools.d.ts +6 -0
  94. package/dist/types/components/map-select-tools/map-select-tools.d.ts +5 -0
  95. package/dist/types/components/public-notification/public-notification.d.ts +29 -11
  96. package/dist/types/components.d.ts +8 -0
  97. package/dist/types/utils/downloadUtils.d.ts +22 -8
  98. package/dist/types/utils/interfaces.d.ts +2 -2
  99. package/package.json +2 -2
  100. package/dist/solutions-components/p-6f27bea2.entry.js +0 -17
  101. package/dist/solutions-components/p-db82a9ce.entry.js +0 -6
  102. package/dist/solutions-components/p-deb90ce7.entry.js +0 -6
@@ -24,8 +24,14 @@ import { loadModules } from "../../utils/loadModules";
24
24
  import { goToSelection, highlightFeatures } from "../../utils/mapViewUtils";
25
25
  import state from "../../utils/publicNotificationStore";
26
26
  import { getLocaleComponentStrings } from "../../utils/locale";
27
+ import { consolidateLabels, removeDuplicateLabels } from "../../utils/downloadUtils";
28
+ import { getAssetPath } from "@stencil/core";
27
29
  export class PublicNotification {
28
30
  constructor() {
31
+ /**
32
+ * string: The url to the onboarding image
33
+ */
34
+ this._onboardingImageUrl = "";
29
35
  /**
30
36
  * number: The number of selected features
31
37
  */
@@ -51,6 +57,7 @@ export class PublicNotification {
51
57
  this._addTitle = false;
52
58
  this._downloadActive = true;
53
59
  this._exportType = EExportType.PDF;
60
+ this._numDuplicates = 0;
54
61
  this._pageType = EPageType.LIST;
55
62
  this._saveEnabled = false;
56
63
  this._selectionSets = [];
@@ -117,6 +124,9 @@ export class PublicNotification {
117
124
  if ((_a = this.mapView) === null || _a === void 0 ? void 0 : _a.popup) {
118
125
  this.mapView.popup.autoOpenEnabled = pageType !== EPageType.LIST ? false : this._popupsEnabled;
119
126
  }
127
+ if (pageType === EPageType.EXPORT) {
128
+ this._numDuplicates = await this._getNumDuplicates();
129
+ }
120
130
  this._clearHighlight();
121
131
  if (oldPageType === EPageType.SELECT || oldPageType === EPageType.REFINE) {
122
132
  // clear any draw shapes or buffers
@@ -144,6 +154,7 @@ export class PublicNotification {
144
154
  await this._getTranslations();
145
155
  await this._initModules();
146
156
  this._initSymbols();
157
+ this._onboardingImageUrl = getAssetPath(`../assets/data/images/onboarding.png`);
147
158
  }
148
159
  /**
149
160
  * Renders the component.
@@ -302,7 +313,16 @@ export class PublicNotification {
302
313
  */
303
314
  _getListPage() {
304
315
  const hasSets = this._hasSelections();
305
- return (h("calcite-panel", null, this._getLabel(this._translations.myLists), this._getNotice(hasSets ? this._translations.listHasSetsTip : this._translations.selectLayerAndAdd, "padding-sides-1 padding-bottom-1"), hasSets ? this._getSelectionSetList() : (null), h("div", { class: "display-flex padding-1" }, h("calcite-button", { onClick: () => { this._setPageType(EPageType.SELECT); }, width: "full" }, this._translations.add))));
316
+ return (h("calcite-panel", null, this._getLabel(this._translations.myLists), this._getNotice(hasSets ? this._translations.listHasSetsTip : this._translations.selectLayerAndAdd, "padding-sides-1 padding-bottom-1"), hasSets ? this._getSelectionSetList() : (this._getOnboardingImage()), h("div", { class: "display-flex padding-1" }, h("calcite-button", { onClick: () => { this._setPageType(EPageType.SELECT); }, width: "full" }, this._translations.add))));
317
+ }
318
+ /**
319
+ * Display an image to help illustrate the basic workflow of the widget
320
+ *
321
+ * @returns the image node to display
322
+ * @protected
323
+ */
324
+ _getOnboardingImage() {
325
+ return (h("div", { class: "display-flex padding-sides-1" }, h("img", { class: "img-container", src: this._onboardingImageUrl })));
306
326
  }
307
327
  /**
308
328
  * Create the selection sets list node for the List page
@@ -324,7 +344,7 @@ export class PublicNotification {
324
344
  validSet = numIds > 0;
325
345
  }
326
346
  if (validSet) {
327
- prev.push((h("calcite-list-item", { description: this._translations.selectedFeatures.replace("{{n}}", ids.length.toString()), label: cur.label, onClick: () => this._gotoSelection(cur, this.mapView) }, h("div", { slot: "content" }, h("div", { class: "list-label" }, cur.label), h("div", { class: "list-description" }, (_a = cur === null || cur === void 0 ? void 0 : cur.layerView) === null || _a === void 0 ? void 0 : _a.layer.title), h("div", { class: "list-description" }, this._translations.selectedFeatures.replace("{{n}}", ids.length.toString()))), this._getAction(true, "pencil", "", (evt) => this._openSelection(cur, evt), false, "actions-end"), this._getAction(true, "x", "", (evt) => this._deleteSelection(i, evt), false, "actions-end"))));
347
+ prev.push((h("calcite-list-item", { label: cur.label, onClick: () => this._gotoSelection(cur, this.mapView) }, h("div", { slot: "content" }, h("div", { class: "list-label" }, cur.label), h("div", { class: "list-description" }, (_a = cur === null || cur === void 0 ? void 0 : cur.layerView) === null || _a === void 0 ? void 0 : _a.layer.title), h("div", { class: "list-description" }, this._translations.selectedFeatures.replace("{{n}}", ids.length.toString()))), this._getAction(true, "pencil", "", (evt) => this._openSelection(cur, evt), false, "actions-end"), this._getAction(true, "x", "", (evt) => this._deleteSelection(i, evt), false, "actions-end"))));
328
348
  }
329
349
  return prev;
330
350
  }, []))));
@@ -360,39 +380,67 @@ export class PublicNotification {
360
380
  return validateRefineSet && hasRefineSet ? ids.length > 0 || this._selectionSets.length > 1 : this._selectionSets.length > 0;
361
381
  }
362
382
  /**
363
- * Check if any duplicates exist
383
+ * Check if any duplicate labels exist
364
384
  *
365
385
  * @returns true if duplicates are found
366
386
  *
367
387
  * @protected
368
388
  */
369
- _hasDuplicates() {
370
- const selectedIds = this._getSelectedIds();
371
- return this._getNumDuplicates(selectedIds) > 0;
389
+ async _getNumDuplicates() {
390
+ const exportInfos = this._getExportInfos();
391
+ const labels = await consolidateLabels(exportInfos);
392
+ const duplicatesRemoved = removeDuplicateLabels(labels);
393
+ return labels.length - duplicatesRemoved.length;
372
394
  }
373
395
  /**
374
- * Return the number of duplicates
375
- *
376
- * @param ids the list of currently selected ids
396
+ * Get key details about what to export
377
397
  *
378
- * @returns the number of duplicates
398
+ * @returns IExportInfos that contain ids and layer
379
399
  *
380
400
  * @protected
381
401
  */
382
- _getNumDuplicates(ids) {
383
- return ids.length - new Set(ids).size;
402
+ _getExportInfos() {
403
+ return this._selectionSets.reduce((prev, cur) => {
404
+ if (cur.download) {
405
+ if (cur.workflowType !== EWorkflowType.REFINE) {
406
+ const id = cur.layerView.layer.id;
407
+ this._updateIds(id, cur.layerView, cur.selectedIds, prev);
408
+ }
409
+ else {
410
+ // REFINE stores ids differently as it can contain ids from multiple layers
411
+ // REFINE will only ever be 1 ISelectionSet
412
+ Object.keys(cur.refineInfos).forEach(k => {
413
+ const refineIds = cur.refineInfos[k];
414
+ this._updateIds(k, refineIds.layerView, refineIds.addIds, prev);
415
+ });
416
+ }
417
+ }
418
+ return prev;
419
+ }, {});
384
420
  }
385
421
  /**
386
- * Get the complete list of selected ids
422
+ * Consolidate ids for each layer
423
+ *
424
+ * @param id the layer id from the selectionSet
425
+ * @param layerView the layerView from the selectionSet
426
+ * @param ids the selectedIds from the selectionSet
427
+ * @param obj the object that will store the consolidated ids and layer info
387
428
  *
388
- * @returns all currently selected IDs
429
+ * @returns IExportInfo key details that will be used for export
389
430
  *
390
431
  * @protected
391
432
  */
392
- _getSelectedIds() {
393
- return this._selectionSets.reduce((prev, cur) => {
394
- return prev.concat(cur.download ? cur.selectedIds : []);
395
- }, []);
433
+ _updateIds(id, layerView, ids, obj) {
434
+ if (obj[id]) {
435
+ obj[id].ids = obj[id].ids.concat(ids);
436
+ }
437
+ else {
438
+ obj[id] = {
439
+ layerView,
440
+ ids
441
+ };
442
+ }
443
+ return obj;
396
444
  }
397
445
  /**
398
446
  * Create the Select page that shows the selection workflows
@@ -402,7 +450,7 @@ export class PublicNotification {
402
450
  */
403
451
  _getSelectPage() {
404
452
  const noticeText = this._translations.selectSearchTip;
405
- return (h("calcite-panel", null, this._getLabel(this._translations.stepTwoFull, true), this._getNotice(noticeText), h("div", null, h("map-select-tools", { bufferColor: this.bufferColor, bufferOutlineColor: this.bufferOutlineColor, class: "font-bold", customLabelEnabled: this.customLabelEnabled, defaultBufferDistance: this.defaultBufferDistance, defaultBufferUnit: this.defaultBufferUnit, enabledLayerIds: this.selectionLayerIds, isUpdate: !!this._activeSelection, mapView: this.mapView, noResultText: this.noResultText, onSelectionSetChange: (evt) => this._updateForSelection(evt), ref: (el) => { this._selectTools = el; }, searchConfiguration: this._searchConfiguration, selectionSet: this._activeSelection, sketchLineSymbol: this.sketchLineSymbol, sketchPointSymbol: this.sketchPointSymbol, sketchPolygonSymbol: this.sketchPolygonSymbol })), this._getPageNavButtons(this._translations.done, this._numSelected === 0, () => { void this._saveSelection(); }, this._translations.cancel, false, () => { void this._home(); })));
453
+ return (h("calcite-panel", null, this._getLabel(this._translations.stepTwoFull, true), this._getNotice(noticeText), h("div", null, h("map-select-tools", { bufferColor: this.bufferColor, bufferOutlineColor: this.bufferOutlineColor, class: "font-bold", customLabelEnabled: this.customLabelEnabled, defaultBufferDistance: this.defaultBufferDistance, defaultBufferUnit: this.defaultBufferUnit, enabledLayerIds: this.addresseeLayerIds, isUpdate: !!this._activeSelection, mapView: this.mapView, noResultText: this.noResultText, onSelectionSetChange: (evt) => this._updateForSelection(evt), ref: (el) => { this._selectTools = el; }, searchConfiguration: this._searchConfiguration, selectionLayerIds: this.selectionLayerIds, selectionSet: this._activeSelection, sketchLineSymbol: this.sketchLineSymbol, sketchPointSymbol: this.sketchPointSymbol, sketchPolygonSymbol: this.sketchPolygonSymbol })), this._getPageNavButtons(this._translations.done, this._numSelected === 0, () => { void this._saveSelection(); }, this._translations.cancel, false, () => { void this._home(); })));
406
454
  }
407
455
  /**
408
456
  * Create the main download page that has the shared aspects of both PDF and CSV
@@ -413,8 +461,8 @@ export class PublicNotification {
413
461
  */
414
462
  _getExportPage() {
415
463
  const hasSelections = this._hasSelections(this.showRefineSelection);
416
- const numDuplicates = this._getNumDuplicates(this._getSelectedIds());
417
- return (h("calcite-panel", null, h("div", null, this._getLabel(this._translations.export, false), hasSelections ? (h("div", null, this._getNotice(this._translations.exportTip, "padding-sides-1"), this._getLabel(this._translations.exportListsLabel), this._getExportSelectionLists(), h("div", { class: "padding-sides-1" }, h("div", { class: "display-flex" }, h("calcite-label", { layout: "inline" }, h("calcite-checkbox", { ref: (el) => { this._removeDuplicates = el; } }), h("div", { class: "display-flex" }, this._translations.removeDuplicate, h("div", { class: "info-message padding-start-1-2" }, h("calcite-input-message", { class: "info-blue margin-top-0", scale: "m" }, ` ${this._translations.numDuplicates.replace("{{n}}", numDuplicates.toString())}`)))), h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "remove-duplicates-icon", scale: "s" })), h("calcite-popover", { closable: true, label: "", referenceElement: "remove-duplicates-icon" }, h("span", { class: "tooltip-message" }, this._translations.duplicatesTip))), h("div", { class: "border-bottom" }), h("div", { class: "padding-top-sides-1" }, h("calcite-segmented-control", { class: "w-100", onCalciteSegmentedControlChange: (evt) => this._exportTypeChange(evt) }, h("calcite-segmented-control-item", { checked: this._exportType === EExportType.PDF, class: "w-50 end-border", value: EExportType.PDF }, this._translations.pdf), h("calcite-segmented-control-item", { checked: this._exportType === EExportType.CSV, class: "w-50", value: EExportType.CSV }, this._translations.csv))), h("div", { class: "padding-bottom-1" }, this._getExportOptions()), h("div", { class: "padding-1 display-flex" }, h("calcite-button", { disabled: !this._downloadActive, onClick: () => void this._export(), width: "full" }, this._translations.export)))) : (this._getNotice(this._translations.downloadNoLists, "padding-sides-1 padding-bottom-1")))));
464
+ const displayDuplicatesClass = this._numDuplicates > 0 ? "display-block" : "display-none";
465
+ return (h("calcite-panel", null, h("div", null, this._getLabel(this._translations.export, false), hasSelections ? (h("div", null, this._getNotice(this._translations.exportTip, "padding-sides-1"), this._getLabel(this._translations.exportListsLabel), this._getExportSelectionLists(), h("div", { class: "padding-sides-1 " + displayDuplicatesClass }, h("div", { class: "display-flex" }, h("calcite-label", { layout: "inline" }, h("calcite-checkbox", { ref: (el) => { this._removeDuplicates = el; } }), h("div", { class: "display-flex" }, this._translations.removeDuplicate, h("div", { class: "info-message padding-start-1-2" }, h("calcite-input-message", { class: "info-blue margin-top-0", scale: "m" }, ` ${this._translations.numDuplicates.replace("{{n}}", this._numDuplicates.toString())}`)))), h("calcite-icon", { class: "padding-start-1-2 icon", icon: "question", id: "remove-duplicates-icon", scale: "s" })), h("calcite-popover", { closable: true, label: "", referenceElement: "remove-duplicates-icon" }, h("span", { class: "tooltip-message" }, this._translations.duplicatesTip))), h("div", { class: "border-bottom" }), h("div", { class: "padding-top-sides-1" }, h("calcite-segmented-control", { class: "w-100", onCalciteSegmentedControlChange: (evt) => this._exportTypeChange(evt) }, h("calcite-segmented-control-item", { checked: this._exportType === EExportType.PDF, class: "w-50 end-border", value: EExportType.PDF }, this._translations.pdf), h("calcite-segmented-control-item", { checked: this._exportType === EExportType.CSV, class: "w-50", value: EExportType.CSV }, this._translations.csv))), h("div", { class: "padding-bottom-1" }, this._getExportOptions()), h("div", { class: "padding-1 display-flex" }, h("calcite-button", { disabled: !this._downloadActive, onClick: () => void this._export(), width: "full" }, this._translations.export)))) : (this._getNotice(this._translations.downloadNoLists, "padding-sides-1 padding-bottom-1")))));
418
466
  }
419
467
  /**
420
468
  * Store the user selected export type CSV || PDF
@@ -494,13 +542,14 @@ export class PublicNotification {
494
542
  */
495
543
  _getExportSelectionLists() {
496
544
  return this._selectionSets.reduce((prev, cur) => {
545
+ var _a;
497
546
  const ids = this._getSelectionSetIds(cur);
498
547
  const validSet = cur.workflowType !== EWorkflowType.REFINE || ids.length > 0;
499
548
  if (!this._downloadActive && cur.download && validSet) {
500
549
  this._downloadActive = true;
501
550
  }
502
551
  if (validSet) {
503
- prev.push((h("div", { class: "display-flex padding-sides-1 padding-bottom-1" }, h("calcite-checkbox", { checked: cur.download, class: "align-center", onClick: () => { void this._toggleDownload(cur.id); } }), h("calcite-list", { class: "list-border margin-start-1-2 width-full", id: "download-list" }, h("calcite-list-item", { description: this._translations.selectedFeatures.replace("{{n}}", ids.length.toString()), disabled: !cur.download, label: cur.label, onClick: () => { void this._toggleDownload(cur.id); } })))));
552
+ prev.push((h("div", { class: "display-flex padding-sides-1 padding-bottom-1" }, h("calcite-checkbox", { checked: cur.download, class: "align-center", onClick: () => { void this._toggleDownload(cur.id); } }), h("calcite-list", { class: "list-border margin-start-1-2 width-full", id: "download-list" }, h("calcite-list-item", { disabled: !cur.download, label: cur.label, onClick: () => { void this._toggleDownload(cur.id); } }, h("div", { slot: "content" }, h("div", { class: "list-label" }, cur.label), h("div", { class: "list-description" }, (_a = cur === null || cur === void 0 ? void 0 : cur.layerView) === null || _a === void 0 ? void 0 : _a.layer.title), h("div", { class: "list-description" }, this._translations.selectedFeatures.replace("{{n}}", ids.length.toString()))))))));
504
553
  }
505
554
  return prev;
506
555
  }, []) || (h("div", null));
@@ -520,6 +569,7 @@ export class PublicNotification {
520
569
  return ss;
521
570
  });
522
571
  this._downloadActive = isActive;
572
+ this._numDuplicates = await this._getNumDuplicates();
523
573
  await this._highlightFeatures();
524
574
  }
525
575
  /**
@@ -1087,6 +1137,7 @@ export class PublicNotification {
1087
1137
  "_addTitle": {},
1088
1138
  "_downloadActive": {},
1089
1139
  "_exportType": {},
1140
+ "_numDuplicates": {},
1090
1141
  "_pageType": {},
1091
1142
  "_saveEnabled": {},
1092
1143
  "_selectionSets": {},
@@ -55,12 +55,12 @@
55
55
 
56
56
  <link
57
57
  rel="stylesheet"
58
- href="https://js.arcgis.com/4.25/esri/themes/light/main.css"
58
+ href="https://js.arcgis.com/4.27/esri/themes/light/main.css"
59
59
  />
60
60
  <link rel="stylesheet" href="https://webapps-cdn.esri.com/CDN/fonts/v1.4.1/fonts.css" />
61
61
  <link rel="stylesheet" href="../solutions-components.css" type="text/css">
62
62
 
63
- <script src="https://js.arcgis.com/4.25/"></script>
63
+ <script src="https://js.arcgis.com/4.27/"></script>
64
64
  <script type="module" src="../solutions-components.esm.js"></script>
65
65
 
66
66
  <script>
@@ -90,7 +90,8 @@
90
90
  });
91
91
  demo.mapView.ui.add(legend, "top-left");
92
92
  });
93
- //demo.addresseeLayerIds = ["18434515eb8-layer-12"];
93
+ // solutions layer id
94
+ //demo.addresseeLayerIds = ["TaxParcels_3419"];
94
95
  //demo.defaultBufferDistance = 100;
95
96
  //demo.defaultBufferUnit = "kilometers";
96
97
  demo.featureEffect = {
@@ -99,7 +100,8 @@
99
100
  };
100
101
  demo.featureHighlightEnabled = true;
101
102
  demo.noResultText = "No results found";
102
- //demo.selectionLayerIds = ["1843422bf6b-layer-7"];
103
+ // solutions layer id
104
+ //demo.selectionLayerIds = ["SiteAddresses_8878"];
103
105
  demo.showSearchSettings = true;
104
106
  demo.customLabelEnabled = true;
105
107
  // demo.bufferColor = [227, 0, 0, 0.8];
@@ -30,39 +30,52 @@ const lineSeparatorChar = "|";
30
30
  /**
31
31
  * Downloads csv of mailing labels for the provided list of ids
32
32
  *
33
- * @param selectionSetNames Names of the selection sets used to provide ids
34
- * @param layer Layer providing features and attributes for download
35
- * @param ids List of ids to download
33
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
36
34
  * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
37
35
  * all attributes are exported
38
36
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
39
37
  * @param addColumnTitle Indicates if column headings should be included in output
40
38
  * @returns Promise resolving when function is done
41
39
  */
42
- export async function downloadCSV(selectionSetNames, layer, ids, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
43
- const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
44
- exportCSV(_createFilename(selectionSetNames), labels);
40
+ export async function downloadCSV(exportInfos, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
41
+ let labels = await consolidateLabels(exportInfos, formatUsingLayerPopup, addColumnTitle, true);
42
+ labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
43
+ const layerIds = Object.keys(exportInfos);
44
+ let layerLabels = [];
45
+ labels.forEach(label => {
46
+ const id = label[0];
47
+ // layerIds are stored as value separator at the end of the values for a given layer
48
+ if (layerIds.indexOf(id) < 0) {
49
+ layerLabels.push(label);
50
+ }
51
+ else {
52
+ const selectionSetNames = _getSelectionSetNames(exportInfos, new RegExp(`\\b${id}\\b`));
53
+ // once we see the layerId we have reached the end of it's values and should export
54
+ exportCSV(_createFilename(selectionSetNames), layerLabels);
55
+ layerLabels = [];
56
+ }
57
+ });
45
58
  return Promise.resolve();
46
59
  }
47
60
  /**
48
61
  * Downloads csv of mailing labels for the provided list of ids
49
62
  *
50
- * @param selectionSetNames Names of the selection sets used to provide ids
51
- * @param layer Layer providing features and attributes for download
52
- * @param ids List of ids to download
63
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
53
64
  * @param labelPageDescription Provides PDF page layout info
54
65
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
55
66
  * @param title Title for each page
56
67
  * @param initialImageDataUrl Data URL of image for first page
57
68
  * @returns Promise resolving when function is done
58
69
  */
59
- export async function downloadPDF(selectionSetNames, layer, ids, labelPageDescription, removeDuplicates = false, title = "", initialImageDataUrl = "") {
60
- let labels = await _prepareLabels(layer, ids, removeDuplicates);
70
+ export async function downloadPDF(exportInfos, labelPageDescription, removeDuplicates = false, title = "", initialImageDataUrl = "") {
71
+ let labels = await consolidateLabels(exportInfos);
72
+ const selectionSetNames = _getSelectionSetNames(exportInfos);
61
73
  labels =
62
74
  // Remove empty lines in labels
63
75
  labels.map(labelLines => labelLines.filter(line => line.length > 0))
64
76
  // Remove empty labels
65
77
  .filter(label => label.length > 0);
78
+ labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
66
79
  exportPDF(_createFilename(selectionSetNames), labels, labelPageDescription, title, initialImageDataUrl);
67
80
  return Promise.resolve();
68
81
  }
@@ -281,13 +294,12 @@ function _prepareAttributeValue(attributeValue, attributeType, attributeDomain,
281
294
  *
282
295
  * @param layer Layer from which to fetch features
283
296
  * @param ids List of ids to download
284
- * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
285
297
  * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
286
298
  * all attributes are exported
287
299
  * @param includeHeaderNames Add the label format at the front of the list of generated labels
288
300
  * @returns Promise resolving when function is done
289
301
  */
290
- async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
302
+ async function _prepareLabels(layer, ids, formatUsingLayerPopup = true, includeHeaderNames = false) {
291
303
  var _a, _b, _c, _d, _e, _f;
292
304
  const [intl] = await loadModules(["esri/intl"]);
293
305
  // Get the features to export
@@ -412,12 +424,6 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
412
424
  });
413
425
  });
414
426
  }
415
- // Remove duplicates
416
- if (removeDuplicates) {
417
- const labelsAsStrings = labels.map(label => JSON.stringify(label));
418
- const uniqueLabels = new Set(labelsAsStrings);
419
- labels = Array.from(uniqueLabels, labelString => JSON.parse(labelString));
420
- }
421
427
  // Add header names
422
428
  if (includeHeaderNames) {
423
429
  let headerNames = [];
@@ -434,4 +440,53 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
434
440
  }
435
441
  return Promise.resolve(labels);
436
442
  }
443
+ /**
444
+ * Remove any duplicate labels
445
+ *
446
+ * @param labels Labels to evaluate for duplicates
447
+ * @returns labels with duplicates removed
448
+ */
449
+ export function removeDuplicateLabels(labels) {
450
+ const labelsAsStrings = labels.map(label => JSON.stringify(label));
451
+ const uniqueLabels = new Set(labelsAsStrings);
452
+ return Array.from(uniqueLabels, labelString => JSON.parse(labelString));
453
+ }
454
+ /**
455
+ * Extract selectionSetNames from the provided exportInfos
456
+ *
457
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
458
+ * @returns selectionSetNames that will be used for export filenames
459
+ */
460
+ function _getSelectionSetNames(exportInfos, id = /.+/) {
461
+ let selectionSetNames = [];
462
+ Object.keys(exportInfos).forEach(k => {
463
+ const exportInfo = exportInfos[k];
464
+ if (id.test(k)) {
465
+ selectionSetNames = selectionSetNames.concat(exportInfo.selectionSetNames);
466
+ }
467
+ });
468
+ return selectionSetNames;
469
+ }
470
+ /**
471
+ * Create and consolidate labels from all layers
472
+ *
473
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
474
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
475
+ * all attributes are exported
476
+ * @param includeHeaderNames Add the label format at the front of the list of generated labels
477
+ * @returns selectionSetNames that will be used for export filenames
478
+ */
479
+ export async function consolidateLabels(exportInfos, formatUsingLayerPopup = true, includeHeaderNames = false, isCSVExport = false) {
480
+ const labelRequests = [];
481
+ Object.keys(exportInfos).forEach(k => {
482
+ const labelInfo = exportInfos[k];
483
+ labelRequests.push(_prepareLabels(labelInfo.layerView.layer, labelInfo.ids, formatUsingLayerPopup, includeHeaderNames));
484
+ if (isCSVExport) {
485
+ // add the layer id as a temp value separator that we can use to split values for CSV export
486
+ labelRequests.push(Promise.resolve([[k]]));
487
+ }
488
+ });
489
+ const labels = await Promise.all(labelRequests);
490
+ return labels.reduce((prev, cur) => prev.concat(cur), []);
491
+ }
437
492
  //#endregion
@@ -20,6 +20,7 @@ import { exportCSV } from "./csvUtils";
20
20
  import { ILabel, exportPDF } from "./pdfUtils";
21
21
  import { loadModules } from "./loadModules";
22
22
  import { queryFeaturesByID } from "./queryUtils";
23
+ import { IExportInfo, IExportInfos } from "../utils/interfaces";
23
24
 
24
25
  export { ILabel } from "./pdfUtils";
25
26
 
@@ -70,9 +71,7 @@ const lineSeparatorChar = "|";
70
71
  /**
71
72
  * Downloads csv of mailing labels for the provided list of ids
72
73
  *
73
- * @param selectionSetNames Names of the selection sets used to provide ids
74
- * @param layer Layer providing features and attributes for download
75
- * @param ids List of ids to download
74
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
76
75
  * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
77
76
  * all attributes are exported
78
77
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
@@ -80,16 +79,30 @@ const lineSeparatorChar = "|";
80
79
  * @returns Promise resolving when function is done
81
80
  */
82
81
  export async function downloadCSV(
83
- selectionSetNames: string[],
84
- layer: __esri.FeatureLayer,
85
- ids: number[],
82
+ exportInfos: IExportInfos,
86
83
  formatUsingLayerPopup: boolean,
87
84
  removeDuplicates = false,
88
85
  addColumnTitle = false
89
86
  ): Promise<void> {
90
- const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
87
+ let labels = await consolidateLabels(exportInfos, formatUsingLayerPopup, addColumnTitle, true);
88
+ labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
89
+
90
+ const layerIds = Object.keys(exportInfos);
91
+
92
+ let layerLabels = [];
93
+ labels.forEach(label => {
94
+ const id = label[0];
95
+ // layerIds are stored as value separator at the end of the values for a given layer
96
+ if (layerIds.indexOf(id) < 0) {
97
+ layerLabels.push(label);
98
+ } else {
99
+ const selectionSetNames = _getSelectionSetNames(exportInfos, new RegExp(`\\b${id}\\b`));
91
100
 
92
- exportCSV(_createFilename(selectionSetNames), labels);
101
+ // once we see the layerId we have reached the end of it's values and should export
102
+ exportCSV(_createFilename(selectionSetNames), layerLabels);
103
+ layerLabels = [];
104
+ }
105
+ });
93
106
 
94
107
  return Promise.resolve();
95
108
  }
@@ -97,9 +110,7 @@ export async function downloadCSV(
97
110
  /**
98
111
  * Downloads csv of mailing labels for the provided list of ids
99
112
  *
100
- * @param selectionSetNames Names of the selection sets used to provide ids
101
- * @param layer Layer providing features and attributes for download
102
- * @param ids List of ids to download
113
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
103
114
  * @param labelPageDescription Provides PDF page layout info
104
115
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
105
116
  * @param title Title for each page
@@ -107,15 +118,14 @@ export async function downloadCSV(
107
118
  * @returns Promise resolving when function is done
108
119
  */
109
120
  export async function downloadPDF(
110
- selectionSetNames: string[],
111
- layer: __esri.FeatureLayer,
112
- ids: number[],
121
+ exportInfos: IExportInfos,
113
122
  labelPageDescription: ILabel,
114
123
  removeDuplicates = false,
115
124
  title = "",
116
125
  initialImageDataUrl = ""
117
126
  ): Promise<void> {
118
- let labels = await _prepareLabels(layer, ids, removeDuplicates);
127
+ let labels = await consolidateLabels(exportInfos);
128
+ const selectionSetNames = _getSelectionSetNames(exportInfos);
119
129
 
120
130
  labels =
121
131
  // Remove empty lines in labels
@@ -123,6 +133,8 @@ export async function downloadPDF(
123
133
  // Remove empty labels
124
134
  .filter(label => label.length > 0);
125
135
 
136
+ labels = removeDuplicates ? removeDuplicateLabels(labels) : labels;
137
+
126
138
  exportPDF(_createFilename(selectionSetNames), labels, labelPageDescription, title, initialImageDataUrl);
127
139
 
128
140
  return Promise.resolve();
@@ -395,7 +407,6 @@ function _prepareAttributeValue(
395
407
  *
396
408
  * @param layer Layer from which to fetch features
397
409
  * @param ids List of ids to download
398
- * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
399
410
  * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
400
411
  * all attributes are exported
401
412
  * @param includeHeaderNames Add the label format at the front of the list of generated labels
@@ -404,7 +415,6 @@ function _prepareAttributeValue(
404
415
  async function _prepareLabels(
405
416
  layer: __esri.FeatureLayer,
406
417
  ids: number[],
407
- removeDuplicates = true,
408
418
  formatUsingLayerPopup = true,
409
419
  includeHeaderNames = false
410
420
  ): Promise<string[][]> {
@@ -584,15 +594,6 @@ async function _prepareLabels(
584
594
  );
585
595
  }
586
596
 
587
- // Remove duplicates
588
- if (removeDuplicates) {
589
- const labelsAsStrings: string[] = labels.map(label => JSON.stringify(label));
590
- const uniqueLabels = new Set(labelsAsStrings);
591
- labels = Array.from(uniqueLabels,
592
- labelString => JSON.parse(labelString)
593
- );
594
- }
595
-
596
597
  // Add header names
597
598
  if (includeHeaderNames) {
598
599
  let headerNames = [];
@@ -613,4 +614,70 @@ async function _prepareLabels(
613
614
  return Promise.resolve(labels);
614
615
  }
615
616
 
617
+ /**
618
+ * Remove any duplicate labels
619
+ *
620
+ * @param labels Labels to evaluate for duplicates
621
+ * @returns labels with duplicates removed
622
+ */
623
+ export function removeDuplicateLabels(
624
+ labels: string[][]
625
+ ): string[][] {
626
+ const labelsAsStrings: string[] = labels.map(label => JSON.stringify(label));
627
+ const uniqueLabels = new Set(labelsAsStrings);
628
+ return Array.from(uniqueLabels,
629
+ labelString => JSON.parse(labelString)
630
+ );
631
+ }
632
+
633
+ /**
634
+ * Extract selectionSetNames from the provided exportInfos
635
+ *
636
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
637
+ * @returns selectionSetNames that will be used for export filenames
638
+ */
639
+ function _getSelectionSetNames(
640
+ exportInfos: IExportInfos,
641
+ id = /.+/
642
+ ): string[] {
643
+ let selectionSetNames: string[] = [];
644
+ Object.keys(exportInfos).forEach(k => {
645
+ const exportInfo: IExportInfo = exportInfos[k];
646
+ if (id.test(k)) {
647
+ selectionSetNames = selectionSetNames.concat(exportInfo.selectionSetNames);
648
+ }
649
+ });
650
+ return selectionSetNames;
651
+ }
652
+
653
+ /**
654
+ * Create and consolidate labels from all layers
655
+ *
656
+ * @param exportInfos Key details about what to export (ids, layer, and selectionSetNames)
657
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
658
+ * all attributes are exported
659
+ * @param includeHeaderNames Add the label format at the front of the list of generated labels
660
+ * @returns selectionSetNames that will be used for export filenames
661
+ */
662
+ export async function consolidateLabels(
663
+ exportInfos: IExportInfos,
664
+ formatUsingLayerPopup = true,
665
+ includeHeaderNames = false,
666
+ isCSVExport = false
667
+ ): Promise<string[][]> {
668
+ const labelRequests = [];
669
+
670
+ Object.keys(exportInfos).forEach(k => {
671
+ const labelInfo: IExportInfo = exportInfos[k];
672
+ labelRequests.push(_prepareLabels(labelInfo.layerView.layer, labelInfo.ids, formatUsingLayerPopup, includeHeaderNames));
673
+ if (isCSVExport) {
674
+ // add the layer id as a temp value separator that we can use to split values for CSV export
675
+ labelRequests.push(Promise.resolve([[k]]));
676
+ }
677
+ });
678
+
679
+ const labels = await Promise.all(labelRequests);
680
+ return labels.reduce((prev, cur) => prev.concat(cur), []);
681
+ }
682
+
616
683
  //#endregion
@@ -437,10 +437,10 @@ export interface IMapInfo {
437
437
  }
438
438
 
439
439
  export interface IExportInfos {
440
- [key: string]: IExportLayerInfo;
440
+ [key: string]: IExportInfo;
441
441
  }
442
442
 
443
- export interface IExportLayerInfo {
443
+ export interface IExportInfo {
444
444
  ids: number[],
445
445
  layerView: __esri.FeatureLayerView
446
446
  selectionSetNames: string[]
@@ -14,7 +14,7 @@ import { d as defineCustomElement$3 } from './progress.js';
14
14
  import { d as defineCustomElement$2 } from './select.js';
15
15
  import { d as defineCustomElement$1 } from './slider.js';
16
16
 
17
- const bufferToolsCss = ":host{display:block}.c-container{display:inline-flex}.flex-1{flex:\"1\"}.padding-end-1{-webkit-padding-end:1rem;padding-inline-end:1rem}";
17
+ const bufferToolsCss = ":host{display:block}.c-container{display:inline-flex}.flex-1{flex:\"1\"}.padding-end-1{-webkit-padding-end:1rem;padding-inline-end:1rem}.w-50{width:50%}";
18
18
 
19
19
  const BufferTools = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
20
20
  constructor() {
@@ -175,7 +175,7 @@ const BufferTools = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
175
175
  * @protected
176
176
  */
177
177
  _getTextBoxDisplay() {
178
- return (h("div", { class: "c-container" }, h("calcite-input", { class: "padding-end-1", max: this.max && this.max > 0 ? this.max : undefined, min: this.min, "number-button-type": "vertical", onCalciteInputInput: (evt) => this._setDistance(evt), placeholder: "0", type: "number", value: this.distance ? this.distance.toString() : undefined }), h("calcite-select", { class: "flex-1", label: "label", onCalciteSelectChange: () => this._setUnit(this._unitElement.value), ref: (el) => { this._unitElement = el; } }, this._getUnits())));
178
+ return (h("div", { class: "c-container" }, h("calcite-input", { class: "padding-end-1 w-50", max: this.max && this.max > 0 ? this.max : undefined, min: this.min, "number-button-type": "vertical", onCalciteInputInput: (evt) => this._setDistance(evt), placeholder: "0", type: "number", value: this.distance ? this.distance.toString() : undefined }), h("calcite-select", { class: "flex-1 w-50", label: "label", onCalciteSelectChange: () => this._setUnit(this._unitElement.value), ref: (el) => { this._unitElement = el; } }, this._getUnits())));
179
179
  }
180
180
  /**
181
181
  * Render distance control as a slider