@esri/solutions-components 0.4.4 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. package/dist/assets/t9n/public-notification/resources.json +5 -5
  2. package/dist/assets/t9n/public-notification/resources_en.json +5 -5
  3. package/dist/cjs/calcite-input-message_5.cjs.entry.js +1 -17
  4. package/dist/cjs/{downloadUtils-ae182e3a.js → downloadUtils-d05b069f.js} +91 -42
  5. package/dist/cjs/{index.es-b71e9071.js → index.es-e47b660c.js} +1 -1
  6. package/dist/cjs/layer-table.cjs.entry.js +1 -1
  7. package/dist/cjs/public-notification.cjs.entry.js +3 -11
  8. package/dist/collection/components/map-select-tools/map-select-tools.js +0 -16
  9. package/dist/collection/components/public-notification/public-notification.js +3 -11
  10. package/dist/collection/utils/downloadUtils.js +90 -41
  11. package/dist/collection/utils/downloadUtils.ts +123 -52
  12. package/dist/components/downloadUtils.js +90 -41
  13. package/dist/components/map-select-tools2.js +0 -16
  14. package/dist/components/public-notification.js +3 -11
  15. package/dist/esm/calcite-input-message_5.entry.js +1 -17
  16. package/dist/esm/{downloadUtils-8d64719e.js → downloadUtils-a9b39194.js} +91 -42
  17. package/dist/esm/{index.es-1d777745.js → index.es-4904465f.js} +1 -1
  18. package/dist/esm/layer-table.entry.js +1 -1
  19. package/dist/esm/public-notification.entry.js +3 -11
  20. package/dist/solutions-components/p-20c73673.entry.js +6 -0
  21. package/dist/solutions-components/{p-702e1915.js → p-25eb36dc.js} +1 -1
  22. package/dist/solutions-components/p-59caff67.entry.js +17 -0
  23. package/dist/solutions-components/{p-048a4c91.entry.js → p-a606221a.entry.js} +1 -1
  24. package/dist/solutions-components/{p-01215468.js → p-e7b3a4d9.js} +3 -3
  25. package/dist/solutions-components/solutions-components.esm.js +1 -1
  26. package/dist/solutions-components/utils/downloadUtils.ts +123 -52
  27. package/dist/types/components/json-editor/assets/monaco-editor/monaco.d.ts +8262 -0
  28. package/package.json +1 -1
  29. package/dist/solutions-components/p-4bfcd9ea.entry.js +0 -17
  30. package/dist/solutions-components/p-a99ca396.entry.js +0 -6
@@ -24,6 +24,7 @@ import { exportPDF } from "./pdfUtils";
24
24
  import { loadModules } from "./loadModules";
25
25
  import { queryFeaturesByID } from "./queryUtils";
26
26
  export { ILabel } from "./pdfUtils";
27
+ const lineSeparatorChar = "|";
27
28
  //#endregion
28
29
  //#region Public functions
29
30
  /**
@@ -56,7 +57,12 @@ export async function downloadCSV(selectionSetNames, layer, ids, formatUsingLaye
56
57
  */
57
58
  export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicates, labelPageDescription) {
58
59
  console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames)); //???
59
- const labels = await _prepareLabels(layer, ids, removeDuplicates);
60
+ let labels = await _prepareLabels(layer, ids, removeDuplicates);
61
+ labels =
62
+ // Remove empty lines in labels
63
+ labels.map(labelLines => labelLines.filter(line => line.length > 0))
64
+ // Remove empty labels
65
+ .filter(label => label.length > 0);
60
66
  exportPDF(labels, labelPageDescription);
61
67
  return Promise.resolve();
62
68
  }
