@esri/solutions-components 0.5.9 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
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