@esri/solutions-components 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|