@esri/solutions-components 0.4.0 → 0.4.2

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 (76) hide show
  1. package/dist/assets/t9n/map-select-tools/resources.json +1 -1
  2. package/dist/assets/t9n/map-select-tools/resources_en.json +1 -1
  3. package/dist/assets/t9n/public-notification/resources.json +5 -5
  4. package/dist/assets/t9n/public-notification/resources_en.json +5 -5
  5. package/dist/assets/t9n/refine-selection/resources.json +3 -3
  6. package/dist/assets/t9n/refine-selection/resources_en.json +3 -3
  7. package/dist/cjs/calcite-input-message_5.cjs.entry.js +1344 -18
  8. package/dist/cjs/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-da276a1c.js → downloadUtils-99981c6b.js} +278 -1383
  9. package/dist/cjs/{index.es-176629d8.js → index.es-53f3bc97.js} +3 -13
  10. package/dist/cjs/layer-table.cjs.entry.js +5 -15
  11. package/dist/cjs/loader.cjs.js +1 -1
  12. package/dist/cjs/public-notification.cjs.entry.js +25 -10
  13. package/dist/cjs/solutions-components.cjs.js +1 -1
  14. package/dist/collection/components/layer-table/layer-table.js +4 -16
  15. package/dist/collection/components/map-search/map-search.js +1 -1
  16. package/dist/collection/components/map-select-tools/map-select-tools.js +43 -39
  17. package/dist/collection/components/pdf-download/pdf-download.js +33 -80
  18. package/dist/collection/components/public-notification/public-notification.js +26 -11
  19. package/dist/collection/components/refine-selection/refine-selection.js +1 -1
  20. package/dist/collection/utils/csvUtils.js +4 -0
  21. package/dist/collection/utils/csvUtils.ts +7 -0
  22. package/dist/collection/utils/downloadUtils.js +254 -0
  23. package/dist/collection/utils/downloadUtils.ts +344 -0
  24. package/dist/collection/utils/interfaces.ts +3 -2
  25. package/dist/collection/utils/pdfUtils.js +7 -0
  26. package/dist/collection/utils/pdfUtils.ts +13 -2
  27. package/dist/components/downloadUtils.js +2352 -0
  28. package/dist/components/index.es.js +1 -1
  29. package/dist/components/layer-table.js +5 -16
  30. package/dist/components/map-layer-picker2.js +1 -1
  31. package/dist/components/map-search.js +1 -1
  32. package/dist/components/map-select-tools2.js +45 -41
  33. package/dist/components/pdf-download2.js +14 -2126
  34. package/dist/components/public-notification.js +26 -11
  35. package/dist/components/queryUtils.js +1 -1
  36. package/dist/components/refine-selection-tools2.js +1 -1
  37. package/dist/components/refine-selection2.js +1 -1
  38. package/dist/esm/buffer-tools_6.entry.js +1 -1
  39. package/dist/esm/calcite-combobox_3.entry.js +1 -1
  40. package/dist/esm/calcite-input-message_5.entry.js +1343 -13
  41. package/dist/esm/{calcite-input-message.calcite-notice.map-select-tools.pdf-download.refine-selection-c6f63458.js → downloadUtils-4bb47330.js} +279 -1381
  42. package/dist/esm/{index.es-54a6f3a3.js → index.es-4424d2f7.js} +2 -12
  43. package/dist/esm/layer-table.entry.js +6 -16
  44. package/dist/esm/loader.js +1 -1
  45. package/dist/esm/{mapViewUtils-63e118f8.js → mapViewUtils-02696ab6.js} +1 -1
  46. package/dist/esm/public-notification.entry.js +26 -11
  47. package/dist/esm/solutions-components.js +1 -1
  48. package/dist/solutions-components/p-0aed9b0d.js +437 -0
  49. package/dist/solutions-components/p-0d3b0fa0.entry.js +17 -0
  50. package/dist/solutions-components/{p-cc2e20c8.js → p-1bfd07e3.js} +1 -1
  51. package/dist/solutions-components/{p-117174e8.entry.js → p-335fce8c.entry.js} +1 -1
  52. package/dist/solutions-components/{p-1e459361.js → p-50117f71.js} +3 -3
  53. package/dist/solutions-components/p-5e4dfbe4.entry.js +6 -0
  54. package/dist/solutions-components/{p-8a0c0935.entry.js → p-a3b60bc9.entry.js} +1 -1
  55. package/dist/solutions-components/p-ec7f7804.entry.js +6 -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 +344 -0
  59. package/dist/solutions-components/utils/interfaces.ts +3 -2
  60. package/dist/solutions-components/utils/pdfUtils.ts +13 -2
  61. package/dist/types/components/map-select-tools/map-select-tools.d.ts +12 -6
  62. package/dist/types/components/pdf-download/pdf-download.d.ts +4 -20
  63. package/dist/types/components/public-notification/public-notification.d.ts +9 -1
  64. package/dist/types/components.d.ts +4 -2
  65. package/dist/types/utils/downloadUtils.d.ts +42 -0
  66. package/dist/types/utils/interfaces.d.ts +2 -3
  67. package/dist/types/utils/pdfUtils.d.ts +3 -1
  68. package/package.json +1 -1
  69. package/dist/cjs/csvUtils-3a56c6d8.js +0 -54
  70. package/dist/components/csvUtils.js +0 -52
  71. package/dist/esm/csvUtils-23b5418f.js +0 -52
  72. package/dist/solutions-components/p-3069e3b7.js +0 -21
  73. package/dist/solutions-components/p-6d28f991.entry.js +0 -6
  74. package/dist/solutions-components/p-80f5e33c.js +0 -416
  75. package/dist/solutions-components/p-8927862a.entry.js +0 -6
  76. 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;