@@ -67,7 +73,7 @@ export async function downloadPDF(selectionSetNames, layer, ids, removeDuplicate
67
73
  *
68
74
  * @param fieldInfos Layer's fieldInfos structure
69
75
  * @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
70
- * @return Label spec
76
+ * @return Label spec with lines separated by `lineSeparatorChar`
71
77
  */
72
78
  function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false) {
73
79
  const labelSpec = [];
@@ -77,7 +83,7 @@ function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false
77
83
  labelSpec.push(`{${fieldInfo.fieldName}}`);
78
84
  }
79
85
  });
80
- return labelSpec;
86
+ return labelSpec.join(lineSeparatorChar);
81
87
  }
82
88
  ;
83
89
  /**
@@ -86,22 +92,19 @@ function _convertPopupFieldsToLabelSpec(fieldInfos, bypassFieldVisiblity = false
86
92
  *
87
93
  * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
88
94
  * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
89
- * @return Label spec
95
+ * @return Label spec with lines separated by `lineSeparatorChar`
90
96
  */
91
97
  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,
98
+ // Replace <br>, <br/> with the line separator character
99
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, lineSeparatorChar);
100
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, and replace some char representations,
95
101
  // and split the label back into individual lines
96
- let labelSpec = popupInfo
102
+ const labelSpec = popupInfo
97
103
  .replace(/<[\s.]*[^<>]*\/?>/gi, "")
98
104
  .replace(/\xA0/gi, " ")
99
105
  .replace(/&lt;/gi, "<")
100
106
  .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);
107
+ .replace(/&nbsp;/gi, " ");
105
108
  return labelSpec;
106
109
  }
107
110
  ;
@@ -121,7 +124,7 @@ async function _createArcadeExecutors(labelFormat, layer) {
121
124
  }
122
125
  // Are there any Arcade expressions in the label format?
123
126
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
124
- const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
127
+ const arcadeExpressionsMatches = labelFormat.match(arcadeExpressionRegExp);
125
128
  if (!arcadeExpressionsMatches) {
126
129
  return Promise.resolve(arcadeExecutors);
127
130
  }
@@ -148,6 +151,39 @@ async function _createArcadeExecutors(labelFormat, layer) {
148
151
  return arcadeExecutors;
149
152
  });
150
153
  }
