@esri/solutions-components 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) 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/calcite-input-message_5.cjs.entry.js +1342 -18
  4. package/dist/cjs/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-da276a1c.js → downloadUtils-27dbd8b9.js} +205 -1383
  5. package/dist/cjs/{index.es-176629d8.js → index.es-40d341ed.js} +3 -13
  6. package/dist/cjs/layer-table.cjs.entry.js +5 -15
  7. package/dist/cjs/loader.cjs.js +1 -1
  8. package/dist/cjs/public-notification.cjs.entry.js +2 -2
  9. package/dist/cjs/solutions-components.cjs.js +1 -1
  10. package/dist/collection/components/layer-table/layer-table.js +4 -16
  11. package/dist/collection/components/map-search/map-search.js +1 -1
  12. package/dist/collection/components/map-select-tools/map-select-tools.js +42 -38
  13. package/dist/collection/components/pdf-download/pdf-download.js +9 -76
  14. package/dist/collection/components/public-notification/public-notification.js +3 -3
  15. package/dist/collection/components/refine-selection/refine-selection.js +1 -1
  16. package/dist/collection/utils/csvUtils.js +4 -0
  17. package/dist/collection/utils/csvUtils.ts +7 -0
  18. package/dist/collection/utils/downloadUtils.js +181 -0
  19. package/dist/collection/utils/downloadUtils.ts +235 -0
  20. package/dist/collection/utils/interfaces.ts +3 -2
  21. package/dist/collection/utils/pdfUtils.js +7 -0
  22. package/dist/collection/utils/pdfUtils.ts +13 -2
  23. package/dist/components/downloadUtils.js +2279 -0
  24. package/dist/components/index.es.js +1 -1
  25. package/dist/components/layer-table.js +5 -16
  26. package/dist/components/map-layer-picker2.js +1 -1
  27. package/dist/components/map-search.js +1 -1
  28. package/dist/components/map-select-tools2.js +44 -40
  29. package/dist/components/pdf-download2.js +10 -2124
  30. package/dist/components/public-notification.js +3 -3
  31. package/dist/components/queryUtils.js +1 -1
  32. package/dist/components/refine-selection-tools2.js +1 -1
  33. package/dist/components/refine-selection2.js +1 -1
  34. package/dist/esm/buffer-tools_6.entry.js +1 -1
  35. package/dist/esm/calcite-combobox_3.entry.js +1 -1
  36. package/dist/esm/calcite-input-message_5.entry.js +1341 -13
  37. package/dist/esm/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-c6f63458.js → downloadUtils-76e38a94.js} +206 -1381
  38. package/dist/esm/{index.es-54a6f3a3.js → index.es-489f4f08.js} +2 -12
  39. package/dist/esm/layer-table.entry.js +6 -16
  40. package/dist/esm/loader.js +1 -1
  41. package/dist/esm/{mapViewUtils-63e118f8.js → mapViewUtils-02696ab6.js} +1 -1
  42. package/dist/esm/public-notification.entry.js +3 -3
  43. package/dist/esm/solutions-components.js +1 -1
  44. package/dist/solutions-components/{p-cc2e20c8.js → p-1bfd07e3.js} +1 -1
  45. package/dist/solutions-components/{p-117174e8.entry.js → p-335fce8c.entry.js} +1 -1
  46. package/dist/solutions-components/p-4ef94c6b.entry.js +6 -0
  47. package/dist/solutions-components/p-5d27b47d.entry.js +17 -0
  48. package/dist/solutions-components/p-92cb569a.entry.js +6 -0
  49. package/dist/solutions-components/{p-8a0c0935.entry.js → p-a3b60bc9.entry.js} +1 -1
  50. package/dist/solutions-components/{p-1e459361.js → p-bff8aa4e.js} +3 -3
  51. package/dist/solutions-components/p-caa7e7a7.js +437 -0
  52. package/dist/solutions-components/solutions-components.esm.js +1 -1
  53. package/dist/solutions-components/utils/csvUtils.ts +7 -0
  54. package/dist/solutions-components/utils/downloadUtils.ts +235 -0
  55. package/dist/solutions-components/utils/interfaces.ts +3 -2
  56. package/dist/solutions-components/utils/pdfUtils.ts +13 -2
  57. package/dist/types/components/map-select-tools/map-select-tools.d.ts +11 -5
  58. package/dist/types/components/pdf-download/pdf-download.d.ts +0 -18
  59. package/dist/types/utils/downloadUtils.d.ts +40 -0
  60. package/dist/types/utils/interfaces.d.ts +2 -3
  61. package/dist/types/utils/pdfUtils.d.ts +3 -1
  62. package/package.json +1 -1
  63. package/dist/cjs/csvUtils-3a56c6d8.js +0 -54
  64. package/dist/components/csvUtils.js +0 -52
  65. package/dist/esm/csvUtils-23b5418f.js +0 -52
  66. package/dist/solutions-components/p-3069e3b7.js +0 -21
  67. package/dist/solutions-components/p-6d28f991.entry.js +0 -6
  68. package/dist/solutions-components/p-80f5e33c.js +0 -416
  69. package/dist/solutions-components/p-8927862a.entry.js +0 -6
  70. package/dist/solutions-components/p-e69c58e5.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>"
