@esri/solutions-components 0.3.9 → 0.4.1

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