154
+ /**
155
+ * Prepares an attribute's value by applying domain and type information.
156
+ *
157
+ * @param attributeValue Value of attribute
158
+ * @param attributeType Type of attribute
159
+ * @param attributeDomain Domain info for attribute, if any
160
+ * @return Attribute value modified appropriate to domain and type
161
+ */
162
+ function _prepareAttributeValue(attributeValue, attributeType, attributeDomain, intl) {
163
+ if (attributeDomain && attributeDomain.type === "coded-value") {
164
+ // "coded-value" domain field
165
+ const value = attributeDomain.getName(attributeValue);
166
+ return value;
167
+ }
168
+ else {
169
+ // Non-domain field or unsupported domain type
170
+ let value = attributeValue;
171
+ switch (attributeType) {
172
+ case "date":
173
+ // Format date produces odd characters for the space between the time and the AM/PM text,
174
+ // e.g., "12/31/1969, 4:00 PM"
175
+ value = intl.formatDate(value).replace(/\xe2\x80\xaf/g, "");
176
+ break;
177
+ case "double":
178
+ case "integer":
179
+ case "long":
180
+ case "small-integer":
181
+ value = intl.formatNumber(value);
182
+ break;
183
+ }
184
+ return value;
185
+ }
186
+ }
151
187
  /**
152
188
  * Creates labels from items.
153
189
  *
@@ -164,6 +200,13 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
164
200
  const [intl] = await loadModules(["esri/intl"]);
165
201
  // Get the features to export
166
202
  const featureSet = await queryFeaturesByID(ids, layer);
203
+ // Get field data types. Do we have any domain-based fields?
204
+ const attributeTypes = {};
205
+ const attributeDomains = {};
206
+ layer.fields.forEach(field => {
207
+ attributeTypes[field.name] = field.type;
208
+ attributeDomains[field.name] = field.domain;
209
+ });
167
210
  // Get the label formatting, if any
168
211
  let labelFormat;
169
212
  let arcadeExecutors = {};
@@ -177,7 +220,7 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
177
220
  // Can we use the popup title?
178
221
  // eslint-disable-next-line unicorn/prefer-ternary
179
222
  if (typeof layer.popupTemplate.title === "string") {
180
- labelFormat = [layer.popupTemplate.title];
223
+ labelFormat = layer.popupTemplate.title;
181
224
  // Otherwise revert to using attributes
182
225
  }
183
226
  else {
@@ -196,35 +239,43 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
196
239
  // eslint-disable-next-line unicorn/prefer-ternary
197
240
  if (labelFormat) {
198
241
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
199
- // Convert attributes into an array of labels
242
+ const attributeRegExp = /\{\w+\}/g;
243
+ // Find the label fields that we need to replace with values
244
+ const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
245
+ const attributeMatches = labelFormat.match(attributeRegExp);
246
+ // Convert feature attributes into an array of labels
200
247
  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
- });
248
+ let labelPrep = labelFormat;
249
+ // Replace Arcade expressions
250
+ if (arcadeExpressionMatches) {
251
+ arcadeExpressionMatches.forEach((match) => {
252
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
253
+ const value = arcadeExecutors[expressionName].execute({ "$feature": feature });
254
+ labelPrep = labelPrep.replace(match, value);
255
+ });
256
+ }
257
+ // Replace non-Arcade fields
258
+ if (attributeMatches) {
259
+ attributeMatches.forEach((match) => {
260
+ const attributeName = match.substring(1, match.length - 1);
261
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], intl);
262
+ labelPrep = labelPrep.replace(match, value);
263
+ });
264
+ }
265
+ // Split label into lines
266
+ let label = labelPrep.split(lineSeparatorChar);
267
+ // Trim lines
268
+ label = label.map(line => line.trim());
219
269
  return label;
220
- })
221
- // Remove empty labels
222
- .filter(label => label.length > 0);
270
+ });
223
271
  }
224
272
  else {
225
273
  // Export all attributes
226
274
  labels = featureSet.features.map(feature => {
227
- return Object.values(feature.attributes).map(attribute => `${attribute}`);
275
+ return Object.keys(feature.attributes).map((attributeName) => {
276
+ const value = _prepareAttributeValue(feature.attributes[attributeName], attributeTypes[attributeName], attributeDomains[attributeName], intl);
277
+ return `${value}`;
278
+ });
228
279
  });
229
280
  }
230
281
  // Remove duplicates
@@ -237,14 +288,12 @@ async function _prepareLabels(layer, ids, removeDuplicates = true, formatUsingLa
237
288
  if (includeHeaderNames) {
238
289
  let headerNames = [];
239
290
  if (labelFormat) {
240
- headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
291
+ headerNames = labelFormat.replace(/\{/g, "").replace(/\}/g, "").split(lineSeparatorChar);
241
292
  }
242
293
  else {
243
294
  const featuresAttrs = featureSet.features[0].attributes;
244
295
  Object.keys(featuresAttrs).forEach(k => {
245
- if (featuresAttrs[0].hasOwnProperty(k)) {
246
- headerNames.push(k);
247
- }
296
+ headerNames.push(k);
248
297
  });
249
298
  }
250
299
  labels.unshift(headerNames);
@@ -31,6 +31,16 @@ interface IArcadeExecutorPromises {
31
31
  [expressionName: string]: Promise<__esri.ArcadeExecutor>;
32
32
  }
33
33
 
34
+ interface IAttributeDomains {
35
+ [attributeName: string]: __esri.CodedValueDomain | __esri.RangeDomain | __esri.InheritedDomain | null;
36
+ }
37
+
38
+ interface IAttributeTypes {
39
+ [attributeName: string]: string;
40
+ }
41
+
42
+ const lineSeparatorChar = "|";
43
+
34
44
  //#endregion
35
45
  //#region Public functions
36
46
 
@@ -80,7 +90,13 @@ export async function downloadPDF(
80
90
  labelPageDescription: ILabel
81
91
  ): Promise<void> {
82
92
  console.log("downloadPDF using selectionSetNames " + JSON.stringify(selectionSetNames));//???
83
- const labels = await _prepareLabels(layer, ids, removeDuplicates);
93
+ let labels = await _prepareLabels(layer, ids, removeDuplicates);
94
+
95
+ labels =
96
+ // Remove empty lines in labels
97
+ labels.map(labelLines => labelLines.filter(line => line.length > 0))
98
+ // Remove empty labels
99
+ .filter(label => label.length > 0);
84
100
 
85
101
  exportPDF(labels, labelPageDescription);
86
102
 
@@ -95,12 +111,12 @@ export async function downloadPDF(
95
111
  *
96
112
  * @param fieldInfos Layer's fieldInfos structure
97
113
  * @param bypassFieldVisiblity Indicates if the configured fieldInfo visibility property should be ignored
98
- * @return Label spec
114
+ * @return Label spec with lines separated by `lineSeparatorChar`
99
115
  */
