@esri/solutions-components 0.3.9 → 0.4.1

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 (79) hide show
  1. package/dist/assets/t9n/public-notification/resources.json +0 -2
  2. package/dist/assets/t9n/public-notification/resources_en.json +0 -2
  3. package/dist/cjs/buffer-tools_6.cjs.entry.js +5 -2
  4. package/dist/cjs/calcite-input-message_5.cjs.entry.js +1342 -18
  5. package/dist/cjs/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-cd8ad61e.js → downloadUtils-27dbd8b9.js} +205 -1337
  6. package/dist/cjs/{index.es-e89ba2a1.js → index.es-40d341ed.js} +3 -13
  7. package/dist/cjs/layer-table.cjs.entry.js +5 -15
  8. package/dist/cjs/loader.cjs.js +1 -1
  9. package/dist/cjs/public-notification.cjs.entry.js +23 -2
  10. package/dist/cjs/solutions-components.cjs.js +1 -1
  11. package/dist/collection/components/layer-table/layer-table.js +4 -16
  12. package/dist/collection/components/map-search/map-search.js +1 -1
  13. package/dist/collection/components/map-select-tools/map-select-tools.js +73 -17
  14. package/dist/collection/components/pdf-download/pdf-download.js +9 -76
  15. package/dist/collection/components/public-notification/public-notification.js +45 -3
  16. package/dist/collection/components/refine-selection/refine-selection.js +1 -1
  17. package/dist/collection/components/refine-selection-tools/refine-selection-tools.js +13 -5
  18. package/dist/collection/demos/buffer-tools.html +1 -1
  19. package/dist/collection/utils/csvUtils.js +4 -0
  20. package/dist/collection/utils/csvUtils.ts +7 -0
  21. package/dist/collection/utils/downloadUtils.js +181 -0
  22. package/dist/collection/utils/downloadUtils.ts +235 -0
  23. package/dist/collection/utils/interfaces.ts +8 -1
  24. package/dist/collection/utils/pdfUtils.js +7 -0
  25. package/dist/collection/utils/pdfUtils.ts +13 -2
  26. package/dist/components/downloadUtils.js +2279 -0
  27. package/dist/components/index.es.js +1 -1
  28. package/dist/components/layer-table.js +5 -16
  29. package/dist/components/map-layer-picker2.js +1 -1
  30. package/dist/components/map-search.js +1 -1
  31. package/dist/components/map-select-tools2.js +69 -19
  32. package/dist/components/pdf-download2.js +10 -2124
  33. package/dist/components/public-notification.js +24 -3
  34. package/dist/components/queryUtils.js +1 -1
  35. package/dist/components/refine-selection-tools2.js +6 -3
  36. package/dist/components/refine-selection2.js +1 -1
  37. package/dist/esm/buffer-tools_6.entry.js +6 -3
  38. package/dist/esm/calcite-combobox_3.entry.js +1 -1
  39. package/dist/esm/calcite-input-message_5.entry.js +1341 -13
  40. package/dist/esm/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-ddd74bd6.js → downloadUtils-76e38a94.js} +206 -1335
  41. package/dist/esm/{index.es-8edafdb2.js → index.es-489f4f08.js} +2 -12
  42. package/dist/esm/layer-table.entry.js +6 -16
  43. package/dist/esm/loader.js +1 -1
  44. package/dist/esm/{mapViewUtils-63e118f8.js → mapViewUtils-02696ab6.js} +1 -1
  45. package/dist/esm/public-notification.entry.js +24 -3
  46. package/dist/esm/solutions-components.js +1 -1
  47. package/dist/solutions-components/demos/buffer-tools.html +1 -1
  48. package/dist/solutions-components/{p-cc2e20c8.js → p-1bfd07e3.js} +1 -1
  49. package/dist/solutions-components/{p-117174e8.entry.js → p-335fce8c.entry.js} +1 -1
  50. package/dist/solutions-components/p-4ef94c6b.entry.js +6 -0
  51. package/dist/solutions-components/p-5d27b47d.entry.js +17 -0
  52. package/dist/solutions-components/p-92cb569a.entry.js +6 -0
  53. package/dist/solutions-components/{p-2a96314a.entry.js → p-a3b60bc9.entry.js} +2 -2
  54. package/dist/solutions-components/{p-98884f44.js → p-bff8aa4e.js} +3 -3
  55. package/dist/solutions-components/p-caa7e7a7.js +437 -0
  56. package/dist/solutions-components/solutions-components.esm.js +1 -1
  57. package/dist/solutions-components/utils/csvUtils.ts +7 -0
  58. package/dist/solutions-components/utils/downloadUtils.ts +235 -0
  59. package/dist/solutions-components/utils/interfaces.ts +8 -1
  60. package/dist/solutions-components/utils/pdfUtils.ts +13 -2
  61. package/dist/types/components/json-editor/assets/monaco-editor/monaco.d.ts +8262 -0
  62. package/dist/types/components/map-select-tools/map-select-tools.d.ts +19 -1
  63. package/dist/types/components/pdf-download/pdf-download.d.ts +0 -18
  64. package/dist/types/components/public-notification/public-notification.d.ts +10 -0
  65. package/dist/types/components/refine-selection-tools/refine-selection-tools.d.ts +2 -2
  66. package/dist/types/components.d.ts +6 -2
  67. package/dist/types/preact.d.ts +2 -1
  68. package/dist/types/utils/downloadUtils.d.ts +40 -0
  69. package/dist/types/utils/interfaces.d.ts +6 -2
  70. package/dist/types/utils/pdfUtils.d.ts +3 -1
  71. package/package.json +1 -1
  72. package/dist/cjs/csvUtils-3a56c6d8.js +0 -54
  73. package/dist/components/csvUtils.js +0 -52
  74. package/dist/esm/csvUtils-23b5418f.js +0 -52
  75. package/dist/solutions-components/p-15f9b0a0.entry.js +0 -6
  76. package/dist/solutions-components/p-238db156.js +0 -416
  77. package/dist/solutions-components/p-3069e3b7.js +0 -21
  78. package/dist/solutions-components/p-6d28f991.entry.js +0 -6
  79. package/dist/solutions-components/p-c5341977.entry.js +0 -6
