@esri/solutions-components 0.4.1 → 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.
- package/dist/assets/t9n/map-select-tools/resources.json +1 -1
- package/dist/assets/t9n/map-select-tools/resources_en.json +1 -1
- package/dist/assets/t9n/public-notification/resources.json +5 -3
- package/dist/assets/t9n/public-notification/resources_en.json +5 -3
- package/dist/assets/t9n/refine-selection/resources.json +3 -3
- package/dist/assets/t9n/refine-selection/resources_en.json +3 -3
- package/dist/cjs/calcite-input-message_5.cjs.entry.js +8 -6
- package/dist/cjs/{downloadUtils-27dbd8b9.js → downloadUtils-99981c6b.js} +88 -15
- package/dist/cjs/{index.es-40d341ed.js → index.es-53f3bc97.js} +1 -1
- package/dist/cjs/layer-table.cjs.entry.js +2 -2
- package/dist/cjs/public-notification.cjs.entry.js +24 -9
- package/dist/collection/components/layer-table/layer-table.js +1 -1
- package/dist/collection/components/map-select-tools/map-select-tools.js +1 -1
- package/dist/collection/components/pdf-download/pdf-download.js +26 -6
- package/dist/collection/components/public-notification/public-notification.js +24 -9
- package/dist/collection/utils/downloadUtils.js +87 -14
- package/dist/collection/utils/downloadUtils.ts +123 -14
- package/dist/components/downloadUtils.js +87 -14
- package/dist/components/layer-table.js +1 -1
- package/dist/components/map-select-tools2.js +1 -1
- package/dist/components/pdf-download2.js +6 -4
- package/dist/components/public-notification.js +24 -9
- package/dist/esm/calcite-input-message_5.entry.js +8 -6
- package/dist/esm/{downloadUtils-76e38a94.js → downloadUtils-4bb47330.js} +88 -15
- package/dist/esm/{index.es-489f4f08.js → index.es-4424d2f7.js} +1 -1
- package/dist/esm/layer-table.entry.js +2 -2
- package/dist/esm/public-notification.entry.js +24 -9
- package/dist/solutions-components/{p-caa7e7a7.js → p-0aed9b0d.js} +11 -11
- package/dist/solutions-components/{p-5d27b47d.entry.js → p-0d3b0fa0.entry.js} +2 -2
- package/dist/solutions-components/{p-bff8aa4e.js → p-50117f71.js} +1 -1
- package/dist/solutions-components/p-5e4dfbe4.entry.js +6 -0
- package/dist/solutions-components/{p-92cb569a.entry.js → p-ec7f7804.entry.js} +1 -1
- package/dist/solutions-components/solutions-components.esm.js +1 -1
- package/dist/solutions-components/utils/downloadUtils.ts +123 -14
- package/dist/types/components/map-select-tools/map-select-tools.d.ts +1 -1
- package/dist/types/components/pdf-download/pdf-download.d.ts +4 -2
- package/dist/types/components/public-notification/public-notification.d.ts +9 -1
- package/dist/types/components.d.ts +4 -2
- package/dist/types/utils/downloadUtils.d.ts +4 -2
- package/package.json +1 -1
- package/dist/solutions-components/p-4ef94c6b.entry.js +0 -6
@@ -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.
|
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
|
-
|
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,
|
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,
|
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
|
*
|
@@ -327,7 +337,9 @@ export class PublicNotification {
|
|
327
337
|
* @protected
|
328
338
|
*/
|
329
339
|
_getRefinePage() {
|
330
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -29,6 +29,7 @@ export { ILabel } from "./pdfUtils";
|
|
29
29
|
/**
|
30
30
|
* Downloads csv of mailing labels for the provided list of ids
|
31
31
|
*
|
32
|
+
* @param selectionSetNames Names of the selection sets used to provide ids
|
32
33
|
* @param layer Layer providing features and attributes for download
|
33
34
|
* @param ids List of ids to download
|
34
35
|
* @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
|
@@ -37,7 +38,8 @@ export { ILabel } from "./pdfUtils";
|
|
37
38
|
* @param addColumnTitle Indicates if column headings should be included in output
|
38
39
|
* @returns Promise resolving when function is done
|
39
40
|
*/
|
40
|
-
export async function downloadCSV(layer, ids, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
|
41
|
+
export async function downloadCSV(selectionSetNames, layer, ids, formatUsingLayerPopup, removeDuplicates = false, addColumnTitle = false) {
|
42
|
+
console.log("downloadCSV using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
|
41
43
|
const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
|
42
44
|
exportCSV(labels);
|
43
45
|
return Promise.resolve();
|
@@ -45,13 +47,15 @@ export async function downloadCSV(layer, ids, formatUsingLayerPopup, removeDupli
|
|
45
47
|
/**
|
46
48
|
* Downloads csv of mailing labels for the provided list of ids
|
47
49
|
*
|
50
|
+
* @param selectionSetNames Names of the selection sets used to provide ids
|
48
51
|
* @param layer Layer providing features and attributes for download
|
49
52
|
* @param ids List of ids to download
|
50
53
|
* @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
|
51
54
|
* @param labelPageDescription Provides PDF page layout info
|
52
55
|
* @returns Promise resolving when function is done
|
53
56
|
*/
|
54
|
-
export async function downloadPDF(layer, ids, removeDuplicates, labelPageDescription) {
|
57
|
+
export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicates, labelPageDescription) {
|
58
|
+
console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
|
55
59
|
const labels = await _prepareLabels(layer, ids, removeDuplicates);
|
56
60
|
exportPDF(labels, labelPageDescription);
|
57
61
|
return Promise.resolve();
|
@@ -62,13 +66,14 @@ export async function downloadPDF(layer, ids, removeDuplicates, labelPageDescrip
|
|
62
66
|
* Converts a set of fieldInfos into template lines.
|
63
67
|
*
|
64
68
|
* @param fieldInfos Layer's fieldInfos structure
|
69
|
+
* @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
|
65
70
|
* @return Label spec
|
66
71
|
*/
|
67
|
-
function _convertPopupFieldsToLabelSpec(fieldInfos) {
|
72
|
+
function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false) {
|
68
73
|
const labelSpec = [];
|
69
74
|
// Every visible attribute is used
|
70
75
|
fieldInfos.forEach(fieldInfo => {
|
71
|
-
if (fieldInfo.visible) {
|
76
|
+
if (fieldInfo.visible || bypassFieldVisiblity) {
|
72
77
|
labelSpec.push(`{${fieldInfo.fieldName}}`);
|
73
78
|
}
|
74
79
|
});
|
@@ -100,6 +105,49 @@ function _convertPopupTextToLabelSpec(popupInfo) {
|
|
100
105
|
return labelSpec;
|
101
106
|
}
|
102
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
|
+
}
|
103
151
|
/**
|
104
152
|
* Creates labels from items.
|
105
153
|
*
|
@@ -113,33 +161,57 @@ function _convertPopupTextToLabelSpec(popupInfo) {
|
|
113
161
|
*/
|
114
162
|
async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLayerPopup = true, includeHeaderNames = false) {
|
115
163
|
var _a, _b, _c, _d;
|
116
|
-
const [intl] = await loadModules([
|
117
|
-
|
118
|
-
]);
|
119
|
-
// Get the attributes of the features to export
|
164
|
+
const [intl] = await loadModules(["esri/intl"]);
|
165
|
+
// Get the features to export
|
120
166
|
const featureSet = await queryFeaturesByID(ids, layer);
|
121
|
-
const featuresAttrs = featureSet.features.map(f => f.attributes);
|
122
167
|
// Get the label formatting, if any
|
123
168
|
let labelFormat;
|
169
|
+
let arcadeExecutors = {};
|
124
170
|
if (layer.popupEnabled) {
|
125
171
|
// What data fields are used in the labels?
|
126
172
|
// Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
|
127
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") {
|
128
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
|
+
}
|
129
187
|
}
|
130
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") {
|
131
189
|
labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
|
190
|
+
// Do we need any Arcade executors?
|
191
|
+
arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
|
132
192
|
}
|
133
193
|
}
|
134
194
|
// Apply the label format
|
135
195
|
let labels;
|
136
196
|
// eslint-disable-next-line unicorn/prefer-ternary
|
137
197
|
if (labelFormat) {
|
198
|
+
const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
|
138
199
|
// Convert attributes into an array of labels
|
139
|
-
labels =
|
200
|
+
labels = featureSet.features.map(feature => {
|
140
201
|
const label = [];
|
141
202
|
labelFormat.forEach(labelLineTemplate => {
|
142
|
-
|
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();
|
143
215
|
if (labelLine.length > 0) {
|
144
216
|
label.push(labelLine);
|
145
217
|
}
|
@@ -151,8 +223,8 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
|
|
151
223
|
}
|
152
224
|
else {
|
153
225
|
// Export all attributes
|
154
|
-
labels =
|
155
|
-
return Object.values(
|
226
|
+
labels = featureSet.features.map(feature => {
|
227
|
+
return Object.values(feature.attributes).map(attribute => `${attribute}`);
|
156
228
|
});
|
157
229
|
}
|
158
230
|
// Remove duplicates
|
@@ -168,7 +240,8 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
|
|
168
240
|
headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
|
169
241
|
}
|
170
242
|
else {
|
171
|
-
|
243
|
+
const featuresAttrs = featureSet.features[0].attributes;
|
244
|
+
Object.keys(featuresAttrs).forEach(k => {
|
172
245
|
if (featuresAttrs[0].hasOwnProperty(k)) {
|
173
246
|
headerNames.push(k);
|
174
247
|
}
|
@@ -23,12 +23,21 @@ import { queryFeaturesByID } from "./queryUtils";
|
|
23
23
|
|
24
24
|
export { ILabel } from "./pdfUtils";
|
25
25
|
|
26
|
+
interface IArcadeExecutors {
|
27
|
+
[expressionName: string]: __esri.ArcadeExecutor;
|
28
|
+
}
|
29
|
+
|
30
|
+
interface IArcadeExecutorPromises {
|
31
|
+
[expressionName: string]: Promise<__esri.ArcadeExecutor>;
|
32
|
+
}
|
33
|
+
|
26
34
|
//#endregion
|
27
35
|
//#region Public functions
|
28
36
|
|
29
37
|
/**
|
30
38
|
* Downloads csv of mailing labels for the provided list of ids
|
31
39
|
*
|
40
|
+
* @param selectionSetNames Names of the selection sets used to provide ids
|
32
41
|
* @param layer Layer providing features and attributes for download
|
33
42
|
* @param ids List of ids to download
|
34
43
|
* @param formatUsingLayerPopup When true, the layer's popup is used to choose attributes for each column; when false,
|
@@ -38,12 +47,14 @@ export { ILabel } from "./pdfUtils";
|
|
38
47
|
* @returns Promise resolving when function is done
|
39
48
|
*/
|
40
49
|
export async function downloadCSV(
|
50
|
+
selectionSetNames: string[],
|
41
51
|
layer: __esri.FeatureLayer,
|
42
52
|
ids: number[],
|
43
53
|
formatUsingLayerPopup: boolean,
|
44
54
|
removeDuplicates = false,
|
45
55
|
addColumnTitle = false
|
46
56
|
): Promise<void> {
|
57
|
+
console.log("downloadCSV using selectionSetNames " + JSON.stringify(selectionSetNames));//???
|
47
58
|
const labels = await _prepareLabels(layer, ids, removeDuplicates, formatUsingLayerPopup, addColumnTitle);
|
48
59
|
|
49
60
|
exportCSV(labels);
|
@@ -54,6 +65,7 @@ export async function downloadCSV(
|
|
54
65
|
/**
|
55
66
|
* Downloads csv of mailing labels for the provided list of ids
|
56
67
|
*
|
68
|
+
* @param selectionSetNames Names of the selection sets used to provide ids
|
57
69
|
* @param layer Layer providing features and attributes for download
|
58
70
|
* @param ids List of ids to download
|
59
71
|
* @param removeDuplicates When true a single label is generated when multiple featues have a shared address value
|
@@ -61,11 +73,13 @@ export async function downloadCSV(
|
|
61
73
|
* @returns Promise resolving when function is done
|
62
74
|
*/
|
63
75
|
export async function downloadPDF(
|
76
|
+
selectionSetNames: string[],
|
64
77
|
layer: __esri.FeatureLayer,
|
65
78
|
ids: number[],
|
66
79
|
removeDuplicates: boolean,
|
67
80
|
labelPageDescription: ILabel
|
68
81
|
): Promise<void> {
|
82
|
+
console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames));//???
|
69
83
|
const labels = await _prepareLabels(layer, ids, removeDuplicates);
|
70
84
|
|
71
85
|
exportPDF(labels, labelPageDescription);
|
@@ -80,17 +94,19 @@ export async function downloadPDF(
|
|
80
94
|
* Converts a set of fieldInfos into template lines.
|
81
95
|
*
|
82
96
|
* @param fieldInfos Layer's fieldInfos structure
|
97
|
+
* @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
|
83
98
|
* @return Label spec
|
84
99
|
*/
|
85
100
|
function _convertPopupFieldsToLabelSpec(
|
86
|
-
fieldInfos: __esri.FieldInfo[]
|
101
|
+
fieldInfos: __esri.FieldInfo[],
|
102
|
+
bypassFieldVisiblity = false
|
87
103
|
): string[] {
|
88
104
|
const labelSpec: string[] = [];
|
89
105
|
|
90
106
|
// Every visible attribute is used
|
91
107
|
fieldInfos.forEach(
|
92
108
|
fieldInfo => {
|
93
|
-
if (fieldInfo.visible) {
|
109
|
+
if (fieldInfo.visible || bypassFieldVisiblity) {
|
94
110
|
labelSpec.push(`{${fieldInfo.fieldName}}`);
|
95
111
|
}
|
96
112
|
}
|
@@ -129,6 +145,67 @@ function _convertPopupTextToLabelSpec(
|
|
129
145
|
return labelSpec;
|
130
146
|
};
|
131
147
|
|
148
|
+
/**
|
149
|
+
* Extracts Arcade expressions from the lines of a label format and creates an Arcade executor for each
|
150
|
+
* referenced expression name.
|
151
|
+
*
|
152
|
+
* @param labelFormat Label to examine
|
153
|
+
* @param layer Layer from which to fetch features
|
154
|
+
* @return Promise resolving to a set of executors keyed using the expression name
|
155
|
+
*/
|
156
|
+
async function _createArcadeExecutors(
|
157
|
+
labelFormat: string[],
|
158
|
+
layer: __esri.FeatureLayer
|
159
|
+
): Promise<IArcadeExecutors> {
|
160
|
+
const arcadeExecutors: IArcadeExecutors = {};
|
161
|
+
|
162
|
+
// Are any Arcade expressions in the layer?
|
163
|
+
if (!Array.isArray(layer.popupTemplate.expressionInfos) || layer.popupTemplate.expressionInfos.length === 0) {
|
164
|
+
return Promise.resolve(arcadeExecutors);
|
165
|
+
}
|
166
|
+
|
167
|
+
// Are there any Arcade expressions in the label format?
|
168
|
+
const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
|
169
|
+
const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
|
170
|
+
if (!arcadeExpressionsMatches) {
|
171
|
+
return Promise.resolve(arcadeExecutors);
|
172
|
+
}
|
173
|
+
|
174
|
+
// Generate an Arcade executor for each match
|
175
|
+
const [arcade] = await loadModules(["esri/arcade"]);
|
176
|
+
const labelingProfile: __esri.Profile = arcade.createArcadeProfile("popup");
|
177
|
+
|
178
|
+
const createArcadeExecutorPromises: IArcadeExecutorPromises = {};
|
179
|
+
arcadeExpressionsMatches.forEach(
|
180
|
+
(match: string) => {
|
181
|
+
const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
|
182
|
+
|
183
|
+
(layer.popupTemplate.expressionInfos || []).forEach(
|
184
|
+
expressionInfo => {
|
185
|
+
if (expressionInfo.name === expressionName) {
|
186
|
+
createArcadeExecutorPromises[expressionName] =
|
187
|
+
arcade.createArcadeExecutor(expressionInfo.expression, labelingProfile);
|
188
|
+
}
|
189
|
+
}
|
190
|
+
);
|
191
|
+
}
|
192
|
+
);
|
193
|
+
|
194
|
+
const promises = Object.values(createArcadeExecutorPromises);
|
195
|
+
return Promise.all(promises)
|
196
|
+
.then(
|
197
|
+
executors => {
|
198
|
+
const expressionNames = Object.keys(createArcadeExecutorPromises);
|
199
|
+
|
200
|
+
for (let i = 0; i < expressionNames.length; ++i) {
|
201
|
+
arcadeExecutors[expressionNames[i]] = executors[i].valueOf() as __esri.ArcadeExecutor;
|
202
|
+
}
|
203
|
+
|
204
|
+
return arcadeExecutors;
|
205
|
+
}
|
206
|
+
);
|
207
|
+
}
|
208
|
+
|
132
209
|
/**
|
133
210
|
* Creates labels from items.
|
134
211
|
*
|
@@ -147,25 +224,38 @@ async function _prepareLabels(
|
|
147
224
|
formatUsingLayerPopup = true,
|
148
225
|
includeHeaderNames = false
|
149
226
|
): Promise<string[][]> {
|
150
|
-
const [intl] = await loadModules([
|
151
|
-
"esri/intl"
|
152
|
-
]);
|
227
|
+
const [intl] = await loadModules(["esri/intl"]);
|
153
228
|
|
154
|
-
// Get the
|
229
|
+
// Get the features to export
|
155
230
|
const featureSet = await queryFeaturesByID(ids, layer);
|
156
|
-
const featuresAttrs = featureSet.features.map(f => f.attributes);
|
157
231
|
|
158
232
|
// Get the label formatting, if any
|
159
233
|
let labelFormat: string[];
|
234
|
+
let arcadeExecutors: IArcadeExecutors = {};
|
160
235
|
if (layer.popupEnabled) {
|
161
236
|
// What data fields are used in the labels?
|
162
237
|
// Example labelFormat: ['{NAME}', '{STREET}', '{CITY}, {STATE} {ZIP}']
|
163
238
|
if (formatUsingLayerPopup && layer.popupTemplate?.content[0]?.type === "fields") {
|
164
239
|
labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos);
|
165
240
|
|
241
|
+
// If popup is configured with "no attribute information", then no fields will visible
|
242
|
+
if (labelFormat.length === 0) {
|
243
|
+
// Can we use the popup title?
|
244
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
245
|
+
if (typeof layer.popupTemplate.title === "string") {
|
246
|
+
labelFormat = [layer.popupTemplate.title];
|
247
|
+
|
248
|
+
// Otherwise revert to using attributes
|
249
|
+
} else {
|
250
|
+
labelFormat = _convertPopupFieldsToLabelSpec(layer.popupTemplate.fieldInfos, true);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
166
254
|
} else if (formatUsingLayerPopup && layer.popupTemplate?.content[0]?.type === "text") {
|
167
255
|
labelFormat = _convertPopupTextToLabelSpec(layer.popupTemplate.content[0].text);
|
168
256
|
|
257
|
+
// Do we need any Arcade executors?
|
258
|
+
arcadeExecutors = await _createArcadeExecutors(labelFormat, layer);
|
169
259
|
}
|
170
260
|
}
|
171
261
|
|
@@ -173,13 +263,31 @@ async function _prepareLabels(
|
|
173
263
|
let labels: string[][];
|
174
264
|
// eslint-disable-next-line unicorn/prefer-ternary
|
175
265
|
if (labelFormat) {
|
266
|
+
const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
|
267
|
+
|
176
268
|
// Convert attributes into an array of labels
|
177
|
-
labels =
|
178
|
-
|
269
|
+
labels = featureSet.features.map(
|
270
|
+
feature => {
|
179
271
|
const label: string[] = [];
|
180
272
|
labelFormat.forEach(
|
181
273
|
labelLineTemplate => {
|
182
|
-
|
274
|
+
let labelLine = labelLineTemplate;
|
275
|
+
|
276
|
+
// Replace Arcade expressions
|
277
|
+
const arcadeExpressionsMatches = labelLine.match(arcadeExpressionRegExp);
|
278
|
+
if (arcadeExpressionsMatches) {
|
279
|
+
arcadeExpressionsMatches.forEach(
|
280
|
+
(match: string) => {
|
281
|
+
const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
|
282
|
+
const replacement = arcadeExecutors[expressionName].execute({"$feature": feature});
|
283
|
+
labelLine = labelLine.replace(match, replacement);
|
284
|
+
}
|
285
|
+
)
|
286
|
+
}
|
287
|
+
|
288
|
+
// Replace fields; must be done after Arcade check because `substitute` will discard Arcade expressions!
|
289
|
+
labelLine = intl.substitute(labelLine, feature.attributes).trim();
|
290
|
+
|
183
291
|
if (labelLine.length > 0) {
|
184
292
|
label.push(labelLine);
|
185
293
|
}
|
@@ -193,9 +301,9 @@ async function _prepareLabels(
|
|
193
301
|
|
194
302
|
} else {
|
195
303
|
// Export all attributes
|
196
|
-
labels =
|
197
|
-
|
198
|
-
return Object.values(
|
304
|
+
labels = featureSet.features.map(
|
305
|
+
feature => {
|
306
|
+
return Object.values(feature.attributes).map(
|
199
307
|
attribute => `${attribute}`
|
200
308
|
);
|
201
309
|
}
|
@@ -219,7 +327,8 @@ async function _prepareLabels(
|
|
219
327
|
headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
|
220
328
|
|
221
329
|
} else {
|
222
|
-
|
330
|
+
const featuresAttrs = featureSet.features[0].attributes;
|
331
|
+
Object.keys(featuresAttrs).forEach(k => {
|
223
332
|
if (featuresAttrs[0].hasOwnProperty(k)) {
|
224
333
|
headerNames.push(k);
|
225
334
|
}
|