100
116
  function _convertPopupFieldsToLabelSpec(
101
117
  fieldInfos: __esri.FieldInfo[],
102
118
  bypassFieldVisiblity = false
103
- ): string[] {
119
+ ): string {
104
120
  const labelSpec: string[] = [];
105
121
 
106
122
  // Every visible attribute is used
@@ -112,7 +128,7 @@ function _convertPopupFieldsToLabelSpec(
112
128
  }
113
129
  );
114
130
 
115
- return labelSpec;
131
+ return labelSpec.join(lineSeparatorChar);
116
132
  };
117
133
 
118
134
  /**
@@ -121,26 +137,22 @@ function _convertPopupFieldsToLabelSpec(
121
137
  *
122
138
  * @param popupInfo Layer's popupInfo structure containing description, fieldInfos, and expressionInfos, e.g.,
123
139
  * "<div style='text-align: left;'>{NAME}<br />{STREET}<br />{CITY}, {STATE} {ZIP} <br /></div>"
124
- * @return Label spec
140
+ * @return Label spec with lines separated by `lineSeparatorChar`
125
141
  */
126
142
  function _convertPopupTextToLabelSpec(
127
- popupInfo: string
128
- ): string[] {
129
- // Replace <br>, <br/> with |
130
- popupInfo = popupInfo.replace(/<br\s*\/?>/gi, "|");
143
+ popupInfo: string,
144
+ ): string {
145
+ // Replace <br>, <br/> with the line separator character
146
+ popupInfo = popupInfo.replace(/<br\s*\/?>/gi, lineSeparatorChar);
131
147
 
132
- // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, replace some char representations,
148
+ // Remove remaining HTML tags, replace 0xA0 that popup uses for spaces, and replace some char representations,
133
149
  // and split the label back into individual lines
134
- let labelSpec = popupInfo
150
+ const labelSpec = popupInfo
135
151
  .replace(/<[\s.]*[^<>]*\/?>/gi, "")
136
152
  .replace(/\xA0/gi, " ")
137
153
  .replace(/&lt;/gi, "<")
138
154
  .replace(/&gt;/gi, ">")
139
- .replace(/&nbsp;/gi, " ")
140
- .split("|");
141
-
142
- // Trim lines and remove empties
143
- labelSpec = labelSpec.map(line => line.trim()).filter(line => line.length > 0);
155
+ .replace(/&nbsp;/gi, " ");
144
156
 
145
157
  return labelSpec;
146
158
  };
