@esri/solutions-components 0.7.31 → 0.7.32

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>