@@ -19,13 +19,11 @@
19
19
  * limitations under the License.
20
20
  */
21
21
  import "@esri/calcite-components";
22
- import * as pdfUtils from "../../assets/data/labelFormats.json";
22
+ import * as pdfLabelFormats from "../../assets/data/labelFormats.json";
23
+ import * as downloadUtils from "../../utils/downloadUtils";
23
24
  import { loadModules } from "../../utils/loadModules";
24
25
  import { Host, h } from "@stencil/core";
25
- import { exportCSV } from "../../utils/csvUtils";
26
- import { exportPDF } from "../../utils/pdfUtils";
27
26
  import { getLocaleComponentStrings } from "../../utils/locale";
28
- import { queryFeaturesByID } from "../../utils/queryUtils";
29
27
  export class PdfDownload {
30
28
  constructor() {
31
29
  this.disabled = false;
@@ -51,8 +49,8 @@ export class PdfDownload {
51
49
  * @returns Promise resolving when function is done
52
50
  */
53
51
  async downloadCSV(ids, removeDuplicates, addColumnTitle = true) {
54
- const labels = await this._prepareLabels(ids, removeDuplicates, addColumnTitle);
55
- return exportCSV(labels);
52
+ return downloadUtils.downloadCSV(this.layerView.layer, ids, true, // formatUsingLayerPopup
53
+ removeDuplicates, addColumnTitle);
56
54
  }
57
55
  /**
58
56
  * Downloads pdf of mailing labels for the provided list of ids
@@ -62,9 +60,7 @@ export class PdfDownload {
62
60
  * @returns Promise resolving when function is done
63
61
  */
64
62
  async downloadPDF(ids, removeDuplicates) {
65
- const labels = await this._prepareLabels(ids, removeDuplicates);
66
- const labelPageDescription = this._labelInfoElement.selectedOption.value;
67
- return exportPDF(labels, labelPageDescription);
63
+ return downloadUtils.downloadPDF(this.layerView.layer, ids, removeDuplicates, this._labelInfoElement.selectedOption.value);
68
64
  }
69
65
  //--------------------------------------------------------------------------
70
66
  //
@@ -107,31 +103,6 @@ export class PdfDownload {
107
103
  ]);
108
104
  this._intl = intl;
109
105
  }
110
- /**
111
- * Converts the text of a custom popup into a multiline label specification; conversion splits text into
112
- * lines on <br>s, and removes HTML tags. It does not handle Arcade and related records.
113
- *
114
- * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
115
- * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
116
- * @return Label spec
117
- */
118
- _convertPopupToLabelSpec(popupInfo) {
119
- // Replace <br>, <br/> with |
120
- popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
121
- // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
122
- // and split the label back into individual lines
123
- let labelSpec = popupInfo
124
- .replace(/<[\s.]*[^<>]*\/?>/gi, "")
125
- .replace(/\xA0/gi, " ")
126
- .replace(/&lt;/gi, "<")
127
- .replace(/&gt;/gi, ">")
128
- .replace(/&nbsp;/gi, " ")
129
- .split("|");
130
- // Trim lines and remove empties
131
- labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
132
- return labelSpec;
133
- }
134
- ;
135
106
  /**
136
107
  * Gets the formatted pdf export size text
137
108
  *
@@ -154,47 +125,6 @@ export class PdfDownload {
154
125
  const translations = await getLocaleComponentStrings(this.el);
155
126
  this._translations = translations[0];
156
127
  }
157
- /**
158
- * Creates labels from items.
159
- *
160
- * @param ids List of ids to download
161
- * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
162
- * @param includeHeaderNames Add the label format at the front of the list of generated labels
163
- * @returns Promise resolving when function is done
164
- */
165
- async _prepareLabels(ids, removeDuplicates, includeHeaderNames = false) {
166
- // Get the attributes of the features to export
167
- const featureSet = await queryFeaturesByID(ids, this.layerView.layer);
168
- const featuresAttrs = featureSet.features.map(f => f.attributes);
169
- // What data fields are used in the labels?
170
- // Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
171
- const labelFormat = this._convertPopupToLabelSpec(this.layerView.layer.popupTemplate.content[0].text);
172
- // Convert attributes into an array of labels
173
- let labels = featuresAttrs.map(featureAttributes => {
174
- const label = [];
175
- labelFormat.forEach(labelLineTemplate => {
176
- const labelLine = this._intl.substitute(labelLineTemplate, featureAttributes).trim();
177
- if (labelLine.length > 0) {
178
- label.push(labelLine);
179
- }
180
- });
181
- return label;
182
- })
183
- // Remove empty labels
184
- .filter(label => label.length > 0);
185
- // Remove duplicates
186
- if (removeDuplicates) {
187
- const labelsAsStrings = labels.map(label => JSON.stringify(label));
188
- const uniqueLabels = new Set(labelsAsStrings);
189
- labels = Array.from(uniqueLabels, labelString => JSON.parse(labelString));
190
- }
191
- // Add header names
192
- if (includeHeaderNames) {
193
- const headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
194
- labels.unshift(headerNames);
195
- }
196
- return Promise.resolve(labels);
197
- }
198
128
  /**
199
129
  * Renders the pdf export size options
200
130
  *
@@ -203,7 +133,7 @@ export class PdfDownload {
203
133
  * @protected
204
134
  */
205
135
  _renderItems() {
206
- const s = pdfUtils;
136
+ const s = pdfLabelFormats;
207
137
  const sortedPdfIndo = (s.default || s).sort((a, b) => {
208
138
  const _a = parseInt(a.descriptionPDF.labelsPerPageDisplay, 10);
209
139
  const _b = parseInt(b.descriptionPDF.labelsPerPageDisplay, 10);
@@ -338,6 +268,9 @@ export class PdfDownload {
338
268
  "references": {
339
269
  "Promise": {
340
270
  "location": "global"
271
+ },
272
+ "downloadUtils": {
273
+ "location": "global"
341
274
  }
342
275
  },
343
276
  "return": "Promise<void>"
@@ -66,6 +66,25 @@ export class PublicNotification {
66
66
  this._popupsEnabled = v === null || v === void 0 ? void 0 : v.popup.autoOpenEnabled;
67
67
  }
68
68
  }
69
+ /**
70
+ * Called each time the searchConfiguration prop is changed.
71
+ *
72
+ * @returns Promise when complete
73
+ */
74
+ async watchSearchConfigurationHandler(newValue, oldValue) {
75
+ //TODO adding this here to see if its any different than having in map-select-tools
76
+ // I would have thought that the prop would have made it through on the next render of map-select-tools
77
+ // however we are still seeing a broken search...need to understand why and don't see a clean way to debug in devext
78
+ console.log("PN watchSearchConfigurationHandler");
79
+ console.log("PN newValue");
80
+ console.log(newValue);
81
+ console.log("PN oldValue");
82
+ console.log(oldValue);
83
+ if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
84
+ console.log("Emit event from parent");
85
+ this.searchConfigurationChange.emit(newValue);
86
+ }
87
+ }
69
88
  /**
70
89
  * Called each time the selectionSets prop is changed.
71
90
  */
@@ -297,7 +316,7 @@ export class PublicNotification {
297
316
  this._selectionWorkflowType === EWorkflowType.SKETCH ? sketchTip : searchTip;
298
317
  const nameLabelClass = this.customLabelEnabled ? "" : "display-none";
299
318
  return (h("calcite-panel", null, this._getLabel(this._translations.stepTwoFull.replace("{{layer}}", (_a = this.addresseeLayer) === null || _a === void 0 ? void 0 : _a.layer.title)), this._getNotice(noticeText), h("div", { class: "padding-top-sides-1" }, h("map-select-tools", { bufferColor: this.bufferColor, bufferOutlineColor: this.bufferOutlineColor, class: "font-bold", defaultBufferDistance: this.defaultBufferDistance, defaultBufferUnit: this.defaultBufferUnit, enabledLayerIds: this.selectionLayerIds, isUpdate: !!this._activeSelection, mapView: this.mapView, onSelectionSetChange: (evt) => this._updateForSelection(evt), onWorkflowTypeChange: (evt) => this._updateForWorkflowType(evt), ref: (el) => { this._selectTools = el; }, searchConfiguration: this.searchConfiguration, selectLayerView: this.addresseeLayer, selectionSet: this._activeSelection, showBufferTools: this.showSearchSettings })), h("div", { class: "padding-sides-1 padding-bottom-1", style: { "align-items": "end", "display": "flex" } }, h("calcite-icon", { class: "info-blue padding-end-1-2", icon: "feature-layer", scale: "s" }), h("calcite-input-message", { active: true, class: "info-blue", scale: "m" }, this.noResultText && this._numSelected === 0 ? this.noResultText :
300
- this._translations.selectedAddresses.replace("{{n}}", this._numSelected.toString()).replace("{{layer}}", ((_b = this.addresseeLayer) === null || _b === void 0 ? void 0 : _b.layer.title) || ""))), h("div", { class: "padding-sides-1 " + nameLabelClass }, h("calcite-label", { class: "font-bold" }, "Name label", h("calcite-input", { onInput: () => {
319
+ this._translations.selectedAddresses.replace("{{n}}", this._numSelected.toString()).replace("{{layer}}", ((_b = this.addresseeLayer) === null || _b === void 0 ? void 0 : _b.layer.title) || ""))), h("div", { class: "padding-sides-1 " + nameLabelClass }, h("calcite-label", { class: "font-bold" }, "List name", h("calcite-input", { onInput: () => {
301
320
  this.labelChange.emit(this._labelName.value);
302
321
  }, placeholder: "Insert label here...", ref: (el) => { this._labelName = el; }, value: this._customLabel || "" }))), this._getPageNavButtons(this._translations.done, this._numSelected === 0, () => { void this._saveSelection(); }, this._translations.cancel, false, () => { void this._home(); })));
303
322
  }
@@ -339,7 +358,7 @@ export class PublicNotification {
339
358
  */
340
359
  _getDownloadPage(type) {
341
360
  const isPdf = type === EExportType.PDF;
342
- return (h("calcite-panel", null, h("div", null, h("div", { class: "padding-top-sides-1" }, h("calcite-label", { class: "font-bold" }, isPdf ? this._translations.pdfDownloads : this._translations.csvDownloads), h("calcite-label", null, this._translations.notifications)), this._getSelectionLists(), h("div", { class: "margin-side-1 padding-top-1 border-bottom" }), h("div", { class: "padding-top-sides-1" }, h("calcite-label", { layout: "inline" }, h("calcite-checkbox", { disabled: !this._downloadActive, ref: (el) => { this._removeDuplicates = el; } }), this._translations.removeDuplicate)), h("div", { class: isPdf ? "" : "display-none" }, this._getLabel(this._translations.selectPDFLabelOption, false), h("div", { class: "padding-sides-1" }, h("pdf-download", { disabled: !this._downloadActive, layerView: this.addresseeLayer, ref: (el) => { this._downloadTools = el; } }))), h("div", { class: "padding-1 display-flex" }, h("calcite-button", { disabled: !this._downloadActive, onClick: isPdf ? () => this._downloadPDF() : () => this._downloadCSV(), width: "full" }, isPdf ? this._translations.downloadPDF : this._translations.downloadCSV)))));
361
+ return (h("calcite-panel", null, h("div", null, h("div", { class: "padding-top-sides-1" }, h("calcite-label", { class: "font-bold" }, isPdf ? this._translations.downloadPDF : this._translations.downloadCSV), h("calcite-label", null, this._translations.notifications)), this._getSelectionLists(), h("div", { class: "margin-side-1 padding-top-1 border-bottom" }), h("div", { class: "padding-top-sides-1" }, h("calcite-label", { layout: "inline" }, h("calcite-checkbox", { disabled: !this._downloadActive, ref: (el) => { this._removeDuplicates = el; } }), this._translations.removeDuplicate)), h("div", { class: isPdf ? "" : "display-none" }, this._getLabel(this._translations.selectPDFLabelOption, false), h("div", { class: "padding-sides-1" }, h("pdf-download", { disabled: !this._downloadActive, layerView: this.addresseeLayer, ref: (el) => { this._downloadTools = el; } }))), h("div", { class: "padding-1 display-flex" }, h("calcite-button", { disabled: !this._downloadActive, onClick: isPdf ? () => this._downloadPDF() : () => this._downloadCSV(), width: "full" }, isPdf ? this._translations.downloadPDF : this._translations.downloadCSV)))));
343
362
  }
344
363
  /**
345
364
  * Create the stacked navigation buttons for a page
@@ -878,7 +897,7 @@ export class PublicNotification {
878
897
  },
879
898
  "searchConfiguration": {
880
899
  "type": "unknown",
881
- "mutable": false,
900
+ "mutable": true,
882
901
  "complexType": {
883
902
  "original": "ISearchConfiguration",
884
903
  "resolved": "ISearchConfiguration",
@@ -980,6 +999,26 @@ export class PublicNotification {
980
999
  "resolved": "string",
981
1000
  "references": {}
982
1001
  }
1002
+ }, {
1003
+ "method": "searchConfigurationChange",
1004
+ "name": "searchConfigurationChange",
1005
+ "bubbles": true,
1006
+ "cancelable": true,
1007
+ "composed": true,
1008
+ "docs": {
1009
+ "tags": [],
1010
+ "text": "Emitted on demand when searchConfiguration gets a new value"
1011
+ },
1012
+ "complexType": {
1013
+ "original": "ISearchConfiguration",
1014
+ "resolved": "ISearchConfiguration",
1015
+ "references": {
1016
+ "ISearchConfiguration": {
1017
+ "location": "import",
1018
+ "path": "../../utils/interfaces"
1019
+ }
1020
+ }
1021
+ }
983
1022
  }];
984
1023
  }
985
1024
  static get elementRef() { return "el"; }
@@ -987,6 +1026,9 @@ export class PublicNotification {
987
1026
  return [{
988
1027
  "propName": "mapView",
989
1028
  "methodName": "mapViewWatchHandler"
1029
+ }, {
1030
+ "propName": "searchConfiguration",
1031
+ "methodName": "watchSearchConfigurationHandler"
990
1032
  }, {
991
1033
  "propName": "_selectionSets",
992
1034
  "methodName": "selectionSetsWatchHandler"
@@ -118,7 +118,7 @@ export class RefineSelection {
118
118
  const refineSet = this._getRefineSelectionSet(this.selectionSets);
119
119
  const numAdded = (refineSet === null || refineSet === void 0 ? void 0 : refineSet.refineIds.addIds.length) || 0;
120
120
  const numRemoved = (refineSet === null || refineSet === void 0 ? void 0 : refineSet.refineIds.removeIds.length) || 0;
121
- return [(h("calcite-list-item", { label: this._translations.featuresAdded.replace("{{n}}", numAdded.toString()) })), (h("calcite-list-item", { label: this._translations.featuresRemoved.replace("{{n}}", numRemoved.toString()) })), (h("calcite-list-item", { label: this._translations.totalSelected.replace("{{n}}", total.toString()) }))];
121
+ return [(h("calcite-list-item", { label: this._translations.featuresAdded.replace("{{n}}", numAdded.toString()), "non-interactive": true })), (h("calcite-list-item", { label: this._translations.featuresRemoved.replace("{{n}}", numRemoved.toString()), "non-interactive": true })), (h("calcite-list-item", { label: this._translations.totalSelected.replace("{{n}}", total.toString()), "non-interactive": true }))];
122
122
  }
123
123
  /**
124
124
  * Fetch the refine selection set
@@ -238,7 +238,7 @@ export class RefineSelectionTools {
238
238
  return prev;
239
239
  }, []);
240
240
  }
241
- this.refineSelectionGraphicsChange.emit(graphics);
241
+ this.refineSelectionGraphicsChange.emit({ graphics, useOIDs: false });
242
242
  this._clear();
243
243
  });
244
244
  });
@@ -312,7 +312,10 @@ export class RefineSelectionTools {
312
312
  });
313
313
  });
314
314
  if (this.refineMode === ERefineMode.SUBSET) {
315
- this.refineSelectionGraphicsChange.emit(graphics);
315
+ this.refineSelectionGraphicsChange.emit({
316
+ graphics,
317
+ useOIDs: this.layerViews[0].layer.title === this.layerView.layer.title
318
+ });
316
319
  }
317
320
  else {
318
321
  const oids = Array.isArray(graphics) ? graphics.map(g => { var _a; return g.attributes[(_a = g === null || g === void 0 ? void 0 : g.layer) === null || _a === void 0 ? void 0 : _a.objectIdField]; }) : [];
@@ -667,9 +670,14 @@ export class RefineSelectionTools {
667
670
  "text": "Emitted on demand when selection graphics change."
668
671
  },
669
672
  "complexType": {
670
- "original": "any[]",
671
- "resolved": "any[]",
672
- "references": {}
673
+ "original": "IRefineSelectionEvent",
674
+ "resolved": "IRefineSelectionEvent",
675
+ "references": {
676
+ "IRefineSelectionEvent": {
677
+ "location": "import",
678
+ "path": "../../utils/interfaces"
679
+ }
680
+ }
673
681
  }
674
682
  }, {
675
683
  "method": "refineSelectionIdsChange",
@@ -76,6 +76,6 @@
76
76
 
77
77
  <body>
78
78
  <div id="viewDiv"></div>
79
- <buffer-tools id="demo" class="over-map column" distance="10" unit="miles"></map-search>
79
+ <buffer-tools id="demo" class="over-map column" distance="10" unit="miles"></buffer-tools>
80
80
  </body>
81
81
  </html>
@@ -18,6 +18,7 @@
18
18
  * See the License for the specific language governing permissions and
19
19
  * limitations under the License.
20
20
  */
21
+ //#region Public functions
21
22
  /**
22
23
  * Export a csv of the attributes from the features that match the provided ids
23
24
  *
@@ -29,6 +30,8 @@ export function exportCSV(labels) {
29
30
  const outputLines = labels.map(label => Object.values(label).map(v => `"${v}"`).join(",") + "\r\n");
30
31
  _downloadCSVFile(outputLines, `notify-${Date.now().toString()}`);
31
32
  }
33
+ //#endregion
34
+ //#region Private functions
32
35
  /**
33
36
  * Download the CSV file
34
37
  *
@@ -48,3 +51,4 @@ function _downloadCSVFile(outputLines, fileTitle) {
48
51
  document.body.removeChild(link);
49
52
  }
50
53
  }
54
+ //#endregion
@@ -14,6 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ //#region Public functions
18
+
17
19
  /**
18
20
  * Export a csv of the attributes from the features that match the provided ids
19
21
  *
@@ -29,6 +31,9 @@ export function exportCSV(
29
31
  _downloadCSVFile(outputLines, `notify-${Date.now().toString()}`);
30
32
  }
31
33
 
34
+ //#endregion
35
+ //#region Private functions
36
+
32
37
  /**
33
38
  * Download the CSV file
34
39
  *
@@ -51,3 +56,5 @@ function _downloadCSVFile(
51
56
  document.body.removeChild(link);
52
57
  }
53
58
  }
59
+
60
+ //#endregion
@@ -0,0 +1,181 @@
1
+ /*!
2
+ * Copyright 2022 Esri
3
+ * Licensed under the Apache License, Version 2.0
4
+ * http://www.apache.org/licenses/LICENSE-2.0
5
+ */
6
+ /** @license
7
+ * Copyright 2022 Esri
8
+ *
9
+ * Licensed under the Apache License, Version 2.0 (the "License");
10
+ * you may not use this file except in compliance with the License.
11
+ * You may obtain a copy of the License at
12
+ *
13
+ * http://www.apache.org/licenses/LICENSE-2.0
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software
16
+ * distributed under the License is distributed on an "AS IS" BASIS,
17
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ * See the License for the specific language governing permissions and
19
+ * limitations under the License.
20
+ */
21
+ //#region Declarations
22
+ import { exportCSV } from "./csvUtils";
23
+ import { exportPDF } from "./pdfUtils";
24
+ import { loadModules } from "./loadModules";
25
+ import { queryFeaturesByID } from "./queryUtils";
26
+ export { ILabel } from "./pdfUtils";
27
+ //#endregion
28
+ //#region Public functions
29
+ /**
30
+ * Downloads csv of mailing labels for the provided list of ids
31
+ *
32
+ * @param layer Layer providing features and attributes for download
33
+ * @param ids List of ids to download
34
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
35
+ * all attributes are exported
36
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
37
+ * @param addColumnTitle Indicates if column headings should be included in output
38
+ * @returns Promise resolving when function is done
39
+ */
40
+ export async function downloadCSV(layer, ids, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
41
+ const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
42
+ exportCSV(labels);
43
+ return Promise.resolve();
44
+ }
45
+ /**
46
+ * Downloads csv of mailing labels for the provided list of ids
47
+ *
48
+ * @param layer Layer providing features and attributes for download
49
+ * @param ids List of ids to download
50
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
51
+ * @param labelPageDescription Provides PDF page layout info
52
+ * @returns Promise resolving when function is done
53
+ */
54
+ export async function downloadPDF(layer, ids, removeDuplicates, labelPageDescription) {
55
+ const labels = await _prepareLabels(layer, ids, removeDuplicates);
56
+ exportPDF(labels, labelPageDescription);
57
+ return Promise.resolve();
58
+ }
59
+ //#endregion
60
+ //#region Private functions
61
+ /**
62
+ * Converts a set of fieldInfos into template lines.
63
+ *
64
+ * @param fieldInfos Layer's fieldInfos structure
65
+ * @return Label spec
66
+ */
67
+ function _convertPopupFieldsToLabelSpec(fieldInfos) {
68
+ const labelSpec = [];
69
+ // Every visible attribute is used
70
+ fieldInfos.forEach(fieldInfo => {
71
+ if (fieldInfo.visible) {
72
+ labelSpec.push(`{${fieldInfo.fieldName}}`);
73
+ }
74
+ });
75
+ return labelSpec;
76
+ }
77
+ ;
78
+ /**
79
+ * Converts the text of a custom popup into a multiline label specification; conversion splits text into
80
+ * lines on <br>s, and removes HTML tags. It does not handle Arcade and related records.
81
+ *
82
+ * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
83
+ * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
84
+ * @return Label spec
85
+ */
86
+ function _convertPopupTextToLabelSpec(popupInfo) {
87
+ // Replace <br>, <br/> with |
88
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
89
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
90
+ // and split the label back into individual lines
91
+ let labelSpec = popupInfo
92
+ .replace(/<[\s.]*[^<>]*\/?>/gi, "")
93
+ .replace(/\xA0/gi, " ")
94
+ .replace(/&lt;/gi, "<")
95
+ .replace(/&gt;/gi, ">")
96
+ .replace(/&nbsp;/gi, " ")
97
+ .split("|");
98
+ // Trim lines and remove empties
99
+ labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
100
+ return labelSpec;
101
+ }
102
+ ;
103
+ /**
104
+ * Creates labels from items.
105
+ *
106
+ * @param layer Layer from which to fetch features
107
+ * @param ids List of ids to download
108
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
109
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
110
+ * all attributes are exported
111
+ * @param includeHeaderNames Add the label format at the front of the list of generated labels
112
+ * @returns Promise resolving when function is done
113
+ */
114
+ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
115
+ var _a, _b, _c, _d;
116
+ const [intl] = await loadModules([
117
+ "esri/intl"
118
+ ]);
119
+ // Get the attributes of the features to export
120
+ const featureSet = await queryFeaturesByID(ids, layer);
121
+ const featuresAttrs = featureSet.features.map(f => f.attributes);
122
+ // Get the label formatting, if any
123
+ let labelFormat;
124
+ if (layer.popupEnabled) {
125
+ // What data fields are used in the labels?
126
+ // Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
127
+ if (formatUsingLayerPopup && ((_b = (_a = layer.popupTemplate) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.type) === "fields") {
128
+ labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos);
129
+ }
130
+ 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") {
131
+ labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
132
+ }
133
+ }
134
+ // Apply the label format
135
+ let labels;
136
+ // eslint-disable-next-line unicorn/prefer-ternary
137
+ if (labelFormat) {
138
+ // Convert attributes into an array of labels
139
+ labels = featuresAttrs.map(featureAttributes => {
140
+ const label = [];
141
+ labelFormat.forEach(labelLineTemplate => {
142
+ const labelLine = intl.substitute(labelLineTemplate, featureAttributes).trim();
143
+ if (labelLine.length > 0) {
144
+ label.push(labelLine);
145
+ }
146
+ });
147
+ return label;
148
+ })
149
+ // Remove empty labels
150
+ .filter(label => label.length > 0);
151
+ }
152
+ else {
153
+ // Export all attributes
154
+ labels = featuresAttrs.map(featureAttributes => {
155
+ return Object.values(featureAttributes).map(attribute => `${attribute}`);
156
+ });
157
+ }
158
+ // Remove duplicates
159
+ if (removeDuplicates) {
160
+ const labelsAsStrings = labels.map(label => JSON.stringify(label));
161
+ const uniqueLabels = new Set(labelsAsStrings);
162
+ labels = Array.from(uniqueLabels, labelString => JSON.parse(labelString));
163
+ }
164
+ // Add header names
165
+ if (includeHeaderNames) {
166
+ let headerNames = [];
167
+ if (labelFormat) {
168
+ headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
169
+ }
170
+ else {
171
+ Object.keys(featuresAttrs[0]).forEach(k => {
172
+ if (featuresAttrs[0].hasOwnProperty(k)) {
173
+ headerNames.push(k);
174
+ }
175
+ });
176
+ }
177
+ labels.unshift(headerNames);
178
+ }
179
+ return Promise.resolve(labels);
180
+ }
181
+ //#endregion