@@ -45,26 +43,26 @@ export class PdfDownload {
45
43
  /**
46
44
  * Downloads csv of mailing labels for the provided list of ids
47
45
  *
46
+ * @param selectionSetNames Names of the selection sets used to provide ids
48
47
  * @param ids List of ids to download
49
48
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
50
49
  * @param addColumnTitle Indicates if column headings should be included in output
51
50
  * @returns Promise resolving when function is done
52
51
  */
53
- async downloadCSV(ids, removeDuplicates, addColumnTitle = true) {
54
- const labels = await this._prepareLabels(ids, removeDuplicates, addColumnTitle);
55
- return exportCSV(labels);
52
+ async downloadCSV(selectionSetNames, ids, removeDuplicates, addColumnTitle = true) {
53
+ return downloadUtils.downloadCSV(selectionSetNames, this.layerView.layer, ids, true, // formatUsingLayerPopup
54
+ removeDuplicates, addColumnTitle);
56
55
  }
57
56
  /**
58
57
  * Downloads pdf of mailing labels for the provided list of ids
59
58
  *
59
+ * @param selectionSetNames Names of the selection sets used to provide ids
60
60
  * @param ids List of ids to download
61
61
  * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
62
62
  * @returns Promise resolving when function is done
63
63
  */
64
- async downloadPDF(ids, removeDuplicates) {
65
- const labels = await this._prepareLabels(ids, removeDuplicates);
66
- const labelPageDescription = this._labelInfoElement.selectedOption.value;
67
- return exportPDF(labels, labelPageDescription);
64
+ async downloadPDF(selectionSetNames, ids, removeDuplicates) {
65
+ return downloadUtils.downloadPDF(selectionSetNames, this.layerView.layer, ids, removeDuplicates, this._labelInfoElement.selectedOption.value);
68
66
  }
69
67
  //--------------------------------------------------------------------------
70
68
  //
@@ -107,31 +105,6 @@ export class PdfDownload {
107
105
  ]);
108
106
  this._intl = intl;
109
107
  }
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
108
  /**
136
109
  * Gets the formatted pdf export size text
137
110
  *
@@ -154,47 +127,6 @@ export class PdfDownload {
154
127
  const translations = await getLocaleComponentStrings(this.el);
155
128
  this._translations = translations[0];
156
129
  }
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
130
  /**
199
131
  * Renders the pdf export size options
200
132
  *
@@ -203,7 +135,7 @@ export class PdfDownload {
203
135
  * @protected
204
136
  */
205
137
  _renderItems() {
206
- const s = pdfUtils;
138
+ const s = pdfLabelFormats;
207
139
  const sortedPdfIndo = (s.default || s).sort((a, b) => {
208
140
  const _a = parseInt(a.descriptionPDF.labelsPerPageDisplay, 10);
209
141
  const _b = parseInt(b.descriptionPDF.labelsPerPageDisplay, 10);
@@ -275,8 +207,14 @@ export class PdfDownload {
275
207
  return {
276
208
  "downloadCSV": {
277
209
  "complexType": {
278
- "signature": "(ids: number[], removeDuplicates: boolean, addColumnTitle?: boolean) => Promise<void>",
210
+ "signature": "(selectionSetNames: string[], ids: number[], removeDuplicates: boolean, addColumnTitle?: boolean) => Promise<void>",
279
211
  "parameters": [{
212
+ "tags": [{
213
+ "name": "param",
214
+ "text": "selectionSetNames Names of the selection sets used to provide ids"
215
+ }],
216
+ "text": "Names of the selection sets used to provide ids"
217
+ }, {
280
218
  "tags": [{
281
219
  "name": "param",
282
220
  "text": "ids List of ids to download"
@@ -305,6 +243,9 @@ export class PdfDownload {
305
243
  "docs": {
306
244
  "text": "Downloads csv of mailing labels for the provided list of ids",
307
245
  "tags": [{
246
+ "name": "param",
247
+ "text": "selectionSetNames Names of the selection sets used to provide ids"
248
+ }, {
308
249
  "name": "param",
309
250
  "text": "ids List of ids to download"
310
251
  }, {
@@ -321,8 +262,14 @@ export class PdfDownload {
321
262
  },
322
263
  "downloadPDF": {
323
264
  "complexType": {
324
- "signature": "(ids: number[], removeDuplicates: boolean) => Promise<void>",
265
+ "signature": "(selectionSetNames: string[], ids: number[], removeDuplicates: boolean) => Promise<void>",
325
266
  "parameters": [{
267
+ "tags": [{
268
+ "name": "param",
269
+ "text": "selectionSetNames Names of the selection sets used to provide ids"
270
+ }],
271
+ "text": "Names of the selection sets used to provide ids"
272
+ }, {
326
273
  "tags": [{
327
274
  "name": "param",
328
275
  "text": "ids List of ids to download"
@@ -338,6 +285,9 @@ export class PdfDownload {
338
285
  "references": {
339
286
  "Promise": {
340
287
  "location": "global"
288
+ },
289
+ "downloadUtils": {
290
+ "location": "global"
341
291
  }
342
292
  },
343
293
  "return": "Promise<void>"
@@ -345,6 +295,9 @@ export class PdfDownload {
345
295
  "docs": {
346
296
  "text": "Downloads pdf of mailing labels for the provided list of ids",
347
297
  "tags": [{
298
+ "name": "param",
299
+ "text": "selectionSetNames Names of the selection sets used to provide ids"
300
+ }, {
348
301
  "name": "param",
349
302
  "text": "ids List of ids to download"
350
303
  }, {
@@ -82,7 +82,8 @@ export class PublicNotification {
82
82
  console.log(oldValue);
83
83
  if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
84
84
  console.log("Emit event from parent");
85
- this.searchConfigurationChange.emit(newValue);
85
+ this.searchConfiguration = Object.assign({}, newValue);
86
+ this.searchConfigurationChange.emit(this.searchConfiguration);
86
87
  }
87
88
  }
88
89
  /**
@@ -152,8 +153,7 @@ export class PublicNotification {
152
153
  * Renders the component.
153
154
  */
154
155
  render() {
155
- const hasSelections = this._selectionSets.length > 0;
156
- return (h(Host, null, h("calcite-shell", null, h("calcite-action-bar", { class: "border-bottom-1 action-bar-size", "expand-disabled": true, layout: "horizontal", slot: "header" }, this._getActionGroup("list-check", false, EPageType.LIST, this._translations.myLists), this.showRefineSelection ? this._getActionGroup("test-data", !hasSelections, EPageType.REFINE, this._translations.refineSelection) : undefined, this._getActionGroup("file-pdf", !hasSelections, EPageType.PDF, this._translations.downloadPDF), this._getActionGroup("file-csv", !hasSelections, EPageType.CSV, this._translations.downloadCSV)), this._getPage(this._pageType))));
156
+ return (h(Host, null, h("calcite-shell", null, h("calcite-action-bar", { class: "border-bottom-1 action-bar-size", "expand-disabled": true, layout: "horizontal", slot: "header" }, this._getActionGroup("list-check", EPageType.LIST, this._translations.myLists), this.showRefineSelection ? this._getActionGroup("test-data", EPageType.REFINE, this._translations.refineSelection) : undefined, this._getActionGroup("file-pdf", EPageType.PDF, this._translations.downloadPDF), this._getActionGroup("file-csv", EPageType.CSV, this._translations.downloadCSV)), this._getPage(this._pageType))));
157
157
  }
158
158
  //--------------------------------------------------------------------------
159
159
  //
@@ -183,9 +183,9 @@ export class PublicNotification {
183
183
  *
184
184
  * @protected
185
185
  */
186
- _getActionGroup(icon, disabled, pageType, tip) {
186
+ _getActionGroup(icon, pageType, tip) {
187
187
  const groupClass = this.showRefineSelection ? "action-center w-1-4" : "action-center w-1-3";
188
- return (h("calcite-action-group", { class: groupClass, layout: "horizontal" }, h("calcite-action", { active: this._pageType === pageType, alignment: "center", class: "width-full height-full", compact: false, disabled: disabled, icon: icon, id: icon, onClick: () => { this._setPageType(pageType); }, text: "" }), h("calcite-tooltip", { label: "", placement: "bottom", "reference-element": icon }, h("span", null, tip))));
188
+ return (h("calcite-action-group", { class: groupClass, layout: "horizontal" }, h("calcite-action", { active: this._pageType === pageType, alignment: "center", class: "width-full height-full", compact: false, icon: icon, id: icon, onClick: () => { this._setPageType(pageType); }, text: "" }), h("calcite-tooltip", { label: "", placement: "bottom", "reference-element": icon }, h("span", null, tip))));
189
189
  }
190
190
  /**
191
191
  * Navigate to the defined page type
@@ -299,6 +299,16 @@ export class PublicNotification {
299
299
  this._layerSelectionChangeEvt.detail[0] : "";
300
300
  await this._updateAddresseeLayer(id);
301
301
  }
302
+ /**
303
+ * Check if any selection sets exist.
304
+ *
305
+ * @returns true if selection sets exist
306
+ *
307
+ * @protected
308
+ */
309
+ _hasSelections() {
310
+ return this._selectionSets.length > 0;
311
+ }
302
312
  /**
303
313
  * Create the Select page that shows the selection workflows
304
314
  *
@@ -316,7 +326,7 @@ export class PublicNotification {
316
326
  this._selectionWorkflowType === EWorkflowType.SKETCH ? sketchTip : searchTip;
317
327
  const nameLabelClass = this.customLabelEnabled ? "" : "display-none";
318
328
  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: () => {
329
+ 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
330
  this.labelChange.emit(this._labelName.value);
321
331
  }, 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
332
  }
@@ -327,7 +337,9 @@ export class PublicNotification {
327
337
  * @protected
328
338
  */
329
339
  _getRefinePage() {
330
- return (h("calcite-panel", null, this._getLabel(this._translations.refineSelection), this._getNotice(this._translations.refineTip, "padding-sides-1"), h("refine-selection", { addresseeLayer: this.addresseeLayer, enabledLayerIds: this.selectionLayerIds, mapView: this.mapView, selectionSets: this._selectionSets })));
340
+ const hasSelections = this._hasSelections();
341
+ return (h("calcite-panel", null, this._getLabel(this._translations.refineSelection), hasSelections ? (h("div", null, this._getNotice(this._translations.refineTip, "padding-sides-1"), h("refine-selection", { addresseeLayer: this.addresseeLayer, enabledLayerIds: this.selectionLayerIds, mapView: this.mapView, selectionSets: this._selectionSets }))) :
342
+ this._getNotice(this._translations.refineTipNoSelections, "padding-sides-1")));
331
343
  }
332
344
  /**
333
345
  * Create the PDF download page that shows the download options
@@ -358,7 +370,8 @@ export class PublicNotification {
358
370
  */
359
371
  _getDownloadPage(type) {
360
372
  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)))));
373
+ const hasSelections = this._hasSelections();
374
+ 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)), hasSelections ? (h("div", null, 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)))) : (this._getNotice(this._translations.downloadNoLists, "padding-sides-1 padding-bottom-1")))));
362
375
  }
363
376
  /**
364
377
  * Create the stacked navigation buttons for a page
@@ -441,7 +454,8 @@ export class PublicNotification {
441
454
  */
442
455
  _downloadPDF() {
443
456
  const ids = utils.getSelectionIds(this._getDownloadSelectionSets());
444
- void this._downloadTools.downloadPDF(ids, this._removeDuplicates.checked);
457
+ const selectionSetNames = this._selectionSets.map(set => set.label);
458
+ void this._downloadTools.downloadPDF(selectionSetNames, ids, this._removeDuplicates.checked);
445
459
  }
446
460
  /**
447
461
  * Download all selection sets as CSV
@@ -450,7 +464,8 @@ export class PublicNotification {
450
464
  */
451
465
  _downloadCSV() {
452
466
  const ids = utils.getSelectionIds(this._getDownloadSelectionSets());
453
- void this._downloadTools.downloadCSV(ids, this._removeDuplicates.checked);
467
+ const selectionSetNames = this._selectionSets.map(set => set.label);
468
+ void this._downloadTools.downloadCSV(selectionSetNames, ids, this._removeDuplicates.checked);
454
469
  }
455
470
  /**
456
471
  * Get all enabled selection sets
@@ -897,7 +912,7 @@ export class PublicNotification {
897
912
  },
898
913
  "searchConfiguration": {
899
914
  "type": "unknown",
900
- "mutable": false,
915
+ "mutable": true,
901
916
  "complexType": {
902
917
  "original": "ISearchConfiguration",
903
918
  "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,254 @@
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 selectionSetNames Names of the selection sets used to provide ids
33
+ * @param layer Layer providing features and attributes for download
34
+ * @param ids List of ids to download
35
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
36
+ * all attributes are exported
37
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
38
+ * @param addColumnTitle Indicates if column headings should be included in output
39
+ * @returns Promise resolving when function is done
40
+ */
41
+ export async function downloadCSV(selectionSetNames, layer, ids, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
42
+ console.log("downloadCSV using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
43
+ const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
44
+ exportCSV(labels);
45
+ return Promise.resolve();
46
+ }
47
+ /**
48
+ * Downloads csv of mailing labels for the provided list of ids
49
+ *
50
+ * @param selectionSetNames Names of the selection sets used to provide ids
51
+ * @param layer Layer providing features and attributes for download
52
+ * @param ids List of ids to download
53
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
54
+ * @param labelPageDescription Provides PDF page layout info
55
+ * @returns Promise resolving when function is done
56
+ */
57
+ export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicates, labelPageDescription) {
58
+ console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
59
+ const labels = await _prepareLabels(layer, ids, removeDuplicates);
60
+ exportPDF(labels, labelPageDescription);
61
+ return Promise.resolve();
62
+ }
63
+ //#endregion
64
+ //#region Private functions
65
+ /**
66
+ * Converts a set of fieldInfos into template lines.
67
+ *
68
+ * @param fieldInfos Layer's fieldInfos structure
69
+ * @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
70
+ * @return Label spec
71
+ */
72
+ function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false) {
73
+ const labelSpec = [];
74
+ // Every visible attribute is used
75
+ fieldInfos.forEach(fieldInfo => {
76
+ if (fieldInfo.visible || bypassFieldVisiblity) {
77
+ labelSpec.push(`{${fieldInfo.fieldName}}`);
78
+ }
79
+ });
80
+ return labelSpec;
81
+ }
82
+ ;
83
+ /**
84
+ * Converts the text of a custom popup into a multiline label specification; conversion splits text into
85
+ * lines on <br>s, and removes HTML tags. It does not handle Arcade and related records.
86
+ *
87
+ * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
88
+ * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
89
+ * @return Label spec
90
+ */
91
+ function _convertPopupTextToLabelSpec(popupInfo) {
92
+ // Replace <br>, <br/> with |
93
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
94
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
95
+ // and split the label back into individual lines
96
+ let labelSpec = popupInfo
97
+ .replace(/<[\s.]*[^<>]*\/?>/gi, "")
98
+ .replace(/\xA0/gi, " ")
99
+ .replace(/&lt;/gi, "<")
100
+ .replace(/&gt;/gi, ">")
101
+ .replace(/&nbsp;/gi, " ")
102
+ .split("|");
103
+ // Trim lines and remove empties
104
+ labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
105
+ return labelSpec;
106
+ }
107
+ ;
108
+ /**
109
+ * Extracts Arcade expressions from the lines of a label format and creates an Arcade executor for each
110
+ * referenced expression name.
111
+ *
112
+ * @param labelFormat Label to examine
113
+ * @param layer Layer from which to fetch features
114
+ * @return Promise resolving to a set of executors keyed using the expression name
115
+ */
116
+ async function _createArcadeExecutors(labelFormat, layer) {
117
+ const arcadeExecutors = {};
118
+ // Are any Arcade expressions in the layer?
119
+ if (!Array.isArray(layer.popupTemplate.expressionInfos) || layer.popupTemplate.expressionInfos.length === 0) {
120
+ return Promise.resolve(arcadeExecutors);
121
+ }
122
+ // Are there any Arcade expressions in the label format?
123
+ const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
124
+ const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
125
+ if (!arcadeExpressionsMatches) {
126
+ return Promise.resolve(arcadeExecutors);
127
+ }
128
+ // Generate an Arcade executor for each match
129
+ const [arcade] = await loadModules(["esri/arcade"]);
130
+ const labelingProfile = arcade.createArcadeProfile("popup");
131
+ const createArcadeExecutorPromises = {};
132
+ arcadeExpressionsMatches.forEach((match) => {
133
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
134
+ (layer.popupTemplate.expressionInfos || []).forEach(expressionInfo => {
135
+ if (expressionInfo.name === expressionName) {
136
+ createArcadeExecutorPromises[expressionName] =
137
+ arcade.createArcadeExecutor(expressionInfo.expression, labelingProfile);
138
+ }
139
+ });
140
+ });
141
+ const promises = Object.values(createArcadeExecutorPromises);
142
+ return Promise.all(promises)
143
+ .then(executors => {
144
+ const expressionNames = Object.keys(createArcadeExecutorPromises);
145
+ for (let i = 0; i < expressionNames.length; ++i) {
146
+ arcadeExecutors[expressionNames[i]] = executors[i].valueOf();
147
+ }
148
+ return arcadeExecutors;
149
+ });
150
+ }
151
+ /**
152
+ * Creates labels from items.
153
+ *
154
+ * @param layer Layer from which to fetch features
155
+ * @param ids List of ids to download
156
+ * @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
157
+ * @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
158
+ * all attributes are exported
159
+ * @param includeHeaderNames Add the label format at the front of the list of generated labels
160
+ * @returns Promise resolving when function is done
161
+ */
162
+ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
163
+ var _a, _b, _c, _d;
164
+ const [intl] = await loadModules(["esri/intl"]);
165
+ // Get the features to export
166
+ const featureSet = await queryFeaturesByID(ids, layer);
167
+ // Get the label formatting, if any
168
+ let labelFormat;
169
+ let arcadeExecutors = {};
170
+ if (layer.popupEnabled) {
171
+ // What data fields are used in the labels?
172
+ // Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
173
+ if (formatUsingLayerPopup && ((_b = (_a = layer.popupTemplate) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.type) === "fields") {
174
+ labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos);
175
+ // If popup is configured with "no attribute information", then no fields will visible
176
+ if (labelFormat.length === 0) {
177
+ // Can we use the popup title?
178
+ // eslint-disable-next-line unicorn/prefer-ternary
179
+ if (typeof layer.popupTemplate.title === "string") {
180
+ labelFormat = [layer.popupTemplate.title];
181
+ // Otherwise revert to using attributes
182
+ }
183
+ else {
184
+ labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos, true);
185
+ }
186
+ }
187
+ }
188
+ 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") {
189
+ labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
190
+ // Do we need any Arcade executors?
191
+ arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
192
+ }
193
+ }
194
+ // Apply the label format
195
+ let labels;
196
+ // eslint-disable-next-line unicorn/prefer-ternary
197
+ if (labelFormat) {
198
+ const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
199
+ // Convert attributes into an array of labels
200
+ labels = featureSet.features.map(feature => {
201
+ const label = [];
202
+ labelFormat.forEach(labelLineTemplate => {
203
+ let labelLine = labelLineTemplate;
204
+ // Replace Arcade expressions
205
+ const arcadeExpressionsMatches = labelLine.match(arcadeExpressionRegExp);
206
+ if (arcadeExpressionsMatches) {
207
+ arcadeExpressionsMatches.forEach((match) => {
208
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
209
+ const replacement = arcadeExecutors[expressionName].execute({ "$feature": feature });
210
+ labelLine = labelLine.replace(match, replacement);
211
+ });
212
+ }
213
+ // Replace fields; must be done after Arcade check because `substitute` will discard Arcade expressions!
214
+ labelLine = intl.substitute(labelLine, feature.attributes).trim();
215
+ if (labelLine.length > 0) {
216
+ label.push(labelLine);
217
+ }
218
+ });
219
+ return label;
220
+ })
221
+ // Remove empty labels
222
+ .filter(label => label.length > 0);
223
+ }
224
+ else {
225
+ // Export all attributes
226
+ labels = featureSet.features.map(feature => {
227
+ return Object.values(feature.attributes).map(attribute => `${attribute}`);
228
+ });
229
+ }
230
+ // Remove duplicates
231
+ if (removeDuplicates) {
232
+ const labelsAsStrings = labels.map(label => JSON.stringify(label));
233
+ const uniqueLabels = new Set(labelsAsStrings);
234
+ labels = Array.from(uniqueLabels, labelString => JSON.parse(labelString));
235
+ }
236
+ // Add header names
237
+ if (includeHeaderNames) {
238
+ let headerNames = [];
239
+ if (labelFormat) {
240
+ headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
241
+ }
242
+ else {
243
+ const featuresAttrs = featureSet.features[0].attributes;
244
+ Object.keys(featuresAttrs).forEach(k => {
245
+ if (featuresAttrs[0].hasOwnProperty(k)) {
246
+ headerNames.push(k);
247
+ }
248
+ });
249
+ }
250
+ labels.unshift(headerNames);
251
+ }
252
+ return Promise.resolve(labels);
253
+ }
254
+ //#endregion