@@ -316,7 +316,7 @@ export class PublicNotification {
316
316
  this._selectionWorkflowType === EWorkflowType.SKETCH ? sketchTip : searchTip;
317
317
  const nameLabelClass = this.customLabelEnabled ? "" : "display-none";
318
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 :
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" }, "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: () => {
320
320
  this.labelChange.emit(this._labelName.value);
321
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(); })));
322
322
  }
@@ -358,7 +358,7 @@ export class PublicNotification {
358
358
  */
359
359
  _getDownloadPage(type) {
360
360
  const isPdf = type === EExportType.PDF;
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.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)))));
362
362
  }
363
363
  /**
364
364
  * Create the stacked navigation buttons for a page
@@ -897,7 +897,7 @@ export class PublicNotification {
897
897
  },
898
898
  "searchConfiguration": {
899
899
  "type": "unknown",
900
- "mutable": false,
900
+ "mutable": true,
901
901
  "complexType": {
902
902
  "original": "ISearchConfiguration",
903
903
  "resolved": "ISearchConfiguration",
@@ -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
@@ -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
@@ -0,0 +1,235 @@
1
+ /** @license
2
+ * Copyright 2022 Esri
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ //#region Declarations
18
+
19
+ import { exportCSV } from "./csvUtils";
20
+ import { ILabel, exportPDF } from "./pdfUtils";
21
+ import { loadModules } from "./loadModules";
22
+ import { queryFeaturesByID } from "./queryUtils";
23
+
24
+ export { ILabel } from "./pdfUtils";
25
+
26
+ //#endregion
27
+ //#region Public functions
28
+
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(
41
+ layer: __esri.FeatureLayer,
42
+ ids: number[],
43
+ formatUsingLayerPopup: boolean,
44
+ removeDuplicates = false,
45
+ addColumnTitle = false
46
+ ): Promise<void> {
47
+ const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
48
+
49
+ exportCSV(labels);
50
+
51
+ return Promise.resolve();
52
+ }
53
+
54
+ /**
55
+ * Downloads csv of mailing labels for the provided list of ids
56
+ *
57
+ * @param layer Layer providing features and attributes for download
58
+ * @param ids List of ids to download
59
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
60
+ * @param labelPageDescription Provides PDF page layout info
61
+ * @returns Promise resolving when function is done
62
+ */
63
+ export async function downloadPDF(
64
+ layer: __esri.FeatureLayer,
65
+ ids: number[],
66
+ removeDuplicates: boolean,
67
+ labelPageDescription: ILabel
68
+ ): Promise<void> {
69
+ const labels = await _prepareLabels(layer, ids, removeDuplicates);
70
+
71
+ exportPDF(labels, labelPageDescription);
72
+
73
+ return Promise.resolve();
74
+ }
75
+
76
+ //#endregion
77
+ //#region Private functions
78
+
79
+ /**
80
+ * Converts a set of fieldInfos into template lines.
81
+ *
82
+ * @param fieldInfos Layer's fieldInfos structure
83
+ * @return Label spec
84
+ */
85
+ function _convertPopupFieldsToLabelSpec(
86
+ fieldInfos: __esri.FieldInfo[]
87
+ ): string[] {
88
+ const labelSpec: string[] = [];
89
+
90
+ // Every visible attribute is used
91
+ fieldInfos.forEach(
92
+ fieldInfo => {
93
+ if (fieldInfo.visible) {
94
+ labelSpec.push(`{${fieldInfo.fieldName}}`);
95
+ }
96
+ }
97
+ );
98
+
99
+ return labelSpec;
100
+ };
101
+
102
+ /**
103
+ * Converts the text of a custom popup into a multiline label specification; conversion splits text into
104
+ * lines on <br>s, and removes HTML tags. It does not handle Arcade and related records.
105
+ *
106
+ * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
107
+ * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
108
+ * @return Label spec
109
+ */
110
+ function _convertPopupTextToLabelSpec(
111
+ popupInfo: string
112
+ ): string[] {
113
+ // Replace <br>, <br/> with |
114
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
115
+
116
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
117
+ // and split the label back into individual lines
118
+ let labelSpec = popupInfo
119
+ .replace(/<[\s.]*[^<>]*\/?>/gi, "")
120
+ .replace(/\xA0/gi, " ")
121
+ .replace(/&lt;/gi, "<")
122
+ .replace(/&gt;/gi, ">")
123
+ .replace(/&nbsp;/gi, " ")
124
+ .split("|");
125
+
126
+ // Trim lines and remove empties
127
+ labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
128
+
129
+ return labelSpec;
130
+ };
131
+
132
+ /**
133
+ * Creates labels from items.
134
+ *
135
+ * @param layer Layer from which to fetch features
136
+ * @param ids List of ids to download
137
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
138
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
139
+ * all attributes are exported
140
+ * @param includeHeaderNames Add the label format at the front of the list of generated labels
141
+ * @returns Promise resolving when function is done
142
+ */
143
+ async function _prepareLabels(
144
+ layer: __esri.FeatureLayer,
145
+ ids: number[],
146
+ removeDuplicates = true,
147
+ formatUsingLayerPopup = true,
148
+ includeHeaderNames = false
149
+ ): Promise<string[][]> {
150
+ const [intl] = await loadModules([
151
+ "esri/intl"
152
+ ]);
153
+
154
+ // Get the attributes of the features to export
155
+ const featureSet = await queryFeaturesByID(ids, layer);
156
+ const featuresAttrs = featureSet.features.map(f => f.attributes);
157
+
158
+ // Get the label formatting, if any
159
+ let labelFormat: string[];
160
+ if (layer.popupEnabled) {
161
+ // What data fields are used in the labels?
162
+ // Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
163
+ if (formatUsingLayerPopup && layer.popupTemplate?.content[0]?.type === "fields") {
164
+ labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos);
165
+
166
+ } else if (formatUsingLayerPopup && layer.popupTemplate?.content[0]?.type === "text") {
167
+ labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
168
+
169
+ }
170
+ }
171
+
172
+ // Apply the label format
173
+ let labels: string[][];
174
+ // eslint-disable-next-line unicorn/prefer-ternary
175
+ if (labelFormat) {
176
+ // Convert attributes into an array of labels
177
+ labels = featuresAttrs.map(
178
+ featureAttributes => {
179
+ const label: string[] = [];
180
+ labelFormat.forEach(
181
+ labelLineTemplate => {
182
+ const labelLine = intl.substitute(labelLineTemplate, featureAttributes).trim();
183
+ if (labelLine.length > 0) {
184
+ label.push(labelLine);
185
+ }
186
+ }
187
+ )
188
+ return label;
189
+ }
190
+ )
191
+ // Remove empty labels
192
+ .filter(label => label.length > 0);
193
+
194
+ } else {
195
+ // Export all attributes
196
+ labels = featuresAttrs.map(
197
+ featureAttributes => {
198
+ return Object.values(featureAttributes).map(
199
+ attribute => `${attribute}`
200
+ );
201
+ }
202
+ );
203
+ }
204
+
205
+ // Remove duplicates
206
+ if (removeDuplicates) {
207
+ const labelsAsStrings: string[] = labels.map(label => JSON.stringify(label));
208
+ const uniqueLabels = new Set(labelsAsStrings);
209
+ labels = Array.from(uniqueLabels,
210
+ labelString => JSON.parse(labelString)
211
+ );
212
+ }
213
+
214
+ // Add header names
215
+ if (includeHeaderNames) {
216
+ let headerNames = [];
217
+
218
+ if (labelFormat) {
219
+ headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
220
+
221
+ } else {
222
+ Object.keys(featuresAttrs[0]).forEach(k => {
223
+ if (featuresAttrs[0].hasOwnProperty(k)) {
224
+ headerNames.push(k);
225
+ }
226
+ });
227
+ }
228
+
229
+ labels.unshift(headerNames);
230
+ }
231
+
232
+ return Promise.resolve(labels);
233
+ }
234
+
235
+ //#endregion
@@ -108,7 +108,7 @@ export interface ISearchConfiguration {
108
108
  sources: Array<ILocatorSourceConfigItem | ILayerSourceConfigItem>;
109
109
  }
110
110
 
111
- interface ISearchSourceConfigItem {
111
+ export interface ISearchSourceConfigItem {
112
112
  maxResults: number;
113
113
  maxSuggestions: number;
114
114
  minSuggestCharacters: number;
@@ -387,7 +387,8 @@ export interface ISelectionSet {
387
387
  refineIds: IRefineIds;
388
388
  redoStack?: IRefineOperation[];
389
389
  undoStack?: IRefineOperation[];
390
- skipGeomQuery?: boolean;
390
+ //skipGeomQuery?: boolean;
391
+ skipGeomOIDs?: number[];
391
392
  }
392
393
 
393
394
  export interface IRefineSelectionEvent {