@@ -154,7 +166,7 @@ function _convertPopupTextToLabelSpec(
154
166
  * @return Promise resolving to a set of executors keyed using the expression name
155
167
  */
156
168
  async function _createArcadeExecutors(
157
- labelFormat: string[],
169
+ labelFormat: string,
158
170
  layer: __esri.FeatureLayer
159
171
  ): Promise<IArcadeExecutors> {
160
172
  const arcadeExecutors: IArcadeExecutors = {};
@@ -166,7 +178,7 @@ async function _createArcadeExecutors(
166
178
 
167
179
  // Are there any Arcade expressions in the label format?
168
180
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
169
- const arcadeExpressionsMatches = labelFormat.join("|").match(arcadeExpressionRegExp);
181
+ const arcadeExpressionsMatches = labelFormat.match(arcadeExpressionRegExp);
170
182
  if (!arcadeExpressionsMatches) {
171
183
  return Promise.resolve(arcadeExecutors);
172
184
  }
@@ -206,6 +218,44 @@ async function _createArcadeExecutors(
206
218
  );
207
219
  }
208
220
 
221
+ /**
222
+ * Prepares an attribute's value by applying domain and type information.
223
+ *
224
+ * @param attributeValue Value of attribute
225
+ * @param attributeType Type of attribute
226
+ * @param attributeDomain Domain info for attribute, if any
227
+ * @return Attribute value modified appropriate to domain and type
228
+ */
229
+ function _prepareAttributeValue(
230
+ attributeValue: any,
231
+ attributeType: string,
232
+ attributeDomain: __esri.CodedValueDomain | __esri.RangeDomain | __esri.InheritedDomain | null,
233
+ intl: any
234
+ ): any {
235
+ if (attributeDomain && (attributeDomain as __esri.CodedValueDomain).type === "coded-value") {
236
+ // "coded-value" domain field
237
+ const value = (attributeDomain as __esri.CodedValueDomain).getName(attributeValue);
238
+ return value;
239
+ } else {
240
+ // Non-domain field or unsupported domain type
241
+ let value = attributeValue;
242
+ switch (attributeType) {
243
+ case "date":
244
+ // Format date produces odd characters for the space between the time and the AM/PM text,
245
+ // e.g., "12/31/1969, 4:00 PM"
246
+ value = intl.formatDate(value).replace(/\xe2\x80\xaf/g, "");
247
+ break;
248
+ case "double":
249
+ case "integer":
250
+ case "long":
251
+ case "small-integer":
252
+ value = intl.formatNumber(value);
253
+ break;
254
+ }
255
+ return value;
256
+ }
257
+ }
258
+
209
259
  /**
210
260
  * Creates labels from items.
211
261
  *
@@ -229,8 +279,18 @@ async function _prepareLabels(
229
279
  // Get the features to export
230
280
  const featureSet = await queryFeaturesByID(ids, layer);
231
281
 
282
+ // Get field data types. Do we have any domain-based fields?
283
+ const attributeTypes: IAttributeTypes = {};
284
+ const attributeDomains: IAttributeDomains = {};
285
+ layer.fields.forEach(
286
+ field => {
287
+ attributeTypes[field.name] = field.type;
288
+ attributeDomains[field.name] = field.domain;
289
+ }
290
+ );
291
+
232
292
  // Get the label formatting, if any
233
- let labelFormat: string[];
293
+ let labelFormat: string;
234
294
  let arcadeExecutors: IArcadeExecutors = {};
235
295
  if (layer.popupEnabled) {
236
296
  // What data fields are used in the labels?
@@ -243,7 +303,7 @@ async function _prepareLabels(
243
303
  // Can we use the popup title?
244
304
  // eslint-disable-next-line unicorn/prefer-ternary
245
305
  if (typeof layer.popupTemplate.title === "string") {
246
- labelFormat = [layer.popupTemplate.title];
306
+ labelFormat = layer.popupTemplate.title;
247
307
 
248
308
  // Otherwise revert to using attributes
249
309
  } else {
@@ -264,47 +324,60 @@ async function _prepareLabels(
264
324
  // eslint-disable-next-line unicorn/prefer-ternary
265
325
  if (labelFormat) {
266
326
  const arcadeExpressionRegExp = /\{expression\/\w+\}/g;
327
+ const attributeRegExp = /\{\w+\}/g;
328
+
329
+ // Find the label fields that we need to replace with values
330
+ const arcadeExpressionMatches = labelFormat.match(arcadeExpressionRegExp);
331
+ const attributeMatches = labelFormat.match(attributeRegExp);
267
332
 
268
- // Convert attributes into an array of labels
333
+ // Convert feature attributes into an array of labels
269
334
  labels = featureSet.features.map(
270
335
  feature => {
271
- const label: string[] = [];
272
- labelFormat.forEach(
273
- labelLineTemplate => {
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
- )
336
+ let labelPrep = labelFormat;
337
+
338
+ // Replace Arcade expressions
339
+ if (arcadeExpressionMatches) {
340
+ arcadeExpressionMatches.forEach(
341
+ (match: string) => {
342
+ const expressionName = match.substring(match.indexOf("/") + 1, match.length - 1);
343
+ const value = arcadeExecutors[expressionName].execute({"$feature": feature});
344
+ labelPrep = labelPrep.replace(match, value);
286
345
  }
346
+ )
347
+ }
287
348
 
288
- // Replace fields; must be done after Arcade check because `substitute` will discard Arcade expressions!
289
- labelLine = intl.substitute(labelLine, feature.attributes).trim();
290
-
291
- if (labelLine.length > 0) {
292
- label.push(labelLine);
349
+ // Replace non-Arcade fields
350
+ if (attributeMatches) {
351
+ attributeMatches.forEach(
352
+ (match: string) => {
353
+ const attributeName = match.substring(1, match.length - 1);
354
+ const value = _prepareAttributeValue(feature.attributes[attributeName],
355
+ attributeTypes[attributeName], attributeDomains[attributeName], intl);
356
+ labelPrep = labelPrep.replace(match, value);
293
357
  }
294
- }
295
- )
358
+ )
359
+ }
360
+
361
+ // Split label into lines
362
+ let label = labelPrep.split(lineSeparatorChar);
363
+
364
+ // Trim lines
365
+ label = label.map(line => line.trim());
366
+
296
367
  return label;
297
368
  }
298
- )
299
- // Remove empty labels
300
- .filter(label => label.length > 0);
369
+ );
301
370
 
302
371
  } else {
303
372
  // Export all attributes
304
373
  labels = featureSet.features.map(
305
374
  feature => {
306
- return Object.values(feature.attributes).map(
307
- attribute => `${attribute}`
375
+ return Object.keys(feature.attributes).map(
376
+ (attributeName: string) => {
377
+ const value = _prepareAttributeValue(feature.attributes[attributeName],
378
+ attributeTypes[attributeName], attributeDomains[attributeName], intl);
379
+ return `${value}`;
380
+ }
308
381
  );
309
382
  }
310
383
  );
@@ -324,14 +397,12 @@ async function _prepareLabels(
324
397
  let headerNames = [];
325
398
 
326
399
  if (labelFormat) {
327
- headerNames = labelFormat.map(labelFormatLine => labelFormatLine.replace(/\{/g, "").replace(/\}/g, ""));
400
+ headerNames = labelFormat.replace(/\{/g, "").replace(/\}/g, "").split(lineSeparatorChar);
328
401
 
329
402
  } else {
330
403
  const featuresAttrs = featureSet.features[0].attributes;
331
404
  Object.keys(featuresAttrs).forEach(k => {
332
- if (featuresAttrs[0].hasOwnProperty(k)) {
333
- headerNames.push(k);
334
- }
405
+ headerNames.push(k);
335
406
  });
336
407
  }
337
408