@esri/solutions-components 0.7.31 → 0.7.32

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.
@@ -18,6 +18,33 @@
18
18
  display: block;
19
19
  }
20
20
 
21
+ /* Override to hide Editor settings panel collapsible */
21
22
  .esri-editor__panel-toolbar {
22
23
  display: none !important;
23
24
  }
25
+
26
+ /* Override to hide the Select section from Editor template */
27
+ .esri-editor__update-actions {
28
+ display: none !important;
29
+ }
30
+
31
+ /* Override to reduce the padding of panel content*/
32
+ .esri-editor__panel-content {
33
+ padding-block: 0px !important;
34
+ }
35
+
36
+ /* Override to hide header */
37
+ .esri-editor .esri-item-list__group__header {
38
+ display: none !important;
39
+ }
40
+
41
+ /* Override to hide the create feature heading title */
42
+ .esri-editor__panel-content__section .esri-widget__heading {
43
+ display: none !important;
44
+ }
45
+
46
+ /* Override to reduce padding for the template filter*/
47
+ .esri-editor .esri-item-list__filter-container--sticky {
48
+ padding-block: 0px !important;
49
+ padding-inline: 10px !important;
50
+ }
@@ -20,11 +20,12 @@
20
20
  */
21
21
  import { Host, h } from "@stencil/core";
22
22
  import { loadModules } from "../../utils/loadModules";
23
- import { getLayerOrTable } from "../../utils/mapViewUtils";
23
+ import { getAllLayers } from "../../utils/mapViewUtils";
24
24
  export class CreateFeature {
25
25
  constructor() {
26
26
  this.mapView = undefined;
27
27
  this.selectedLayerId = undefined;
28
+ this.customizeSubmit = false;
28
29
  }
29
30
  //--------------------------------------------------------------------------
30
31
  //
@@ -95,18 +96,8 @@ export class CreateFeature {
95
96
  * Init Editor widget and starts the create workflow
96
97
  */
97
98
  async init() {
98
- if (this.mapView) {
99
- if (this.mapView && this.selectedLayerId) {
100
- await getLayerOrTable(this.mapView, this.selectedLayerId);
101
- await this.createEditorWidget();
102
- await this.startCreate();
103
- this._editor.viewModel.featureFormViewModel.on('submit', this.submitted.bind(this));
104
- setTimeout(() => {
105
- var _a, _b;
106
- this.el.querySelector('.esri-editor').querySelectorAll('calcite-flow-item')[1].shadowRoot.querySelector('calcite-panel').shadowRoot.querySelector('article').querySelector('header').setAttribute('style', 'display: none');
107
- (_b = (_a = this.el.querySelector('.esri-editor').querySelectorAll('calcite-flow-item')[1].shadowRoot.querySelector('calcite-panel').shadowRoot.querySelector('article')) === null || _a === void 0 ? void 0 : _a.querySelector('footer')) === null || _b === void 0 ? void 0 : _b.setAttribute('style', 'display: none');
108
- }, 700);
109
- }
99
+ if (this.mapView && this.selectedLayerId) {
100
+ await this.createEditorWidget();
110
101
  }
111
102
  }
112
103
  /**
@@ -115,10 +106,12 @@ export class CreateFeature {
115
106
  * @protected
116
107
  */
117
108
  async initModules() {
118
- const [Editor] = await loadModules([
119
- "esri/widgets/Editor"
109
+ const [Editor, reactiveUtils] = await loadModules([
110
+ "esri/widgets/Editor",
111
+ "esri/core/reactiveUtils"
120
112
  ]);
121
113
  this.Editor = Editor;
114
+ this.reactiveUtils = reactiveUtils;
122
115
  }
123
116
  /**
124
117
  * Display editor widget to create the new feature
@@ -128,51 +121,105 @@ export class CreateFeature {
128
121
  if (this._editor) {
129
122
  this._editor.destroy();
130
123
  }
124
+ const layerInfos = [];
131
125
  const container = document.createElement("div");
132
- const layer = await getLayerOrTable(this.mapView, this.selectedLayerId);
133
- const selectedLayer = {
134
- layer: layer
135
- };
126
+ const allMapLayers = await getAllLayers(this.mapView);
127
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
128
+ allMapLayers.forEach(async (eachLayer) => {
129
+ layerInfos.push({
130
+ layer: eachLayer,
131
+ enabled: (eachLayer === null || eachLayer === void 0 ? void 0 : eachLayer.type) === "feature" && (eachLayer === null || eachLayer === void 0 ? void 0 : eachLayer.id) === this.selectedLayerId,
132
+ addEnabled: true,
133
+ updateEnabled: false,
134
+ deleteEnabled: false // default is true, set to false to disable the ability to delete features
135
+ });
136
+ });
136
137
  this._editor = new this.Editor({
137
138
  allowedWorkflows: "create-features",
138
139
  view: this.mapView,
139
- layerInfos: [selectedLayer],
140
+ layerInfos: layerInfos,
140
141
  visibleElements: {
141
- snappingControls: false,
142
- snappingControlsElements: {
143
- featureEnabledToggle: false,
144
- layerList: false,
145
- enabledToggle: false
146
- }
142
+ snappingControls: false
147
143
  },
148
- container,
144
+ container
149
145
  });
150
146
  this.el.appendChild(container);
147
+ //Add handle to watch if attachments are added/edited
148
+ const attachmentHandle = this.reactiveUtils.watch(() => this._editor.viewModel.state, (state) => {
149
+ if (state === 'adding-attachment' || state === 'editing-attachment') {
150
+ this._addingAttachment = true;
151
+ this.editingAttachment.emit(true);
152
+ }
153
+ else {
154
+ if (this._addingAttachment) {
155
+ this.editingAttachment.emit(false);
156
+ this._addingAttachment = false;
157
+ }
158
+ }
159
+ });
160
+ this._editor.viewModel.addHandles(attachmentHandle);
161
+ //Add handle to watch featureTemplatesViewModel ready state and then start the creation
162
+ const handle = this.reactiveUtils.watch(() => this._editor.viewModel.featureTemplatesViewModel.state, (state) => {
163
+ if (state === 'ready') {
164
+ void this.startCreate();
165
+ }
166
+ });
167
+ this._editor.viewModel.addHandles(handle);
151
168
  }
152
169
  /**
153
170
  * Start creating the feature
154
171
  * @protected
155
172
  */
156
173
  async startCreate() {
157
- var _a, _b;
158
- const layer = await getLayerOrTable(this.mapView, this.selectedLayerId);
159
- if (layer) {
160
- let template = layer.templates && layer.templates.length ? layer.templates[0] : {};
161
- if (((_a = layer.sourceJSON) === null || _a === void 0 ? void 0 : _a.types.length) && ((_b = layer.sourceJSON.types[0].templates) === null || _b === void 0 ? void 0 : _b.length)) {
162
- template = layer.sourceJSON.types[0].templates[0];
163
- }
164
- const creationInfo = {
165
- layer: layer,
166
- template: template
167
- };
168
- await this._editor.startCreateFeaturesWorkflowAtFeatureCreation(creationInfo);
169
- this._editor.viewModel.sketchViewModel.on("create", (evt) => {
170
- if (evt.state === "complete") {
171
- this.drawComplete.emit();
172
- }
174
+ var _a;
175
+ if ((_a = this._editor.viewModel.featureTemplatesViewModel.items) === null || _a === void 0 ? void 0 : _a.length) {
176
+ const items = this._editor.viewModel.featureTemplatesViewModel.items[0].get("items");
177
+ //once the feature template is selected handle the event for formSubmit and sketch complete
178
+ //also, hide the headers and footer in the editor as we will be showing our own submit and cancel button
179
+ this._editor.viewModel.featureTemplatesViewModel.on('select', () => {
180
+ setTimeout(() => {
181
+ //on form submit
182
+ this._editor.viewModel.featureFormViewModel.on('submit', this.submitted.bind(this));
183
+ //on sketch complete emit the event
184
+ this._editor.viewModel.sketchViewModel.on("create", (evt) => {
185
+ if (evt.state === "complete") {
186
+ this.drawComplete.emit();
187
+ }
188
+ });
189
+ this.hideEditorsElements();
190
+ }, 700);
191
+ this.hideEditorsElements();
173
192
  });
193
+ //if only one feature template then directly start geometry creation for that
194
+ //else allow feature template selection to user
195
+ if (items.length === 1) {
196
+ this._editor.viewModel.featureTemplatesViewModel.select(items[0]);
197
+ }
198
+ //hides the header and footer elements in editor widget
199
+ this.hideEditorsElements();
174
200
  }
175
201
  }
202
+ /**
203
+ * Hides the elements of editor widget
204
+ * @protected
205
+ */
206
+ hideEditorsElements() {
207
+ if (!this.customizeSubmit) {
208
+ return;
209
+ }
210
+ setTimeout(() => {
211
+ var _a;
212
+ //hides the header and footer on the featureForm
213
+ (_a = this.el.querySelector('.esri-editor').querySelectorAll('calcite-flow-item')) === null || _a === void 0 ? void 0 : _a.forEach((flowItem) => {
214
+ var _a, _b, _c, _d, _e;
215
+ const article = (_c = (_b = (_a = flowItem.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('calcite-panel')) === null || _b === void 0 ? void 0 : _b.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('article');
216
+ //hide the header
217
+ (_d = article === null || article === void 0 ? void 0 : article.querySelector('header')) === null || _d === void 0 ? void 0 : _d.setAttribute('style', 'display: none');
218
+ //hide the footer
219
+ (_e = article === null || article === void 0 ? void 0 : article.querySelector('footer')) === null || _e === void 0 ? void 0 : _e.setAttribute('style', 'display: none');
220
+ });
221
+ }, 700);
222
+ }
176
223
  /**
177
224
  * On creation of feature emit the event that the feature is created
178
225
  * @param evt feature submit event
@@ -180,9 +227,12 @@ export class CreateFeature {
180
227
  */
181
228
  async submitted(evt) {
182
229
  var _a;
230
+ //return if any attribute is invalid , focus will be shifted to the invalid attribute in feature form
183
231
  if (evt.invalid.length) {
184
232
  return;
185
233
  }
234
+ //Submit only when valid attributes
235
+ //emit success or fail based on the result
186
236
  if (evt.valid.length) {
187
237
  try {
188
238
  await this._editor.activeWorkflow.commit();
@@ -251,6 +301,24 @@ export class CreateFeature {
251
301
  },
252
302
  "attribute": "selected-layer-id",
253
303
  "reflect": false
304
+ },
305
+ "customizeSubmit": {
306
+ "type": "boolean",
307
+ "mutable": false,
308
+ "complexType": {
309
+ "original": "boolean",
310
+ "resolved": "boolean",
311
+ "references": {}
312
+ },
313
+ "required": false,
314
+ "optional": true,
315
+ "docs": {
316
+ "tags": [],
317
+ "text": "boolean: Set this to true when have a custom submit button in the app.\r\nThis will hide the header and footer elements of the editor and user needs to execute the submit method manually."
318
+ },
319
+ "attribute": "customize-submit",
320
+ "reflect": false,
321
+ "defaultValue": "false"
254
322
  }
255
323
  };
256
324
  }
@@ -305,6 +373,21 @@ export class CreateFeature {
305
373
  "resolved": "void",
306
374
  "references": {}
307
375
  }
376
+ }, {
377
+ "method": "editingAttachment",
378
+ "name": "editingAttachment",
379
+ "bubbles": true,
380
+ "cancelable": true,
381
+ "composed": true,
382
+ "docs": {
383
+ "tags": [],
384
+ "text": "Emitted on demand when editing attachments"
385
+ },
386
+ "complexType": {
387
+ "original": "boolean",
388
+ "resolved": "boolean",
389
+ "references": {}
390
+ }
308
391
  }];
309
392
  }
310
393
  static get methods() {
@@ -88,6 +88,7 @@ export class CrowdsourceReporter {
88
88
  * @returns Promise when complete
89
89
  */
90
90
  async componentWillLoad() {
91
+ this._urlParamsLoaded = false;
91
92
  await this._initModules();
92
93
  await this._getTranslations();
93
94
  }
@@ -124,6 +125,11 @@ export class CrowdsourceReporter {
124
125
  setSelectedLayer(layerId, layerName) {
125
126
  this._selectedLayerId = layerId;
126
127
  this._selectedLayerName = layerName;
128
+ //show only current layer on map and hide other valid editable layers
129
+ //if layerId is empty then show all the layers on map
130
+ this._validLayers.forEach(layer => {
131
+ layer.set('visible', !layerId || (layer.id === layerId));
132
+ });
127
133
  }
128
134
  /**
129
135
  * Get the reporter app functionality
@@ -180,15 +186,22 @@ export class CrowdsourceReporter {
180
186
  * @protected
181
187
  */
182
188
  getFeatureCreateFlowItem() {
183
- return (h("calcite-flow-item", { collapsed: this.isMobile && this._sidePanelCollapsed, heading: this._selectedLayerName, onCalciteFlowItemBack: this.backFromCreateFeaturePanel.bind(this) }, this.isMobile && this.getActionToExpandCollapsePanel(), this._showSubmitCancelButton && h("div", { class: "width-full", slot: "footer" }, h("calcite-button", { appearance: "secondary", class: "footer-top-button footer-button", onClick: this.onSubmitButtonClick.bind(this), width: "full" }, this._translations.submit), h("calcite-button", { appearance: "outline", class: "footer-button", onClick: this.backFromCreateFeaturePanel.bind(this), width: "full" }, this._translations.cancel)), h("calcite-panel", { "full-height": true, "full-width": true }, h("calcite-notice", { class: "notice-msg", icon: "lightbulb", kind: "success", open: true }, h("div", { slot: "message" }, this._translations.featureEditFormInfoMsg)), h("create-feature", { mapView: this.mapView, onDrawComplete: this.showSubmitCancelButton.bind(this), onFail: this.createFeatureFailed.bind(this), onSuccess: this.navigateHomePage.bind(this), ref: el => this._createFeature = el, selectedLayerId: this._selectedLayerId }))));
189
+ return (h("calcite-flow-item", { collapsed: this.isMobile && this._sidePanelCollapsed, heading: this._selectedLayerName, onCalciteFlowItemBack: this.backFromCreateFeaturePanel.bind(this) }, this.isMobile && this.getActionToExpandCollapsePanel(), this._showSubmitCancelButton && h("div", { class: "width-full", slot: "footer" }, h("calcite-button", { appearance: "secondary", class: "footer-top-button footer-button", onClick: this.onSubmitButtonClick.bind(this), width: "full" }, this._translations.submit), h("calcite-button", { appearance: "outline", class: "footer-button", onClick: this.backFromCreateFeaturePanel.bind(this), width: "full" }, this._translations.cancel)), h("calcite-panel", { "full-height": true, "full-width": true }, h("calcite-notice", { class: "notice-msg", icon: "lightbulb", kind: "success", open: true }, h("div", { slot: "message" }, this._translations.featureEditFormInfoMsg)), h("create-feature", { customizeSubmit: true, mapView: this.mapView, onDrawComplete: this.onDrawComplete.bind(this), onEditingAttachment: this.showSubmitCancelButton.bind(this), onFail: this.createFeatureFailed.bind(this), onSuccess: this.onReportSubmitted.bind(this), ref: el => this._createFeature = el, selectedLayerId: this._selectedLayerId }))));
184
190
  }
185
191
  /**
186
192
  * When drawing of incident location completed on map show the submit and cancel button
187
193
  * @protected
188
194
  */
189
- showSubmitCancelButton() {
195
+ onDrawComplete() {
190
196
  this._showSubmitCancelButton = true;
191
197
  }
198
+ /**
199
+ * When Add attachment panel is enabled hide the submit and cancel button
200
+ * @protected
201
+ */
202
+ showSubmitCancelButton(evt) {
203
+ this._showSubmitCancelButton = !evt.detail;
204
+ }
192
205
  /**
193
206
  * On back from create feature, call submit editor to destroy the Editor widget instance
194
207
  * @protected
@@ -221,11 +234,22 @@ export class CrowdsourceReporter {
221
234
  * On submit report navigate to the layer list home page and refresh the layer list
222
235
  * @protected
223
236
  */
224
- navigateHomePage() {
237
+ onReportSubmitted() {
225
238
  this._reportSubmitted = true;
239
+ this.navigateToHomePage();
240
+ }
241
+ /**
242
+ * Navigates to layer-list
243
+ * @protected
244
+ */
245
+ navigateToHomePage() {
246
+ if (this._createFeature) {
247
+ this._createFeature.close();
248
+ }
226
249
  if (this._layerList) {
227
250
  this._layerList.refresh();
228
251
  }
252
+ this.setSelectedFeatures([]);
229
253
  this._flowItems = ["layer-list"];
230
254
  }
231
255
  /**
@@ -271,7 +295,10 @@ export class CrowdsourceReporter {
271
295
  //update the has valid layer state
272
296
  this._hasValidLayers = layersListed.length > 0;
273
297
  //navigate to the feature details if URL params found
274
- await this.loadFeatureFromURLParams();
298
+ if (!this._urlParamsLoaded) {
299
+ this._urlParamsLoaded = true;
300
+ await this.loadFeatureFromURLParams();
301
+ }
275
302
  }
276
303
  /**On click of layer list item show feature list
277
304
  * @param evt Event which has details of selected layerId and layerName
@@ -288,10 +315,10 @@ export class CrowdsourceReporter {
288
315
  backFromSelectedPanel() {
289
316
  const updatedFlowItems = [...this._flowItems];
290
317
  updatedFlowItems.pop();
291
- //clear the selected layer and feature when back to layer list
318
+ //Back to layer list, and return as the flowItems will be reset in navigateToHomePage
292
319
  if (updatedFlowItems.length === 1) {
293
- this.setSelectedLayer('', '');
294
- this.setSelectedFeatures([]);
320
+ this.navigateToHomePage();
321
+ return;
295
322
  }
296
323
  this._flowItems = [...updatedFlowItems];
297
324
  }
@@ -440,7 +467,6 @@ export class CrowdsourceReporter {
440
467
  }
441
468
  /**
442
469
  * Updates the share url for current selected feature
443
- * @returns
444
470
  * @protected
445
471
  */
446
472
  _updateShareURL() {
@@ -469,6 +495,7 @@ export class CrowdsourceReporter {
469
495
  }
470
496
  /**
471
497
  * Navigates to selected features detail based on the URL params
498
+ * @protected
472
499
  */
473
500
  async loadFeatureFromURLParams() {
474
501
  if (this.layerId && this.objectId) {
@@ -0,0 +1,90 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8" />
4
+ <meta
5
+ name="viewport"
6
+ content="initial-scale=1,maximum-scale=1,user-scalable=no"
7
+ />
8
+ <title>Create Feature</title>
9
+ <!--
10
+ | Copyright 2022 Esri
11
+ |
12
+ | Licensed under the Apache License, Version 2.0 (the "License");
13
+ | you may not use this file except in compliance with the License.
14
+ | You may obtain a copy of the License at
15
+ |
16
+ | http://www.apache.org/licenses/LICENSE-2.0
17
+ |
18
+ | Unless required by applicable law or agreed to in writing, software
19
+ | distributed under the License is distributed on an "AS IS" BASIS,
20
+ | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ | See the License for the specific language governing permissions and
22
+ | limitations under the License.
23
+ -->
24
+ <style>
25
+ #viewDiv {
26
+ padding: 0;
27
+ margin: 0;
28
+ height: 100%;
29
+ width: calc(100% - 402px);
30
+ }
31
+
32
+ .over-map {
33
+ position: absolute;
34
+ top: 0;
35
+ right: 0px;
36
+ background-color: var(--calcite-color-foreground-1);
37
+ }
38
+
39
+ .column {
40
+ width: 400px;
41
+ height: 100%;
42
+ border: 1px solid var(--calcite-color-border-2);
43
+ }
44
+
45
+ .column[dir="rtl"] {
46
+ float: left;
47
+ }
48
+
49
+ .full-height {
50
+ height: 100%;
51
+ }
52
+ </style>
53
+
54
+ <link rel="stylesheet" href="https://jsdev.arcgis.com/4.29/esri/themes/light/main.css" />
55
+ <link rel="stylesheet" href="https://webapps-cdn.esri.com/CDN/fonts/v1.4.1/fonts.css" />
56
+ <link rel="stylesheet" href="../solutions-components.css" type="text/css">
57
+ <link rel="stylesheet" type="text/css" href="https://js.arcgis.com/calcite-components/2.0.0/calcite.css" />
58
+
59
+ <script src="https://jsdev.arcgis.com/4.29/"></script>
60
+ <script type="module" src="../solutions-components.esm.js"></script>
61
+
62
+ <script>
63
+ require(["esri/WebMap", "esri/views/MapView"], (
64
+ WebMap,
65
+ MapView
66
+ ) => {
67
+ var webMap = new WebMap({
68
+ portalItem: {
69
+ id: "024e8a5e73a34c5aade9632d651c5750"
70
+ }
71
+ });
72
+ var view = new MapView({
73
+ container: "viewDiv",
74
+ map: webMap
75
+ });
76
+
77
+ view.when(function () {
78
+ const demo = document.getElementById("demo");
79
+ demo.mapView = view;
80
+ demo.selectedLayerId = 'SE_attch_7327';
81
+ })
82
+ });
83
+ </script>
84
+ </head>
85
+
86
+ <body>
87
+ <div id="viewDiv"></div>
88
+ <create-feature id="demo" class="over-map column full-height"></create-feature>
89
+ </body>
90
+ </html>